美文网首页python3学习
Python3(12) Python 常用内建模块

Python3(12) Python 常用内建模块

作者: 猿来如痴 | 来源:发表于2018-02-07 19:56 被阅读123次

    本系列主要学习Python的基本使用和语法知识,后续可能会围绕着AI学习展开。
    Python3 (1) Python语言的简介
    Python3 (2) Python语法基础
    Python3 (3) Python函数
    Python3 (4) Python高级特性
    Python3 (5) Python 函数式编程
    Python3 (6) Python 模块
    Python3 (7) Python 面向对象编程
    Python3 (8) Python 面向对象高级编程
    Python3 (9) Python 错误、调试和测试
    Python3 (10) Python IO编程
    Python3 (11) Python 进程和线程
    Python3 (12) Python 常用内建模块
    Python 中有一些内置的函数无需额外安装和配置,即可直接使用,这就是python的内置模块。这篇比较简单,主要去熟悉一下有哪些常见的内置模块,具体开发的时候心中有数。

    datetime

    datetime与时间有关的,是处理日期和时间的标准库。

    获取当前日期和时间

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    from datetime import datetime
    
    # 获取当前datetime:
    now = datetime.now()
    print('now =', now)
    print('type(now) =', type(now))
    

    输出结果:

    now = 2018-02-02 10:47:52.740622
    type(now) = <class 'datetime.datetime'>
    

    获取指定日期和时间

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    from datetime import datetime
    
    # 设置时间
    set_time = datetime(2018,8,18,20,18)
    print(set_time)
    

    输出结果:

    2018-08-18 20:18:00
    

    datetime转换为timestamp

    时间戳这个概念,应该对应上计算机的时间规则:1970年1月1日 00:00:00 UTC+00:00时区的时刻称为epoch time,记为0,当前时间就是相对于epoch time的秒数,称为timestamp:

    # 标准时间
    timestamp = 0 = 1970-1-1 00:00:00 UTC+0:00
    # 相当于 北京时间 (东八区)
    timestamp = 0 = 1970-1-1 08:00:00 UTC+8:00
    
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    from datetime import datetime
    
    # 设置时间
    set_time = datetime(2018,8,18,20,18)
    print(set_time)
    # 获取时间戳
    s = set_time.timestamp()
    print(s)
    # 当前时间
    current_time = datetime.now()
    print(current_time)
    # 获取当前时间戳
    cs = current_time.timestamp()
    print(cs)
    

    输出结果:

    2018-08-18 20:18:00
    1534594680.0
    2018-02-02 13:49:35.115818
    1517550575.115818
    

    Python的timestamp是一个浮点数。如果有小数位,小数位表示毫秒数,java 中的timestamp使用整数表示毫秒数,我们只需要把timestamp除以1000就得到Python的浮点表示方法。

    timestamp转换为datetime

    由时间戳转化成时间的格式 :datetime.fromtimestamp

    # 获取设置的时间 (本地时间)
    set_t = datetime.fromtimestamp(1534594680.0)
    print(set_t)
    # 获取当前时间  (本地时间)
    cur_t = datetime.fromtimestamp(1517550847.361579)
    print(cur_t)
    # 获取 标准时间:
    print(datetime.utcfromtimestamp(1534594680.0),'\n',datetime.utcfromtimestamp(1517550847.361579))
    

    输出结果:

    2018-08-18 20:18:00
    2018-02-02 13:54:07.361579
    2018-08-18 12:18:00 
     2018-02-02 05:54:07.361579
    

    通过fromtimestamp转化的时间,是针对当前操作系统的时区。utcfromtimestamp转化的是标准时间。

    str转换为datetime

    str转换为datetime:datetime.strptime()

    cday = datetime.strptime('2018-8-18 08:18:18', '%Y-%m-%d %H:%M:%S')
    print(cday)
    print(type(cday))
    

    输出结果:

    2018-08-18 08:18:18
    <class 'datetime.datetime'>
    

    转换后的datetime是没有时区信息的,并且参数%Y-%m-%d %H:%M:%S是时间的格式,Python中内置了好多格式,具体参考官方文档

    datetime加减运算

    在Python 中,对时间的加减运算可以直接通过+-运算,但是不能直接加数字,应该转换成timedelta对象。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    from datetime import datetime, timedelta
    
    # 当前时间
    current_time = datetime.now()
    print(current_time)
    
    # 加减运算
    add_time = current_time + timedelta(days=2, hours=12)
    print(add_time)
    

    输出结果:

    2018-02-02 16:54:00.977390
    2018-02-05 04:54:00.977390
    

    本地时间转换为UTC时间

    本地时间是指系统设定时区的时间,如北京时间是UTC+8:00时区的时间,而UTC时间指UTC+0:00时区的时间。datetime类型有一个时区属性tzinfo,但是默认为None,可以强行给datetime设置一个时区,如果没有设置是不知道是哪个时区的。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    from datetime import datetime, timedelta, timezone
    
    # 创建时区UTC+8:00
    tz_utc_8 = timezone(timedelta(hours=8))
    print(tz_utc_8)
    now = datetime.now()
    print(now)
    
    # 强制设置为UTC+8:00
    dt = now.replace(tzinfo=tz_utc_8)
    print(dt)
    

    输出结果:

    UTC+08:00
    2018-02-02 17:06:30.659342
    2018-02-02 17:06:30.659342+08:00
    

    时区转换

    Python中可以直接获取到当前时间的标准时间(UTC),然后可以通过astimezone切换成其他时区的时间。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    from datetime import datetime, timedelta, timezone
    
    # 拿到UTC时间,并强制设置时区为UTC+0:00:
    utc_dt = datetime.utcnow().replace(tzinfo=timezone.utc)
    print(utc_dt)
    
    # 切换成北京时间
    bj_dt = utc_dt.astimezone(timezone(timedelta(hours=8)))
    print(bj_dt)
    # 切换成东京时间
    tokyo_dt = utc_dt.astimezone(timezone(timedelta(hours=9)))
    print(tokyo_dt)
    
    # 北京时间切换东京时间
    tokyo_dt2 = bj_dt.astimezone(timezone(timedelta(hours=9)))
    print(tokyo_dt2)
    

    输出结果:

    2018-02-02 09:22:26.540967+00:00
    2018-02-02 17:22:26.540967+08:00
    2018-02-02 18:22:26.540967+09:00
    2018-02-02 18:22:26.540967+09:00
    

    时区转换的关键在于,拿到一个datetime时,要获知其正确的时区,然后强制设置时区,作为基准时间。如果不知道当前时区,就去找 UTC 标准时间,并强制设置时区为00:00,带时区的datetime通过astimezone()方法,可以转换到任意时区,不是必须从UTC+0:00时区转换到其他时区,任何带时区的datetime都可以正确转换。

    最后我们要存储时间,一般用datetime转成timestamp进行存储。这里时间的扩展类就写完了,我们可以根据具体的情况来进行选择使用。

    collections

    collections是Python内建的一个集合模块,提供了许多有用的集合类。

    namedtuple

    namedtuple是一个函数,返回一个自定义的tuple对象,并且规定了tuple元素的个数,并可以用属性而不是索引来引用tuple的某个元素。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    from collections import namedtuple
    
    # 创建一个Point 表示坐标
    Point = namedtuple('Point', ['x', 'y'])
    p = Point(2,3)
    print(p.x)
    print(p.y)
    print(isinstance(p,Point))
    print(isinstance(p,tuple))
    
    # 创建坐标和半径表示一个圆
    Circle = namedtuple('Circle',['x','y','r'])
    c = Circle(2,3,2)
    print(c.x,c.y,c.r)
    

    输出结果:

    2
    3
    True
    True
    2 3 2
    

    namedtuple()根据具体的应用场景,通过事物的属性定义不同的tuple对象。使表达的的事物更加的形象具体。

    deque

    首先双端队列的出现是解决list单向列表结构,对插入、删除操作效率低的问题。双端队列:采用双向列表结构,更高效实现插入和删除操作。适合用于队列和栈。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    from collections import deque
    
    q = deque(['a', 'b', 'c'])
    q.append('x')
    q.appendleft('y')
    print(q)
    

    输出结果:

    deque(['y', 'a', 'b', 'c', 'x'])
    

    deque除了实现listappend()pop()外,还支持appendleft()popleft(),这样就可以非常高效地往头部添加或删除元素。

    defaultdict

    使用dict时,如果引用的Key不存在,就会抛出KeyError。如果希望key不存在时,返回一个默认值,defaultdict应用而生。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    from collections import defaultdict
    
    dd = defaultdict(lambda: 'N/A')
    dd['key1'] = 'abc'
    print(dd['key1'])
    print(dd['key2'])
    

    输出结果:

    abc
    N/A
    

    默认值是调用函数返回的,而函数在创建defaultdict对象时传入。除了在Key不存在时返回默认值,defaultdict的其他行为跟dict是完全一样的。

    OrderedDict

    使用dict时,Key是无序的。在对dict做迭代时,我们无法确定Key的顺序如果要保持Key的顺序,可以用OrderedDict:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    from collections import OrderedDict
    
    d = dict([('a', 1), ('b', 2), ('c', 3)])
    # dict的Key是无序的
    print(d)
    od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
    # OrderedDict的Key是有序的
    print(od)
    

    输出结果:

    {'a': 1, 'b': 2, 'c': 3}
    OrderedDict([('a', 1), ('b', 2), ('c', 3)])
    

    使用OrderedDict的Key会按照插入的顺序排列,不是Key本身排序。 我们可以通过OrderedDict实现一个FIFO(先进先出)的dict。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    from collections import OrderedDict
    
    class LastUpdatedOrderedDict(OrderedDict):
    
        def __init__(self, capacity):
            super(LastUpdatedOrderedDict, self).__init__()
            self._capacity = capacity
    
        def __setitem__(self, key, value):
            containsKey = 1 if key in self else 0
            if len(self) - containsKey >= self._capacity:
                last = self.popitem(last=False)
                print('remove:', last)
            if containsKey:
                del self[key]
                print('set:', (key, value))
            else:
                print('add:', (key, value))
            OrderedDict.__setitem__(self, key, value)
    
    d = LastUpdatedOrderedDict(3)
    d['a'] = 1
    d['b'] = 2
    d['c'] = 3
    d['d'] = 4
    print(d)
    

    输出结果:

    add: ('a', 1)
    add: ('b', 2)
    add: ('c', 3)
    remove: ('a', 1)
    add: ('d', 4)
    LastUpdatedOrderedDict([('b', 2), ('c', 3), ('d', 4)])
    

    从输出的数据我们可以清楚的看到数据的插入删除顺序,实现了先进先出的FIFO 队列。

    Counter

    Counter作为一个简单的计数器,例如:我们来统计字符出现的次数:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    from typing import Counter
    
    c = Counter()
    
    for ch in 'programming':
        c[ch] = c[ch]+1
    
    print(c)
    

    输出结果:

    Counter({'r': 2, 'g': 2, 'm': 2, 'p': 1, 'o': 1, 'a': 1, 'i': 1, 'n': 1})
    

    从输出的结果我们可以看出,Counter 其实是一个dict 子类,通过循环来找到字符的出现次数。

    这里集合类的扩展类我们说完了。可以根据实际情况来进行使用。

    base64

    在开发中我们经常会通过将图片转成Base64作为字符串上传等,在Python中也存在base64。今天我们系统的来学习一下base64的组成。

    • Base64是一种用64个字符来表示任意二进制数据的方法。
    ['A', 'B', 'C', ... 'a', 'b', 'c', ... '0', '1', ... '+', '/']
    
    • Base64对二进制数据进行处理,每3个字节一组,一共是3x8=24bit,划为4组,每组正好6个bit,这样我们得到4个数字作为索引,然后查表,获得相应的4个字符,就是编码后的字符串。Base64编码会把3字节的二进制数据编码为4字节的文本数据,长度增加33%,如果要编码的二进制数据不是3的倍数,Base64用\x00字节在末尾补足后,再在编码的末尾加上1个或2个=号,表示补了多少字节,解码的时候,会自动去掉。
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    import base64
    
    s = base64.b64encode('在Python中使用BASE 64编码'.encode('utf-8'))
    print(s)
    d = base64.b64decode(s).decode('utf-8')
    print(d)
    
    s = base64.urlsafe_b64encode('在Python中使用BASE 64编码'.encode('utf-8'))
    print(s)
    d = base64.urlsafe_b64decode(s).decode('utf-8')
    print(d)
    

    输出结果:

    b'5ZyoUHl0aG9u5Lit5L2/55SoQkFTRSA2NOe8lueggQ=='
    在Python中使用BASE 64编码
    b'5ZyoUHl0aG9u5Lit5L2_55SoQkFTRSA2NOe8lueggQ=='
    在Python中使用BASE 64编码
    

    Python 中提供了一种"url safe"base64编码,其实就是把字符+/分别变成-_使其可以在url中安全拼接。

    • Base64是一种任意二进制到文本字符串的编码方法,常用于在URLCookie、网页中传输少量二进制数据。
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    import base64
    
    def safe_base64_decode(s):
        s1 = s + b'='*( - len(s) % 4)
        print(s1)
        return base64.b64decode(s1)
    
    s = base64.b64encode('在Python中使用BASE 64编码'.encode('utf-8'))
    print(s)
    d1= base64.b64decode(s).decode('utf-8')
    d2 = safe_base64_decode(s).decode('utf-8')
    d3 = safe_base64_decode(b'5ZyoUHl0aG9u5Lit5L2/55SoQkFTRSA2NOe8lueggQ').decode('utf-8')
    print(d1)
    print(d2)
    print(d3)
    

    输出结果:

    b'5ZyoUHl0aG9u5Lit5L2/55SoQkFTRSA2NOe8lueggQ=='
    b'5ZyoUHl0aG9u5Lit5L2/55SoQkFTRSA2NOe8lueggQ=='
    b'5ZyoUHl0aG9u5Lit5L2/55SoQkFTRSA2NOe8lueggQ=='
    在Python中使用BASE 64编码
    在Python中使用BASE 64编码
    在Python中使用BASE 64编码
    

    因为=用在URLCookie里面会造成歧义,所以,很多Base64编码后会把=去掉,因为Base64是把3个字节变为4个字节,所以,Base64编码的长度永远是4的倍数,因此,需要加上 =Base64字符串的长度变为4的倍数,我们可以通过上面的方法解析base64

    struct

    在Python提供了一个struct模块来解决bytes和其他二进制数据类型的转换。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import struct
    # 把任意数据类型变成bytes
    # >表示字节顺序是big-endian,也就是网络序,I表示4字节无符号整数。
    p1 = struct.pack('>I', 10240099)
    print(p1)
    # 把bytes变成相应的数据类型
    p2 = struct.unpack('>I', b'\x00\x9c@c')
    print(p2)
    # 根据>IH的说明,后面的bytes依次变为I:4字节无符号整数和H:2字节无符号整数。
    p3 = struct.unpack('>IH', b'\xf0\xf0\xf0\xf0\x80\x80')
    print(p3)
    

    输出结果:

    b'\x00\x9c@c'
    (10240099,)
    (4042322160, 32896)
    

    struct模块定义的数据类型可以参考Python官方文档:https://docs.python.org/3/library/struct.html#format-characters
    我们通过一个demo 来练习struct的使用:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import struct
    
    
    def bmpinfo(str):
        f = open(str, 'rb')
        s = f.read(30)
        h = struct.unpack('<ccIIIIIIHH', s)
        if h[0] == b'B' and (h[1] == b'M' or h[1] == b'A'):
            print(True)
            print('位图大小:', h[6], '*', h[7], '颜色数:', h[9])
        else:
            print(False)
    
    if __name__ == "__main__":
        bmpinfo('F:\python\HelloWord\BMP.bmp')
    

    输出结果:

    True
    位图大小: 1152 * 648 颜色数: 8
    

    以上验证了一个文件是否是位图文件,如果是,打印出图片大小和颜色数。

    hashlib

    Pythonhashlib提供了常见的摘要算法,又称哈希算法、散列算法,它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示),常见的有MD5SHA1等。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import hashlib
    
    md5 = hashlib.md5()
    md5.update('how to use md5 in python hashlib?'.encode('utf-8'))
    print(md5.hexdigest())
    
    md51 = hashlib.md5()
    md51.update('how to use md5 in python hashlib'.encode('utf-8'))
    print(md51.hexdigest())
    

    输出结果:

    d26a53750bc40b38b65a520292f69306
    846014c3556d79e878be15fde5426e8a
    

    我们通过 MD5生成的128 bit字节,通常用一个32位的16进制字符串表示,两个我们只是去掉一个符号,就生成差别非常大的两个不同字符串,所以我们经常通过MD5 加密,为了确保密码的更加安全也可以在加密前加上盐,使一些常见的密码更加的安全。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import hashlib
    
    sha1 = hashlib.sha1()
    sha1.update('how to use sha1 in '.encode('utf-8'))
    sha1.update('python hashlib?'.encode('utf-8'))
    print(sha1.hexdigest())
    

    输出结果:

    2c76b57293ce30acef38d98f6046927161b46a44
    

    这是SHA1算法,生成的是一个160 bit字节,通常用一个40位的16进制字符串表示,比SHA1更安全的算法是SHA256和SHA512,不过越安全的算法不仅越慢,而且摘要长度更长。

    hmac

    Hmac算法:Keyed-Hashing for Message Authentication通过一个标准算法,在计算哈希的过程中,把key混入计算过程中,适用于所有的哈希算法,如 MD5、SHA-1等。使用如下:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import hmac
    
    message = b'Hello, world!'
    key = b'secret'
    h = hmac.new(key, message, digestmod='MD5')
    print(h.hexdigest())
    

    输出结果:

    fa4ee7d173f2d97ee79022d1a7355bcf
    

    其实hmac 算法就是将key混入MD5算法中,得出一个有口令的值,达到防止黑客破解的作用。

    itertools

    Python的内建模块itertools提供了非常有用的用于操作迭代对象的函数。

    “无限”迭代器

    itertools中提供了几个无限迭代器,如: count()cycle()repeat()

    • count 创建一个无限迭代器,传入的是初始值。
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import itertools
    # 打印出自然数序列,无限打印下去
    natuals = itertools.count(1)
    for n in natuals:
        print(n)
    

    输出结果:

    1
    2
    3
    4
    5
    ...
    
    • cycle() 创建一个无限迭代器,传入的是一个序列,会无限的循环序列
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import itertools
    # 打印出自然数序列,无限打印下去
    cs = itertools.cycle('ABCDE')  # 注意字符串也是序列的一种
    for n in cs :
        print(n)
    

    输出结果:

    A
    B
    C
    D
    E
    A
    B
    ...
    
    • cycle() 创建一个无限迭代器,传入的是一个元素,会无限的循环元素,第二个参数也可以传入循环的次数
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import itertools
    # 打印出自然数序列,无限打印下去
    ns = itertools.repeat('A',5)
    for n in ns :
        print(n)
    

    输出结果:

    A
    A
    A
    A
    A
    

    chain()

    chain()可以把一组迭代对象串联起来,形成一个更大的迭代器:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import itertools
    
    for c in itertools.chain('ABC', 'XYZ'):
        print(c)
    

    输出结果:

    A
    B
    C
    X
    Y
    Z
    

    groupby()

    groupby()把迭代器中相邻的重复元素挑出来放在一起:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import itertools
    
    for key, group in itertools.groupby('AAABBBCCAAA'):
        print(key, list(group))
    

    输出结果:

    A ['A', 'A', 'A']
    B ['B', 'B', 'B']
    C ['C', 'C']
    A ['A', 'A', 'A']
    

    Python中的itertools模块还有许多有用的函数,通过这些函数创建出来的是Iterator,而不是list

    contextlib

    Python中并不是只有open()函数返回的fp对象才能使用with语句。实际上,任何对象,只要正确实现了上下文管理,就可以用于with语句。contextlib模块提供了@contextmanager@closing来快捷的实现上下文管理。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    from contextlib import contextmanager
    
    @contextmanager
    def tag(name):
        print("<%s>" % name)
        yield
        print("</%s>" % name)
    
    with tag("h1"):
        print("hello")
        print("world")
    

    输出结果:

    <h1>
    hello
    world
    </h1>
    

    @contextmanager让我们通过编写generator来简化上下文管理。也可以使用@closing,他其实是经过@contextmanager装饰的generator

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    from contextlib import closing
    from urllib.request import urlopen
    
    with closing(urlopen('https://www.baidu.com')) as page:
        for line in page:
            print(line)
    

    输出结果:

    b'<html>\r\n'
    b'<head>\r\n'
    b'\t<script>\r\n'
    b'\t\tlocation.replace(location.href.replace("https://","http://"));\r\n'
    b'\t</script>\r\n'
    b'</head>\r\n'
    b'<body>\r\n'
    b'\t<noscript><meta http-equiv="refresh" content="0;url=http://www.baidu.com/"></noscript>\r\n'
    b'</body>\r\n'
    b'</html>'
    

    通过@closing快速的解析到每一行代码。

    contextlib模块还提供了很多好用的装饰器。

    urllib

    urllib提供了一系列用于操作URL的功能。

    Get

    urllib的request模块可以非常方便地抓取URL内容,也就是发送一个GET请求到指定的页面,然后返回HTTP的响应:使得网络抓取在python中非常的简单,下面我们抓取一个网址:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    from urllib import request
    
    with request.urlopen('https://api.douban.com/v2/book/2129650') as f:
        data = f.read()
        print('Status:', f.status, f.reason)
        for k, v in f.getheaders():
            print('%s: %s' % (k, v))
        print('Data:', data.decode('utf-8'))
    

    输出结果:

    Status: 200 OK
    Date: Wed, 07 Feb 2018 11:28:06 GMT
    Content-Type: application/json; charset=utf-8
    Content-Length: 2058
    Connection: close
    Vary: Accept-Encoding
    X-Ratelimit-Remaining2: 99
    X-Ratelimit-Limit2: 100
    Expires: Sun, 1 Jan 2006 01:00:00 GMT
    Pragma: no-cache
    Cache-Control: must-revalidate, no-cache, private
    Set-Cookie: bid=vWIbncKafc0; Expires=Thu, 07-Feb-19 11:28:06 GMT; Domain=.douban.com; Path=/
    X-DOUBAN-NEWBID: vWIbncKafc0
    X-DAE-Node: dis8
    X-DAE-App: book
    Server: dae
    Data: {"rating":{"max":10,"numRaters":16,"average":"7.4","min":0},"subtitle":"","author":["廖雪峰"],"pubdate":"2007","tags":[{"count":21,"name":"spring","title":"spring"},{"count":13,"name":"Java","title":"Java"},{"count":6,"name":"javaee","title":"javaee"},{"count":5,"name":"j2ee","title":"j2ee"},{"count":4,"name":"计算机","title":"计算机"},{"count":4,"name":"编程","title":"编程"},{"count":3,"name":"藏书","title":"藏书"},{"count":3,"name":"POJO","title":"POJO"}],"origin_title":"","image":"https://img3.doubanio.com\/mpic\/s2552283.jpg","binding":"平装","translator":[],"catalog":"","pages":"509","images":{"small":"https://img3.doubanio.com\/spic\/s2552283.jpg","large":"https://img3.doubanio.com\/lpic\/s2552283.jpg","medium":"https://img3.doubanio.com\/mpic\/s2552283.jpg"},"alt":"https:\/\/book.douban.com\/subject\/2129650\/","id":"2129650","publisher":"电子工业出版社","isbn10":"7121042622","isbn13":"9787121042621","title":"Spring 2.0核心技术与最佳实践","url":"https:\/\/api.douban.com\/v2\/book\/2129650","alt_title":"","author_intro":"","summary":"本书注重实践而又深入理论,由浅入深且详细介绍了Spring 2.0框架的几乎全部的内容,并重点突出2.0版本的新特性。本书将为读者展示如何应用Spring 2.0框架创建灵活高效的JavaEE应用,并提供了一个真正可直接部署的完整的Web应用程序——Live在线书店(http:\/\/www.livebookstore.net)。\n在介绍Spring框架的同时,本书还介绍了与Spring相关的大量第三方框架,涉及领域全面,实用性强。本书另一大特色是实用性强,易于上手,以实际项目为出发点,介绍项目开发中应遵循的最佳开发模式。\n本书还介绍了大量实践性极强的例子,并给出了完整的配置步骤,几乎覆盖了Spring 2.0版本的新特性。\n本书适合有一定Java基础的读者,对JavaEE开发人员特别有帮助。本书既可以作为Spring 2.0的学习指南,也可以作为实际项目开发的参考手册。","price":"59.8"}
    

    可以看到HTTP响应的头和JSON数据。如果我们要模拟浏览器来访问,一般会设计到Request,并在其上添加HTTP头部信息,如下:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    from urllib import request
    
    req = request.Request('http://www.douban.com/')
    req.add_header('User-Agent', 'Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25')
    with request.urlopen(req) as f:
        print('Status:', f.status, f.reason)
        for k, v in f.getheaders():
            print('%s: %s' % (k, v))
        print('Data:', f.read().decode('utf-8'))
    

    输出结果:

    ...
    <meta charset="UTF-8">
            <title>豆瓣(手机版)</title>
            <meta name="viewport" content="width=device-width, height=device-height, user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0">
            <meta name="format-detection" content="telephone=no">
            <link rel="canonical" href="
    ...
    

    从截取的部分数据来看,我们模拟的是手机端的首页。

    Post

    如果要以POST发送一个请求,只需要把参数data以bytes形式传入。我们来模拟一下微博的登录

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    from urllib import request, parse
    
    print('Login to weibo.cn...')
    email = input('Email: ')
    passwd = input('Password: ')
    login_data = parse.urlencode([
        ('username', email),
        ('password', passwd),
        ('entry', 'mweibo'),
        ('client_id', ''),
        ('savestate', '1'),
        ('ec', ''),
        ('pagerefer', 'https://passport.weibo.cn/signin/welcome?entry=mweibo&r=http%3A%2F%2Fm.weibo.cn%2F')
    ])
    
    req = request.Request('https://passport.weibo.cn/sso/login')
    req.add_header('Origin', 'https://passport.weibo.cn')
    req.add_header('User-Agent', 'Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25')
    req.add_header('Referer', 'https://passport.weibo.cn/signin/login?entry=mweibo&res=wel&wm=3349&r=http%3A%2F%2Fm.weibo.cn%2F')
    
    with request.urlopen(req, data=login_data.encode('utf-8')) as f:
        print('Status:', f.status, f.reason)
        for k, v in f.getheaders():
            print('%s: %s' % (k, v))
        print('Data:', f.read().decode('utf-8'))
    

    输出结果:

    Status: 200 OK
    Server: nginx/1.6.1
    Date: Wed, 07 Feb 2018 11:35:42 GMT
    Content-Type: text/html
    Transfer-Encoding: chunked
    Connection: close
    Vary: Accept-Encoding
    Cache-Control: no-cache, must-revalidate
    Expires: Sat, 26 Jul 1997 05:00:00 GMT
    Pragma: no-cache
    Access-Control-Allow-Origin: https://passport.weibo.cn
    Access-Control-Allow-Credentials: true
    Set-Cookie: SUB=_2A253fpTeDeThGeVP4lQU9irKzT-IHXVUgDyWrDV6PUJbkdANLVnFkW1NTTnNOZWZSyoKlZCOotoJpMs1Uoa0SND8; Path=/; Domain=.weibo.cn; Expires=Thu, 07 Feb 2019 11:35:42 GMT; HttpOnly
    Set-Cookie: SUHB=0vJYfuFBzMfWcU; expires=Thursday, 07-Feb-2019 11:35:42 GMT; path=/; domain=.weibo.cn
    Set-Cookie: SCF=AudxsVEcHHQp3hfmOm6IPPEjejSbj3rJ6m0Z77U_WmWNRu-wr0A2W1OgKqMjpcMiF8-OuoYLLjnk9I4qUXNZqgg.; expires=Saturday, 05-Feb-2028 11:35:42 GMT; path=/; domain=.weibo.cn; httponly
    Set-Cookie: SSOLoginState=1518003342; path=/; domain=weibo.cn
    Set-Cookie: ALF=1520595342; expires=Friday, 09-Mar-2018 11:35:42 GMT; path=/; domain=.sina.cn
    DPOOL_HEADER: lich80
    Set-Cookie: login=f34d845f2ab0a4b6999b8de4cd23cb3f; Path=/
    Data: {"retcode":20000000,"msg":"","data":{"loginresulturl":"https:\/\/passport.weibo.com\/sso\/crossdomain?entry=mweibo&action=login&proj=1&ticket=ST-MzE5NjU2NDY2Mw%3D%3D-1518003342-aliyun-5F83119A92CC6966117D1D20D46575FE-1&display=0&cdurl=https%3A%2F%2Flogin.sina.com.cn%2Fsso%2Fcrossdomain%3Fentry%3Dmweibo%26action%3Dlogin%26proj%3D1%26ticket%3DST-MzE5NjU2NDY2Mw%253D%253D-1518003342-aliyun-6ED95CC67505BF202311D1BD83F67D6A-1%26display%3D0%26cdurl%3Dhttps%253A%252F%252Fpassport.sina.cn%252Fsso%252Fcrossdomain%253Fentry%253Dmweibo%2526action%253Dlogin%2526display%253D0%2526ticket%253DST-MzE5NjU2NDY2Mw%25253D%25253D-1518003342-aliyun-787B7337EFDF43151FC3B52C1E383597-1","uid":"3196564663"}}
    

    Handler

    如果还需要更复杂的控制,比如通过一个Proxy去访问网站,我们需要利用ProxyHandler来处理,示例代码如下:

    proxy_handler = urllib.request.ProxyHandler({'http': 'http://www.example.com:3128/'})
    proxy_auth_handler = urllib.request.ProxyBasicAuthHandler()
    proxy_auth_handler.add_password('realm', 'host', 'username', 'password')
    opener = urllib.request.build_opener(proxy_handler, proxy_auth_handler)
    with opener.open('http://www.example.com/login.html') as f:
        pass
    

    urllib提供的功能就是利用程序去执行各种HTTP请求。如果要模拟浏览器完成特定功能,需要把请求伪装成浏览器。伪装的方法是先监控浏览器发出的请求,再根据浏览器的请求头来伪装。

    XML

    json没有流行起来是,Web中应用中还采用的XML格式,XML 的解析有两种方式DOMSAXPython中提供了专门的解析方法。

    DOM vs SAX

    操作XML有两种方法:DOM和SAX。DOM会把整个XML读入内存,解析为树,因此占用内存大,解析慢,优点是可以任意遍历树的节点。SAX是流模式,边读边解析,占用内存小,解析快,缺点是我们需要自己处理事件,正常情况下,优先考虑SAX,因为DOM实在太占内存。

    在Python中使用SAX解析XML非常简洁,通常我们关心的事件是start_element,end_element和char_data,准备好这3个函数,然后就可以解析xml了。如下:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    from xml.parsers.expat import ParserCreate
    
    class DefaultSaxHandler(object):
        def start_element(self, name, attrs):
            print('sax:start_element: %s, attrs: %s' % (name, str(attrs)))
    
        def end_element(self, name):
            print('sax:end_element: %s' % name)
    
        def char_data(self, text):
            print('sax:char_data: %s' % text)
    
    xml = r'''<?xml version="1.0"?>
    <ol>
        <li><a href="/python">Python</a></li>
        <li><a href="/ruby">Ruby</a></li>
    </ol>
    '''
    
    handler = DefaultSaxHandler()
    parser = ParserCreate()
    parser.StartElementHandler = handler.start_element
    parser.EndElementHandler = handler.end_element
    parser.CharacterDataHandler = handler.char_data
    parser.Parse(xml)
    

    输出结果:

    sax:start_element: ol, attrs: {}
    sax:char_data: 
    
    sax:char_data:     
    sax:start_element: li, attrs: {}
    sax:start_element: a, attrs: {'href': '/python'}
    sax:char_data: Python
    sax:end_element: a
    sax:end_element: li
    sax:char_data: 
    
    sax:char_data:     
    sax:start_element: li, attrs: {}
    sax:start_element: a, attrs: {'href': '/ruby'}
    sax:char_data: Ruby
    sax:end_element: a
    sax:end_element: li
    sax:char_data: 
    
    sax:end_element: ol
    
    • 解析XML时,注意找出自己感兴趣的节点,响应事件时,把节点数据保存起来。解析完毕后,就可以处理数据。

    HTMLParser

    如何解析该HTML页面,我们用爬虫把目标网站的页面抓下来,然后就是解析HTML,因为HTML本质上是XML的子集,但是HTML的语法没有XML那么严格,所以不能用标准的DOM或SAX来解析HTML。所以Python提供了HTMLParser来非常方便地解析HTML,如下:

    from html.parser import HTMLParser
    from html.entities import name2codepoint
    
    class MyHTMLParser(HTMLParser):
    
        def handle_starttag(self, tag, attrs):
            print('<%s>' % tag)
    
        def handle_endtag(self, tag):
            print('</%s>' % tag)
    
        def handle_startendtag(self, tag, attrs):
            print('<%s/>' % tag)
    
        def handle_data(self, data):
            print(data)
    
        def handle_comment(self, data):
            print('<!--', data, '-->')
    
        def handle_entityref(self, name):
            print('&%s;' % name)
    
        def handle_charref(self, name):
            print('&#%s;' % name)
    
    parser = MyHTMLParser()
    parser.feed('''<html>
    <head></head>
    <body>
    <!-- test html parser -->
        <p>Some <a href=\"#\">html</a> HTML&nbsp;tutorial...<br>END</p>
    </body></html>''')
    

    输出结果:

    <html>
    
    
    <head>
    </head>
    
    
    <body>
    
    
    <!--  test html parser  -->
    
        
    <p>
    Some 
    <a>
    html
    </a>
     HTML tutorial...
    <br>
    END
    </p>
    
    
    </body>
    </html>
    

    解析过程中,feed()方法可以多次调用,也就是不一定一次把整个HTML字符串都塞进去,可以一部分一部分塞进去。特殊字符有两种,一种是英文表示的 ,一种是数字表示的Ӓ,这两种字符都可以通过Parser解析出来。

    • 利用HTMLParser,可以把网页中的文本、图像等解析出来。

    在这里我们基本上把python中内置模块的部分重要的方法和用法讲解了,但是还有好多都没有提到,在实践中可以通过官方文档,采用最优的方法解决具体问题。

    相关文章

      网友评论

        本文标题:Python3(12) Python 常用内建模块

        本文链接:https://www.haomeiwen.com/subject/sguszxtx.html