Python爬蟲系列(三):requests高級(jí)耍法

昨天牍疏,我們更多的討論了request的基礎(chǔ)API,讓我們對(duì)它有了基礎(chǔ)的認(rèn)知拨齐。學(xué)會(huì)上一課程鳞陨,我們已經(jīng)能寫點(diǎn)基本的爬蟲了。但是還不夠瞻惋,因?yàn)橄寐耍芏嗾军c(diǎn)是需要登錄的,在站點(diǎn)的各個(gè)請(qǐng)求之間歼狼,是需要保持回話狀態(tài)的掏导,有的站點(diǎn)還需要證書驗(yàn)證,等等這一系列的問題羽峰,我們將在今天這一環(huán)節(jié)趟咆,加以討論。

1.會(huì)話對(duì)象

會(huì)話:session梅屉,就是你點(diǎn)進(jìn)這個(gè)站點(diǎn)后值纱,由瀏覽器與服務(wù)器之間保持的一次連接。這次連接里面坯汤,你跳轉(zhuǎn)頁面虐唠,或發(fā)起其他請(qǐng)求,服務(wù)器要求某些數(shù)據(jù)驗(yàn)證惰聂。服務(wù)器不會(huì)叫你在每次跳轉(zhuǎn)時(shí)候進(jìn)行驗(yàn)證凿滤,而是用已驗(yàn)證的結(jié)果進(jìn)行跳轉(zhuǎn)。這樣就節(jié)省服務(wù)器資源庶近,底層的TCP連接也會(huì)被重用翁脆。

跨請(qǐng)求保持?jǐn)?shù)據(jù)(客戶端存數(shù)據(jù)):

s = requests.Session()

s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')

r = s.get("http://httpbin.org/cookies")

print(r.text)

回話提供請(qǐng)求默認(rèn)數(shù)據(jù)(數(shù)據(jù)會(huì)被存到服務(wù)端):

s = requests.Session()

s.auth = ('user', 'pass')

s.headers.update({'x-test': 'true'})

# 'x-test' 和 'x-test2' 一起發(fā)送給url

s.get('http://httpbin.org/headers', headers={'x-test2': 'true'})

注意:即便使用了session,方法級(jí)別的參數(shù)鼻种,仍然不會(huì)再跨請(qǐng)求保持反番。

以下代碼,另個(gè)請(qǐng)求分別有自己的cookies

s = requests.Session()

r = s.get('http://httpbin.org/cookies')

print(r.text)

# '{"cookies": {}}'

r = s.get('http://httpbin.org/cookies', cookies={'from-my': 'browser'})

print(r.text)

# '{"cookies": {"from-my": "browser"}}'

會(huì)話上下文管理器:是指用with 塊限定會(huì)話對(duì)象的使用范圍

with requests.Session() as s:

s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')

2.請(qǐng)求與響應(yīng)對(duì)象

任何時(shí)候叉钥,我們往服務(wù)器發(fā)消息罢缸,都會(huì)返回一個(gè)response的響應(yīng)對(duì)象,同時(shí)投队,還能獲得我們自己創(chuàng)建的request對(duì)象

r = requests.get('http://en.wikipedia.org/wiki/Monty_Python')

print (r.headers)

print (r.request.headers)

在發(fā)送請(qǐng)求之前枫疆,需要對(duì)body獲header做一些額外處理,使用如下方法:

from requests import Request, Session

s = Session()

req = Request('GET', url,

data=data,

headers=header

)

#要想獲取有狀態(tài)的請(qǐng)求敷鸦,使用:

prepped = s.prepare_request(req)

而不是使用:

prepped = req.prepare()

# do something with prepped.body

# do something with prepped.headers

resp = s.send(prepped,

stream=stream,

verify=verify,

proxies=proxies,

cert=cert,

timeout=timeout

)

print(resp.status_code)

3.SSL 證書驗(yàn)證

requests.get('https://requestb.in')

requests.get('https://github.com', verify=True)

requests.get('https://github.com', verify='/path/to/certfile')

# 將驗(yàn)證路徑保持在會(huì)話中

s = requests.Session()

s.verify = '/path/to/certfile'

# 忽略對(duì)SSL的驗(yàn)證

requests.get('https://kennethreitz.org', verify=False)

4.客戶端證書

requests.get('https://kennethreitz.org',cert=('/path/client.cert','/path/client.key'))

#證書在會(huì)話中

s = requests.Session()

s.cert = '/path/client.cert'

注意:本地證書的私有 key 必須是解密狀態(tài)亮蛔。目前,Requests 不支持使用加密的 key恢筝。

