Python3(12) Python 常用内建模块

本系列主要学习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中内置模块的部分重要的方法和用法讲解了,但是还有好多都没有提到,在实践中可以通过官方文档,采用最优的方法解决具体问题。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,265评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,274评论 1 288
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,087评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,479评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,782评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,218评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,594评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,316评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,955评论 1 237
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,274评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,803评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,177评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,732评论 3 229
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,953评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,687评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,263评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,189评论 2 258

推荐阅读更多精彩内容

  • pyton review 学习指南 https://www.zhihu.com/question/29138020...
    孙小二wuk阅读 1,013评论 0 2
  • # Python 资源大全中文版 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列...
    aimaile阅读 26,290评论 6 428
  • Python常用库大全,看看有没有你需要的。 环境管理 管理 Python 版本和环境的工具 p – 非常简单的交...
    XDgbh阅读 15,742评论 4 147
  • 环境管理管理Python版本和环境的工具。p–非常简单的交互式python版本管理工具。pyenv–简单的Pyth...
    MrHamster阅读 3,745评论 1 61
  • 我看到你了,浑身散发着白色的光,向我走来,很纯净很舒服,而且我还看到,你在我肚子里的时候,一点点光照亮你的脸庞,柔...
    金晶花阅读 185评论 0 0