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閱讀 229,698評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,202評論 3 426
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,742評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,580評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,297評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,688評論 1 327
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,693評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,875評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,438評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,183評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,384評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,931評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,612評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,022評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,297評論 1 292
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,093評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,330評論 2 377

推薦閱讀更多精彩內容

  • pyton review 學習指南 https://www.zhihu.com/question/29138020...
    孫小二wuk閱讀 1,061評論 0 2
  • # Python 資源大全中文版 我想很多程序員應該記得 GitHub 上有一個 Awesome - XXX 系列...
    aimaile閱讀 26,533評論 6 427
  • Python常用庫大全,看看有沒有你需要的。 環境管理 管理 Python 版本和環境的工具 p – 非常簡單的交...
    XDgbh閱讀 15,875評論 4 147
  • 環境管理管理Python版本和環境的工具。p–非常簡單的交互式python版本管理工具。pyenv–簡單的Pyth...
    MrHamster閱讀 3,812評論 1 61
  • 我看到你了,渾身散發著白色的光,向我走來,很純凈很舒服,而且我還看到,你在我肚子里的時候,一點點光照亮你的臉龐,柔...
    金晶花閱讀 188評論 0 0