這里補(bǔ)充一句:當(dāng)?shù)卿?2306時(shí)喊你要安裝證書痊远。當(dāng)向有證書驗(yàn)證要求的站點(diǎn),就使用上面的代碼

http://5.CA證書 科普:

Requests 默認(rèn)附帶了一套它信任的根證書,來自于Mozilla trust store。然而它們?cè)诿看?Requests 更新時(shí)才會(huì)更新。這意味著如果你固定使用某一版本的 Requests颇蜡,你的證書有可能已經(jīng) 太舊了。

從 Requests 2.4.0 版之后辆亏,如果系統(tǒng)中裝了certifi包风秤,Requests 會(huì)試圖使用它里邊的 證書。這樣用戶就可以在不修改代碼的情況下更新他們的可信任證書扮叨。

6.響應(yīng)體內(nèi)容工作流

import requests

# 默認(rèn)情況下唁情,發(fā)起請(qǐng)求會(huì)同時(shí)下載響應(yīng)頭和響應(yīng)體(就是響應(yīng)內(nèi)容)

tarball_url = 'https://github.com/kennethreitz/requests/tarball/master'

# 如果將stream=True 則會(huì)推遲響應(yīng)內(nèi)容的下載

r = requests.get(tarball_url, stream=True)

# 這里就是:滿足某種條件才去下載

if int(r.headers['content-length']) < TOO_LONG:

content = r.content

# 在請(qǐng)求中把 stream 設(shè)為 True,Requests 無法將連接釋放回連接池甫匹,

# 除非消耗了所有的數(shù)據(jù)甸鸟,或者調(diào)用了 Response.close。

# 這樣會(huì)帶來連接效率低下的問題兵迅。如果在使用 stream=True 的同時(shí)還在部分讀取請(qǐng)求的 body(或者完全沒有讀取 body)抢韭,

# 那么就應(yīng)該使用 with 語句發(fā)送請(qǐng)求,這樣可以保證請(qǐng)求一定會(huì)被關(guān)閉

with requests.get('http://httpbin.org/get', stream=True) as r:

# 這里處理響應(yīng)

content = r.content

7.保持活動(dòng)狀態(tài)(持久連接)科普

同一會(huì)話內(nèi)的持久連接是完全自動(dòng)處理的恍箭,同一會(huì)話內(nèi)你發(fā)出的任何請(qǐng)求都會(huì)自動(dòng)復(fù)用恰當(dāng)?shù)倪B接刻恭。

注意:只有所有的響應(yīng)體數(shù)據(jù)被讀取完畢連接才會(huì)被釋放回連接池;所以確保將 stream 設(shè)置為 False 或讀取 Response 對(duì)象的 content 屬性扯夭。

8.流式上傳

Requests支持流式上傳鳍贾,這允許發(fā)送大的數(shù)據(jù)流或文件,而無需先把它們讀入內(nèi)存交洗。要使用流式上傳骑科,需,為請(qǐng)求體构拳,提供一個(gè)類文件對(duì)象即可:

with open('massive-body') as f:

requests.post('http://some.url/streamed', data=f)

注意的問題:

最好使用二進(jìn)制模式打開文件咆爽。這是因?yàn)?requests 有默認(rèn)設(shè)置 header 中的 Content-Length,在這種情況下該值會(huì)被設(shè)為文件的字節(jié)數(shù)置森。如果用文本模式打開文件斗埂,就可能碰到錯(cuò)誤

9.塊編碼請(qǐng)求

對(duì)于出去和進(jìn)來的請(qǐng)求,Requests 也支持分塊傳輸編碼凫海。要發(fā)送一個(gè)塊編碼的請(qǐng)求呛凶,僅需為請(qǐng)求體提供一個(gè)生成器(或任意沒有具體長度的迭代器)

def gen():

yield 'hi'

yield 'there'

requests.post('http://some.url/chunked', data=gen())

注意:

對(duì)于分塊的編碼請(qǐng)求,最好使用 Response.iter_content() 對(duì)其數(shù)據(jù)進(jìn)行迭代行贪。在理想情況下漾稀,request 會(huì)設(shè)置 stream=True模闲,這樣就可以通過調(diào)用 iter_content 并將分塊大小參數(shù)設(shè)為 none,從而進(jìn)行分塊的迭代县好。如果要設(shè)置分塊的最大體積,可以把分塊大小參數(shù)設(shè)為任意整數(shù)暖混。

說白了缕贡,就是段點(diǎn)續(xù)傳

妹的,報(bào)了一堆錯(cuò)拣播。先記錄在這晾咪,后面遇到了再研究

10.POST 多個(gè)分塊編碼的文件

