人生苦短,,我用 Python
引言
上一篇我們聊了 urlopen 的基本使用姿勢,,但這幾個簡單的參數(shù)并不足以構(gòu)建一個完整的請求。對于復(fù)雜的請求,,例如需要添加請求頭就顯得無能為力,這時我們可以選擇使用 Request ,。
Request
官方文檔:https://docs./zh-cn/3.7/library/urllib.request.html
首先來看一下 Request 的使用語法:
class urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
- url:請求的地址鏈接,,只有這個是必傳參數(shù),其余都是可選參數(shù),。
- data:如果這個參數(shù)需要傳遞,,則必須傳bytes(字節(jié)流)類型的,。
- headers:請求頭信息,它是一個字典,,可以在構(gòu)造請求的時候通過 headers 之間構(gòu)造,,也可以調(diào)用 add_header() 添加。
- origin_req_host:發(fā)起請求一方的 host 名稱或者也可以是 ip 地址,。
- unverifiable:指的是這個請求是否是無法驗(yàn)證的,,默認(rèn)是 False 。意思就是說用戶沒有足夠權(quán)限來選擇接收這個請求的結(jié)果,。例如我們請求一個HTML文檔中的圖片,,但是我們沒有自動抓取圖像的權(quán)限,這時 unverifiable 的值就是 True ,。
- method:請求方法,,如 GET 、 POST ,、 PUT ,、 DELETE 等等。
還是先來看一個簡單的示例,,使用 Request 爬取博客網(wǎng)站:
import urllib.request
request = urllib.request.Request('https://www./')
response = urllib.request.urlopen(request)
print(response.read().decode('utf-8'))
可以看到,,這里還是使用 urlopen() 來發(fā)起請求,只是參數(shù)不再是之前的 URL ,、 Data ,、 timeout 等等信息,而是變成了 Request 類型的對象,。
我們來構(gòu)建一個稍微復(fù)雜一點(diǎn)的請求,。
import urllib.request, urllib.parse
import json
url = 'https:///post'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36',
'Content-Type': 'application/json;encoding=utf-8',
'Host': ''
}
data = {
'name': 'geekdigging',
'hello':'world'
}
data = bytes(json.dumps(data), encoding='utf8')
req = urllib.request.Request(url=url, data=data, headers=headers, method='POST')
resp = urllib.request.urlopen(req)
print(resp.read().decode('utf-8'))
結(jié)果如下:
{
"args": {},
"data": "{\"name\": \"geekdigging\", \"hello\": \"world\"}",
"files": {},
"form": {},
"headers": {
"Accept-Encoding": "identity",
"Content-Length": "41",
"Content-Type": "application/json;encoding=utf-8",
"Host": "",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
},
"json": {
"hello": "world",
"name": "geekdigging"
},
"origin": "116.234.254.11, 116.234.254.11",
"url": "https:///post"
}
這里我們通過 4 個參數(shù)構(gòu)建了一個 Request 對象。
通過 url 指定了訪問的鏈接,,還是前面一片文章中提到的測試鏈接,。
在 headers 中指定了 User-Agent 、 Content-Type 和 Host 3 個參數(shù),。
在 data 中使用 json.dumps() 將一個 dict 轉(zhuǎn)換成 json 格式,,并通過 bytes() 最終轉(zhuǎn)換為字節(jié)流。
最后,,指定了訪問方式為 POST ,。
從最終的結(jié)果中,,可以看到我們前面的設(shè)定全都成功,。
進(jìn)階操作
前面我們使用 Request 完成了請求頭的添加,如果我們想處理 Cookies 和使用代理訪問,,就需要使用到更加強(qiáng)大的 Handler 了,。 Handler 可以簡單理解為各種功能的處理器,,使用它,幾乎可以為我們做到所有有關(guān) HTTP 請求的事情,。
urllib.request 為我們提供了 BaseHandler 類,,它是所有其他 Handler 的父類,它提供了直接使用使用的方法如下:
- add_parent():添加director作為父類,。
- close():關(guān)閉它的父類,。
- parent():打開使用不同的協(xié)議或處理錯誤。
- default_open():捕獲所有的URL及子類,,在協(xié)議打開之前調(diào)用,。
接下來,就有各種 Handler 子類集成這個 BaseHandler 類:
- HTTPDefaultErrorHandler:用來處理http響應(yīng)錯誤,,錯誤會拋出HTTPError類的異常,。
- HTTPRedirectHandler:用于處理重定向。
- ProxyHandler:用于設(shè)置代理,,默認(rèn)代理為空,。
- HTTPPasswordMgr:用于管理密碼,它維護(hù)用戶名和密碼表,。
- AbstractBasicAuthHandler:用于獲取用戶/密碼對,,然后重試請求來處理身份驗(yàn)證請求。
- HTTPBasicAuthHandler:用于重試帶有身份認(rèn)證信息的請求,。
- HTTPCookieProcessor:用于處理cookies,。
等等, urllib 為我們提供的 BaseHandler 子類非常的多,,小編這里就不一一列舉,,各位同學(xué)可以通過訪問官方文檔來查看。
官方文檔地址:https://docs./zh-cn/3.7/library/urllib.request.html#basehandler-objects
在介紹如何使用 Handler 之前,,先介紹一個高級類: OpenerDirector ,。
OpenerDirector 是用來處理URL的高級類,它分三個階段來打開URL:
在每個階段中調(diào)用這些方法的順序是通過對處理程序?qū)嵗M(jìn)行排序來確定的;每個使用此類方法的程序都會調(diào)用 protocol_request() 方法來預(yù)處理請求,,然后調(diào)用 protocol_open() 來處理請求;最后調(diào)用 protocol_response() 方法來處理響應(yīng),。
我們可以稱 OpenerDirector 為 Opener 。我們之前用過 urlopen() 這個方法,,實(shí)際上它就是 urllib 為我們提供的一個 Opener ,。
Opener的方法包括:
- add_handler(handler):添加處理程序到鏈接中
- open(url,data=None[,timeout]):打開給定的URL與urlopen()方法相同
- error(proto,*args):處理給定協(xié)議的錯誤
下面我們來演示一下如何獲取網(wǎng)站的 Cookies :
import http.cookiejar, urllib.request
# 實(shí)例化cookiejar對象
cookie = http.cookiejar.CookieJar()
# 使用 HTTPCookieProcessor 構(gòu)建一個 handler
handler = urllib.request.HTTPCookieProcessor(cookie)
# 構(gòu)建Opener
opener = urllib.request.build_opener(handler)
# 發(fā)起請求
response = opener.open('https://www.baidu.com/')
print(cookie)
for item in cookie:
print(item.name + " = " + item.value)
代碼中具體的含義小編就不再解釋了,注釋已經(jīng)寫得比較完善,。最后得到的打印結(jié)果如下:
<CookieJar[<Cookie BAIDUID=48EA1A60922D7A30F711A420D3C5BA22:FG=1 for .baidu.com/>, <Cookie BIDUPSID=48EA1A60922D7A30DA2E4CBE7B81D738 for .baidu.com/>, <Cookie PSTM=1575167484 for .baidu.com/>, <Cookie BD_NOT_HTTPS=1 for www.baidu.com/>]>
BAIDUID = 48EA1A60922D7A30F711A420D3C5BA22:FG=1
BIDUPSID = 48EA1A60922D7A30DA2E4CBE7B81D738
PSTM = 1575167484
BD_NOT_HTTPS = 1
這里產(chǎn)生一個問題,, cookie 既然可以打印,那么我們能不能將 cookie 的輸出保存到文件中呢?
答案當(dāng)然是可以的,,因?yàn)槲覀冎溃?cookie 本身就是保存在文件中的,。
# cookies 保存 Mozilla 型文件示例
filename = 'cookies_mozilla.txt'
cookie = http.cookiejar.MozillaCookieJar(filename)
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
cookie.save(ignore_discard=True, ignore_expires=True)
print('cookies_mozilla 保存成功')
這里我們需修改之前的 CookieJar 為 MozillaCookieJar ,它在生成文件時會用到,,是 CookieJar 的子類,,可以用來處理 Cookies 和文件相關(guān)的事件,比如讀取和保存 Cookies ,,可以將 Cookies 保存成 Mozilla 型瀏覽器的 Cookies 格式,。
在運(yùn)行完成之后,我們可以在當(dāng)前程序的目錄下看到生成了一個 cookies.txt 的文件,,具體內(nèi)容如下:
# Netscape HTTP Cookie File
# http://curl./rfc/cookie_spec.html
# This is a generated file! Do not edit.
.baidu.com TRUE / FALSE 1606703804 BAIDUID 0A7A76A3705A730B35A559B601425953:FG=1
.baidu.com TRUE / FALSE 3722651451 BIDUPSID 0A7A76A3705A730BE64A1F6D826869B5
.baidu.com TRUE / FALSE H_PS_PSSID 1461_21102_30211_30125_26350_30239
.baidu.com TRUE / FALSE 3722651451 PSTM 1575167805
.baidu.com TRUE / FALSE delPer 0
www.baidu.com FALSE / FALSE BDSVRTM 0
www.baidu.com FALSE / FALSE BD_HOME 0
小編比較懶,,就不截圖了,直接貼結(jié)果了,。
當(dāng)然我們除了可以將 cookies 保存成為 Mozilla 型瀏覽器的格式,,還可以將 cookies 保存成為 libwww-perl(LWP) 格式的 Cookies 文件。
要保存成LWP格式的Cookies文件,,在聲明的時候需要修改為 LWPCookieJar:
# cookies 保存 LWP 型文件示例
filename = 'cookies_lwp.txt'
cookie = http.cookiejar.LWPCookieJar(filename)
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
cookie.save(ignore_discard=True, ignore_expires=True)
print('cookies_lwp 保存成功')
執(zhí)行結(jié)果如下:
#LWP-Cookies-2.0
Set-Cookie3: BAIDUID="D634D45523004545C6E23691E7CE3894:FG=1"; path="/"; domain=".baidu.com"; path_spec; domain_dot; expires="2020-11-30 02:45:24Z"; comment=bd; version=0
Set-Cookie3: BIDUPSID=D634D455230045458E6056651566B7E3; path="/"; domain=".baidu.com"; path_spec; domain_dot; expires="2087-12-19 05:59:31Z"; version=0
Set-Cookie3: H_PS_PSSID=1427_21095_30210_18560_30125; path="/"; domain=".baidu.com"; path_spec; domain_dot; discard; version=0
Set-Cookie3: PSTM=1575168325; path="/"; domain=".baidu.com"; path_spec; domain_dot; expires="2087-12-19 05:59:31Z"; version=0
Set-Cookie3: delPer=0; path="/"; domain=".baidu.com"; path_spec; domain_dot; discard; version=0
Set-Cookie3: BDSVRTM=0; path="/"; domain="www.baidu.com"; path_spec; discard; version=0
Set-Cookie3: BD_HOME=0; path="/"; domain="www.baidu.com"; path_spec; discard; version=0
可以看到,,兩種類型產(chǎn)生的 cookie 文件格式差異還是非常大的。
已經(jīng)生成了 cookie 文件,,下一步我們就是要在請求的時候添加 cookie ,,示例代碼如下:
# 請求是使用 Mozilla 型文件
cookie = http.cookiejar.MozillaCookieJar()
cookie.load('cookies_mozilla.txt', ignore_discard=True, ignore_expires=True)
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
print(response.read().decode('utf-8'))
這里我們使用 load() 方法來讀取本地的Cookies文件,獲取到了 Cookies 的內(nèi)容,。
前提是,,我們需要提前生成了 Mozilla 格式的 cookie 文件,然后讀取 Cookies 之后使用同樣的方法構(gòu)建 Handler 和 Opener 即可,。
請求正常的時候可以相應(yīng)擺渡首頁的源碼,,結(jié)果小編也就不貼了,屬實(shí)有點(diǎn)長,。
本篇的內(nèi)容就到這里了,,希望各位同學(xué)記得自己動手寫代碼哦~~~
示例代碼
本系列的所有代碼小編都會放在代碼管理倉庫 Github 和 Gitee 上,方便大家取用,。
示例代碼-Github
示例代碼-Gitee
參考
https://www.cnblogs.com/zhangxinqi/p/9170312.html
https:///5500.html
|