本系列主要學習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
除了實現list
的append()
和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
是一種任意二進制到文本字符串的編碼方法,常用于在URL
、Cookie
、網頁中傳輸少量二進制數據。
#!/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編碼
因為=
用在URL
、Cookie
里面會造成歧義,所以,很多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
Python
的hashlib
提供了常見的摘要算法,又稱哈希算法、散列算法,它通過一個函數,把任意長度的數據轉換為一個長度固定的數據串(通常用16進制的字符串表示),常見的有MD5
、SHA1
等。
#!/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
的解析有兩種方式DOM
、SAX
。Python
中提供了專門的解析方法。
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 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中內置模塊的部分重要的方法和用法講解了,但是還有好多都沒有提到,在實踐中可以通過官方文檔,采用最優的方法解決具體問題。