上傳圖片

import requests

url = 'http://httpbin.org/post'

multiple_files = [

('images', ('foo.png', open('foo.png', 'rb'), 'image/png')),

('images', ('bar.png', open('bar.png', 'rb'), 'image/png'))]

r = requests.post(url, files=multiple_files)

print (r.text)

11.事件掛鉤

import requests

def print_url(r, *args, **kwargs):

print(r.url)

# Requests有一個(gè)鉤子系統(tǒng),你可以用來操控部分請(qǐng)求過程贮配,或信號(hào)事件處理谍倦。

# 在產(chǎn)生響應(yīng)之前調(diào)用print_url 就是server響應(yīng)之前的回調(diào)

hooks = dict(response=print_url)

r = requests.get('http://httpbin.org', hooks=dict(response=print_url))

print (r.text)

也沒看有什么用

12.自定義身份驗(yàn)證

import requests

from requests.auth import AuthBase

class PizzaAuth(AuthBase):

def __init__(self, username):

# setup any auth-related data here

self.username = username

def __call__(self, r):

# modify and return the request

r.headers['X-Pizza'] = self.username

return r

#注意:auth參數(shù)必須是一個(gè)可調(diào)用對(duì)象 實(shí)現(xiàn)了 __call__ 方法

requests.get('http://pizzabin.org/admin', auth=PizzaAuth('kenneth'))

作用就是在請(qǐng)求發(fā)出之前,有機(jī)會(huì)修改請(qǐng)求

13.流式請(qǐng)求

import requests

r = requests.get('http://httpbin.org/stream/20', stream=True)

for line in r.iter_lines():

# filter out keep-alive new lines

if line:

decoded_line = line.decode('utf-8')

print(json.loads(decoded_line))

就是將請(qǐng)求參數(shù)設(shè)置stream=True

import requests

r = requests.get('http://httpbin.org/stream/20', stream=True)

#當(dāng)使用 decode_unicode=True 在Response.iter_lines()Response.iter_content()中#時(shí)泪勒,需要提供一個(gè)編碼方式昼蛀,以防服務(wù)器沒有提供默認(rèn)回退編碼,從而導(dǎo)致錯(cuò)誤

if r.encoding is None:

r.encoding = 'utf-8'#設(shè)置編碼方式

for line in r.iter_lines(decode_unicode=True):

if line:

print(json.loads(line))

#注意:iter_lines不保證重進(jìn)入時(shí)的安全性圆存。多次調(diào)用該方法 會(huì)導(dǎo)致部分收到的數(shù)據(jù)丟失叼旋。#如果要在多處調(diào)用它,就應(yīng)該使用生成的迭代器對(duì)象:

lines = r.iter_lines()

# 保存第一行以供后面使用沦辙,或者直接跳過

first_line = next(lines)

forlineinlines:

print(line)

不曉得各位看懂什么是流式請(qǐng)求沒夫植。指的不是請(qǐng)求是流,而是請(qǐng)求返回的數(shù)據(jù)流油讯。返回一點(diǎn)即取一點(diǎn)详民。而不是普通的是返回完成后在取內(nèi)容

14.代理

import requests

proxies = {

"http": "http://10.10.1.10:3128",

"https": "http://10.10.1.10:1080",

}

requests.get("http://example.org", proxies=proxies)

代理:就是你訪問一個(gè)網(wǎng)站,其實(shí)并不是你直接訪問的陌兑,而是你發(fā)請(qǐng)求給A機(jī)器沈跨,A機(jī)器取請(qǐng)求B機(jī)器。B返回給A兔综,A再返回給你谒出。代理就是中間人的意思。為什么需要代理邻奠?因?yàn)椋悍磁老x網(wǎng)站一般使用IP來識(shí)別一個(gè)機(jī)器笤喳。老是一個(gè)IP再不停訪問網(wǎng)站,該網(wǎng)站就會(huì)把這個(gè)IP拉入黑名單碌宴,不允許訪問杀狡。這時(shí),就需要很多IP再擾亂反爬蟲工具的思維贰镣,避免封IP呜象。

15.SOCKS

除了基本的 HTTP 代理膳凝,Request 還支持 SOCKS 協(xié)議的代理。這是一個(gè)可選功能恭陡,若要使用蹬音, 你需要安裝第三方庫。

在后面的項(xiàng)目實(shí)戰(zhàn)中休玩,我們將會(huì)使用

16.HTTP動(dòng)詞

GET著淆、OPTIONS、HEAD拴疤、POST永部、PUT、PATCH呐矾、DELETE

GET POST最常用

