1.datetime
datetime是Python處理日期和時(shí)間的標(biāo)準(zhǔn)庫(kù)。它的用法如下:
a.獲取當(dāng)前時(shí)間
>>> from datetime import datetime
>>> now = datetime.now() # 獲取當(dāng)前datetime
>>> print(now)
b.獲取指定時(shí)間和日期
>>> from datetime import datetime
>>> dt = datetime(2015, 4, 19, 12, 20) # 用指定日期時(shí)間創(chuàng)建datetime
>>> print(dt)
c.datetime轉(zhuǎn)換為timestamp
在計(jì)算機(jī)中,時(shí)間實(shí)際上是用數(shù)字表示的。我們把1970年1月1日 00:00:00 UTC+00:00時(shí)區(qū)的時(shí)刻稱為epoch time,記為0
(1970年以前的時(shí)間timestamp為負(fù)數(shù)),當(dāng)前時(shí)間就是相對(duì)于epoch time的秒數(shù),稱為timestamp。timestamp的值與時(shí)區(qū)毫無(wú)關(guān)系,因?yàn)閠imestamp一旦確定,其UTC時(shí)間就確定了,轉(zhuǎn)換到任意時(shí)區(qū)的時(shí)間也是完全確定的,這就是為什么計(jì)算機(jī)存儲(chǔ)的當(dāng)前時(shí)間是以timestamp表示的,因?yàn)槿蚋鞯氐挠?jì)算機(jī)在任意時(shí)刻的timestamp都是完全相同的(假定時(shí)間已校準(zhǔn))。
>>> from datetime import datetime
>>> dt = datetime(2015, 4, 19, 12, 20) # 用指定日期時(shí)間創(chuàng)建datetime
>>> dt.timestamp()
d.timestamp轉(zhuǎn)換為datetime
>>> from datetime import datetime
>>> t = 1429417200.0
>>> print(datetime.fromtimestamp(t))
>>> print(datetime.utcfromtimestamp(t)) # UTC時(shí)間
e.str轉(zhuǎn)換為datetime
>>> from datetime import datetime
>>> cday = datetime.strptime('2015-6-1 18:19:59', '%Y-%m-%d %H:%M:%S')
>>> print(cday)
f.datetime轉(zhuǎn)換為str
>>> from datetime import datetime
>>> now = datetime.now()
>>> print(now.strftime('%a, %b %d %H:%M'))
Mon, May 05 16:28
g.時(shí)區(qū)轉(zhuǎn)換
# 拿到UTC時(shí)間,并強(qiáng)制設(shè)置時(shí)區(qū)為UTC+0:00:
>>> utc_dt = datetime.utcnow().replace(tzinfo=timezone.utc)
>>> print(utc_dt)
2015-05-18 09:05:12.377316+00:00# astimezone()
將轉(zhuǎn)換時(shí)區(qū)為北京時(shí)間:
>>> bj_dt = utc_dt.astimezone(timezone(timedelta(hours=8)))
>>> print(bj_dt)
2015-05-18 17:05:12.377316+08:00# astimezone()
將轉(zhuǎn)換時(shí)區(qū)為東京時(shí)間:
>>> tokyo_dt = utc_dt.astimezone(timezone(timedelta(hours=9)))
>>> print(tokyo_dt)
2015-05-18 18:05:12.377316+09:00# astimezone()
將bj_dt轉(zhuǎn)換時(shí)區(qū)為東京時(shí)間:
>>> tokyo_dt2 = bj_dt.astimezone(timezone(timedelta(hours=9)))
>>> print(tokyo_dt2)
2015-05-18 18:05:12.377316+09:00
2namedtuple
namedtuple是一個(gè)函數(shù),它用來(lái)創(chuàng)建一個(gè)自定義的tuple對(duì)象,并且規(guī)定了tuple元素的個(gè)數(shù),并可以用屬性而不是索引來(lái)引用tuple的某個(gè)元素。
這樣一來(lái),我們用namedtuple可以很方便地定義一種數(shù)據(jù)類型,它具備tuple的不變性,又可以根據(jù)屬性來(lái)引用,使用十分方便。
>>> from collections import namedtuple
>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(1, 2)
>>> p.x1
>>> p.y2
3.deque
使用list存儲(chǔ)數(shù)據(jù)時(shí),按索引訪問(wèn)元素很快,但是插入和刪除元素就很慢了,因?yàn)閘ist是線性存儲(chǔ),數(shù)據(jù)量大的時(shí)候,插入和刪除效率很低。
deque是為了高效實(shí)現(xiàn)插入和刪除操作的雙向列表,適合用于隊(duì)列和棧:
>>> from collections import deque
>>> q = deque(['a', 'b', 'c'])
>>> q.append('x')
>>> q.appendleft('y')
>>> q
deque(['y', 'a', 'b', 'c', 'x'])
4.defaultdict
使用dict時(shí),如果引用的Key不存在,就會(huì)拋出KeyError。如果希望key不存在時(shí),返回一個(gè)默認(rèn)值,就可以用defaultdict:
>>> from collections import defaultdict
>>> dd = defaultdict(lambda: 'N/A')
>>> dd['key1'] = 'abc'
>>> dd['key1'] # key1存在
'abc'
>>> dd['key2'] # key2不存在,返回默認(rèn)值
'N/A'
注意默認(rèn)值是調(diào)用函數(shù)返回的,而函數(shù)在創(chuàng)建defaultdict對(duì)象時(shí)傳入。
除了在Key不存在時(shí)返回默認(rèn)值,defaultdict的其他行為跟dict是完全一樣的。
5.OrderedDict
使用dict時(shí),Key是無(wú)序的。在對(duì)dict做迭代時(shí),我們無(wú)法確定Key的順序。如果要保持Key的順序,可以用OrderedDict:
>>> from collections import OrderedDict
>>> d = dict([('a', 1), ('b', 2), ('c', 3)])
>>> d # dict的Key是無(wú)序的
{'a': 1, 'c': 3, 'b': 2}
>>> od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
>>> od # OrderedDict的Key是有序的
OrderedDict([('a', 1), ('b', 2), ('c', 3)])
6.Counter
Counter是一個(gè)簡(jiǎn)單的計(jì)數(shù)器,例如,統(tǒng)計(jì)字符出現(xiàn)的個(gè)數(shù):
>>> from collections import Counter
>>> c = Counter()
>>> for ch in 'programming':
... c[ch] = c[ch] + 1...
>>> c
Counter({'g': 2, 'm': 2, 'r': 2, 'a': 1, 'i': 1, 'o': 1, 'n': 1, 'p': 1})
Counter實(shí)際上也是dict的一個(gè)子類,上面的結(jié)果可以看出,字符'g'、'm'、'r'各出現(xiàn)了兩次,其他字符各出現(xiàn)了一次。
7.base64
Base64是一種用64個(gè)字符來(lái)表示任意二進(jìn)制數(shù)據(jù)的方法。
用記事本打開exe、jpg、pdf這些文件時(shí),我們都會(huì)看到一大堆亂碼,因?yàn)槎M(jìn)制文件包含很多無(wú)法顯示和打印的字符,所以,如果要讓記事本這樣的文本處理軟件能處理二進(jìn)制數(shù)據(jù),就需要一個(gè)二進(jìn)制到字符串的轉(zhuǎn)換方法。Base64是一種最常見的二進(jìn)制編碼方法。
Base64編碼會(huì)把3字節(jié)的二進(jìn)制數(shù)據(jù)編碼為4字節(jié)的文本數(shù)據(jù),長(zhǎng)度增加33%,好處是編碼后的文本數(shù)據(jù)可以在郵件正文、網(wǎng)頁(yè)等直接顯示。
如果要編碼的二進(jìn)制數(shù)據(jù)不是3的倍數(shù),最后會(huì)剩下1個(gè)或2個(gè)字節(jié)怎么辦?Base64用\x00字節(jié)在末尾補(bǔ)足后,再在編碼的末尾加上1個(gè)或2個(gè)=號(hào),表示補(bǔ)了多少字節(jié),解碼的時(shí)候,會(huì)自動(dòng)去掉。
Python內(nèi)置的base64可以直接進(jìn)行base64的編解碼:
>>> import base64
>>> base64.b64encode(b'binary\x00string')
b'YmluYXJ5AHN0cmluZw=='
>>> base64.b64decode(b'YmluYXJ5AHN0cmluZw==')
b'binary\x00string'
由于標(biāo)準(zhǔn)的Base64編碼后可能出現(xiàn)字符+和/,在URL中就不能直接作為參數(shù),所以又有一種"url safe"的base64編碼,其實(shí)就是把字符+和/分別變成-和_:
>>> base64.b64encode(b'i\xb7\x1d\xfb\xef\xff')
b'abcd++//'
>>> base64.urlsafe_b64encode(b'i\xb7\x1d\xfb\xef\xff')
b'abcd--__'
>>> base64.urlsafe_b64decode('abcd--__')
b'i\xb7\x1d\xfb\xef\xff'
8.struct
Python提供了一個(gè)struct模塊來(lái)解決bytes和其他二進(jìn)制數(shù)據(jù)類型的轉(zhuǎn)換。
struct的pack函數(shù)把任意數(shù)據(jù)類型變成bytes:
>>> import struct
>>> struct.pack('>I', 10240099)
b'\x00\x9c@c'
pack的第一個(gè)參數(shù)是處理指令,'>I'的意思是:
>表示字節(jié)順序是big-endian,也就是網(wǎng)絡(luò)序,I表示4字節(jié)無(wú)符號(hào)整數(shù)。后面的參數(shù)個(gè)數(shù)要和處理指令一致。
unpack把bytes變成相應(yīng)的數(shù)據(jù)類型:
>>> struct.unpack('>IH', b'\xf0\xf0\xf0\xf0\x80\x80')
(4042322160, 32896)
根據(jù)>IH的說(shuō)明,后面的bytes依次變?yōu)镮:4字節(jié)無(wú)符號(hào)整數(shù)和H:2字節(jié)無(wú)符號(hào)整數(shù)。
9.hashlib
Python的hashlib提供了常見的摘要算法,如MD5,SHA1等等。
什么是摘要算法呢?摘要算法又稱哈希算法、散列算法。它通過(guò)一個(gè)函數(shù),把任意長(zhǎng)度的數(shù)據(jù)轉(zhuǎn)換為一個(gè)長(zhǎng)度固定的數(shù)據(jù)串(通常用16進(jìn)制的字符串表示)。
import hashlib
md5 = hashlib.md5()
md5.update('how to use md5 in python hashlib?'.encode('utf-8'))
print(md5.hexdigest())
MD5是最常見的摘要算法,速度很快,生成結(jié)果是固定的128 bit字節(jié),通常用一個(gè)32位的16進(jìn)制字符串表示。
另一種常見的摘要算法是SHA1,調(diào)用SHA1和調(diào)用MD5完全類似:
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())
SHA1的結(jié)果是160 bit字節(jié),通常用一個(gè)40位的16進(jìn)制字符串表示。
比SHA1更安全的算法是SHA256和SHA512,不過(guò)越安全的算法不僅越慢,而且摘要長(zhǎng)度更長(zhǎng)。
10.itertools
Python的內(nèi)建模塊itertools提供了非常有用的用于操作迭代對(duì)象的函數(shù)
a.count函數(shù)
>>> import itertools
>>> natuals = itertools.count(1)
>>> for n in natuals:
... print(n)
...
1
2
3
...
b.cycle函數(shù)
>>> import itertools
>>> cs = itertools.cycle('ABC') # 注意字符串也是序列的一種
>>> for c in cs:... print(c)
...
'A'
'B'
'C'
'A'
'B'
'C'
...
c.repeat函數(shù)
>> ns = itertools.repeat('A', 3)
>>> for n in ns:
... print(n)
...
A
A
A
無(wú)限序列雖然可以無(wú)限迭代下去,但是通常我們會(huì)通過(guò)takewhile()等函數(shù)根據(jù)條件判斷來(lái)截取出一個(gè)有限的序列:
>>> natuals = itertools.count(1)
>>> ns = itertools.takewhile(lambda x: x <= 10, natuals)
>>> list(ns)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
itertools提供的幾個(gè)迭代器操作函數(shù)更加有用:
d.chain()函數(shù)
chain()可以把一組迭代對(duì)象串聯(lián)起來(lái),形成一個(gè)更大的迭代器:
>>> for c in itertools.chain('ABC', 'XYZ')
... print(c)
# 迭代效果:
'A' 'B' 'C' 'X' 'Y' 'Z'
e.groupby()函數(shù)
groupby()把迭代器中相鄰的重復(fù)元素挑出來(lái)放在一起:
>>> 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']
實(shí)際上挑選規(guī)則是通過(guò)函數(shù)完成的,只要作用于函數(shù)的兩個(gè)元素返回的值相等,這兩個(gè)元素就被認(rèn)為是在一組的,而函數(shù)返回值作為組的key。如果我們要忽略大小寫分組,就可以讓元素'A'和'a'都返回相同的key:
>>> for key, group in itertools.groupby('AaaBBbcCAAa', lambda c: c.upper()):
... print(key, list(group))
...
A ['A', 'a', 'a']
B ['B', 'B', 'b']
C ['c', 'C']
A ['A', 'A', 'a']
11.xml
DOM vs SAX
操作XML有兩種方法:DOM和SAX。DOM會(huì)把整個(gè)XML讀入內(nèi)存,解析為樹,因此占用內(nèi)存大,解析慢,優(yōu)點(diǎn)是可以任意遍歷樹的節(jié)點(diǎn)。SAX是流模式,邊讀邊解析,占用內(nèi)存小,解析快,缺點(diǎn)是我們需要自己處理事件。
正常情況下,優(yōu)先考慮SAX,因?yàn)镈OM實(shí)在太占內(nèi)存。
在Python中使用SAX解析XML非常簡(jiǎn)潔,通常我們關(guān)心的事件是start_element,end_element和char_data,準(zhǔn)備好這3個(gè)函數(shù),然后就可以解析xml了。
舉個(gè)例子,當(dāng)SAX解析器讀到一個(gè)節(jié)點(diǎn)時(shí):
<a href="/">python</a>
會(huì)產(chǎn)生3個(gè)事件:
1.start_element事件,在讀取<a href="/">時(shí);
2.char_data事件,在讀取python時(shí);
3.end_element事件,在讀取</a>時(shí)。
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)
12.HTMLParser
Python提供了HTMLParser來(lái)非常方便地解析HTML,只需簡(jiǎn)單幾行代碼:
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>''')
feed()方法可以多次調(diào)用,也就是不一定一次把整個(gè)HTML字符串都塞進(jìn)去,可以一部分一部分塞進(jìn)去。特殊字符有兩種,一種是英文表示的?,一種是數(shù)字表示的?,這兩種字符都可以通過(guò)Parser解析出來(lái)。
13.urllib
urllib提供了一系列用于操作URL的功能。
a.Get方法
urllib的request模塊可以非常方便地抓取URL內(nèi)容,也就是發(fā)送一個(gè)GET請(qǐng)求到指定的頁(yè)面,然后返回HTTP的響應(yīng):
例如,對(duì)豆瓣的一個(gè)URLhttps://api.douban.com/v2/book/2129650
進(jìn)行抓取,并返回響應(yīng):
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'))
可以看到HTTP響應(yīng)的頭和JSON數(shù)據(jù):
Status: 200 OK
Server: nginx
Date: Tue, 26 May 2015 10:02:27 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 2049
Connection: close
Expires: Sun, 1 Jan 2006 01:00:00 GMT
Pragma: no-cache
Cache-Control: must-revalidate, no-cache, private
X-DAE-Node: pidl1
Data: {"rating":{"max":10,"numRaters":16,"average":"7.4","min":0},"subtitle":"","author":["廖雪峰編著"],"pubdate":"2007-6","tags":[{"count":20,"name":"spring","title":"spring"}...}
如果我們要想模擬瀏覽器發(fā)送GET請(qǐng)求,就需要使用Request對(duì)象,通過(guò)往Request對(duì)象添加HTTP頭,我們就可以把請(qǐng)求偽裝成瀏覽器。例如,模擬iPhone 6去請(qǐng)求豆瓣首頁(yè):
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'))
這樣豆瓣會(huì)返回適合iPhone的移動(dòng)版網(wǎng)頁(yè):
... <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0">
<meta name="format-detection" content="telephone=no">
<link rel="apple-touch-icon" sizes="57x57" />...
b.Post方法
如果要以POST發(fā)送一個(gè)請(qǐng)求,只需要把參數(shù)data以bytes形式傳入。
我們模擬一個(gè)微博登錄,先讀取登錄的郵箱和口令,然后按照weibo.cn的登錄頁(yè)的格式以u(píng)sername=xxx&password=xxx的編碼傳入:
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'))
如果登錄成功,我們獲得的響應(yīng)如下:
Status: 200 OK
Server: nginx/1.2.0
...
Set-Cookie: SSOLoginState=1432620126; path=/; domain=weibo.cn
...
Data: {"retcode":20000000,"msg":"","data":{...,"uid":"1658384301"}}
如果登錄失敗,我們獲得的響應(yīng)如下:
...
Data: {"retcode":50011015,"msg":"\u7528\u6237\u540d\u6216\u5bc6\u7801\u9519\u8bef","data":{"username":"example@python.org","errline":536}}
14.PIL
參考:
PIL--廖雪峰的官方教程