PUT苔埋,DELETE在調(diào)用rest的接口時(shí),也會(huì)用到

17.定制動(dòng)詞

有些服務(wù)器不接受GET POST等蜒犯,要求用自定義的组橄,就使用如下方法

r = requests.request('MKCOL',url,data=data)

r.status_code

18.響應(yīng)頭鏈接字段

許多 HTTP API 都有響應(yīng)頭鏈接字段的特性,它們使得 API 能夠更好地自我描述和自我顯露罚随。

比如在使用分頁的情況下最有用

url = 'https://api.github.com/users/kennethreitz/repos?page=1&per_page=10'

r = requests.head(url=url)

print(r.headers['link'])

print(r.links["next"])

19.阻塞和非阻塞

使用默認(rèn)的傳輸適配器晨炕,Requests 不提供任何形式的非阻塞 IO。Response.content屬性會(huì)阻塞毫炉,直到整個(gè)響應(yīng)下載完成瓮栗。如果你需要更多精細(xì)控制,該庫的數(shù)據(jù)流功能(見流式請(qǐng)求) 允許你每次接受少量的一部分響應(yīng)瞄勾,不過這些調(diào)用依然是阻塞式的费奸。

如果你對(duì)于阻塞式 IO 有所顧慮,還有很多項(xiàng)目可以供你使用进陡,它們結(jié)合了 Requests 和 Python 的某個(gè)異步框架愿阐。典型的優(yōu)秀例子是grequestsrequests-futures

20.Header 排序

在某些特殊情況下你也許需要按照次序來提供 header趾疚,如果你向 headers 關(guān)鍵字參數(shù)傳入一個(gè)OrderedDict缨历,就可以向提供一個(gè)帶排序的 header。然而糙麦,Requests 使用的默認(rèn) header 的次序會(huì)被優(yōu)先選擇辛孵,這意味著如果你在 headers 關(guān)鍵字參數(shù)中覆蓋了默認(rèn) header,和關(guān)鍵字參數(shù)中別的 header 相比赡磅,它們也許看上去會(huì)是次序錯(cuò)誤的魄缚。

如果這個(gè)對(duì)你來說是個(gè)問題,那么用戶應(yīng)該考慮在Session對(duì)象上面設(shè)置默認(rèn) header,只要將Session設(shè)為一個(gè)定制的 OrderedDict 即可冶匹。這樣就會(huì)讓它成為優(yōu)選的次序习劫。

以上內(nèi)容,是request的高級(jí)耍法嚼隘,也不算好高級(jí)诽里。接下來的一周,我們將著重討論如何解析網(wǎng)頁飞蛹。當(dāng)然谤狡,這一部分內(nèi)容不會(huì)包含JS生成的HTML。這個(gè)我們將會(huì)在項(xiàng)目實(shí)戰(zhàn)環(huán)節(jié)再討論桩皿。

最近培訓(xùn)新人豌汇,搞得很晚才下班幢炸。精力不夠哇泄隔。若是喜歡我的系列文章,還請(qǐng)帥哥美女點(diǎn)個(gè)贊宛徊,足矣

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末佛嬉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子闸天,更是在濱河造成了極大的恐慌暖呕,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件苞氮,死亡現(xiàn)場離奇詭異湾揽,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)笼吟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門库物,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人贷帮,你說我怎么就攤上這事戚揭。” “怎么了撵枢?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵民晒,是天一觀的道長。 經(jīng)常有香客問我锄禽,道長潜必,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任沃但,我火速辦了婚禮刮便,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘绽慈。我一直安慰自己恨旱,他們只是感情好辈毯,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著搜贤,像睡著了一般谆沃。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上仪芒,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天唁影,我揣著相機(jī)與錄音,去河邊找鬼掂名。 笑死据沈,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的饺蔑。 我是一名探鬼主播锌介,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼猾警!你這毒婦竟也來了孔祸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤发皿,失蹤者是張志新(化名)和其女友劉穎崔慧,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體穴墅,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡惶室,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了玄货。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片皇钞。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖誉结,靈堂內(nèi)的尸體忽然破棺而出鹅士,到底是詐尸還是另有隱情,我是刑警寧澤惩坑,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布掉盅,位于F島的核電站,受9級(jí)特大地震影響以舒,放射性物質(zhì)發(fā)生泄漏趾痘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一蔓钟、第九天 我趴在偏房一處隱蔽的房頂上張望永票。 院中可真熱鬧,春花似錦、人聲如沸侣集。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽世分。三九已至编振,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間臭埋,已是汗流浹背踪央。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瓢阴,地道東北人畅蹂。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像荣恐,于是被迫代替她去往敵國和親液斜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容