[Python]requests模塊:HTTP請求時的回調(diào)函數(shù)

前排提醒变勇!

本文并非是“零起點(diǎn)”的憨攒。閱讀下列文檔之前,請保證你具有以下的知識:

  • 了解requests和grequests模塊待笑,并能夠使用其建立HTTP請求;
  • 了解回調(diào)函數(shù)的含義抓谴。

背景

進(jìn)行爬蟲操作時暮蹂,程序員除了想要獲取請求的結(jié)果之外,有時還想在請求過程中獲取相關(guān)的信息(比如說在某個時刻已請求數(shù)據(jù)的長度)齐邦,以完成某些任務(wù)(比如說下載進(jìn)度條)椎侠。我們知道Python內(nèi)置模塊urllib下的方法urllib.request.urlretrieve可以添加回調(diào)函數(shù)參數(shù)callback,但是在另外一個常用模塊requests中措拇,我們難以找到直接的回調(diào)函數(shù)參數(shù)我纪,在國內(nèi)互聯(lián)網(wǎng)中也難以找到相關(guān)信息,但這不代表這個模塊完不成這個任務(wù)丐吓。在此我查閱了一些資料和問答頻道浅悉,并寫成此文,以供參考券犁。

研究

以下我們以添加GET請求的進(jìn)度條的方式來展示改進(jìn)方法术健。

使用的目標(biāo)URL:http://wppkg.baidupcs.com/issue/netdisk/yunguanjia/BaiduNetdisk_6.8.9.1.exe(百度網(wǎng)盤Windows端安裝包,大小約30MB粘衬,下載需時約10s)荞估。

requests沒有直接的callback參數(shù)(這一點(diǎn)待定),但是其具有一個參數(shù)“流”(stream)允許我們以流的形式進(jìn)行請求稚新。以流形式請求時勘伺,我們可以“一塊”(chunk)“一塊”地獲取請求的內(nèi)容,請求對象Response在此時不會再全部內(nèi)容完全被獲取時才能返回褂删;并且Response支持生成器iter_content的方式來返回請求的內(nèi)容飞醉。由以上二點(diǎn),我們可以構(gòu)造一個新的請求函數(shù)屯阀,來做出回調(diào)函數(shù)的效果:

注:本方法源自Stack Overflow某條回答缅帘,源地址遺失轴术。

def get_callback(url, data=None, chunk_size=1024, callback=None):
    r = requests.get(url, data=data, stream=True) # Response請求,由于添加了stream钦无,請求速度不會慢
    length = r.headers.get('content-length') # 請求的總長度逗栽,便于回調(diào)
    if length == None: # 有的請求不帶上面的頭參數(shù),故要特判
        return r.content # 直接返回
    if not callback: # 如果回調(diào)函數(shù)沒有給定
        callback = lambda now, tot: None # 一個無用的函數(shù)铃诬,占位用祭陷,其中callback需要接受兩個參數(shù),分別對應(yīng)已獲取長度和總長度趣席,后同
    dl = 0 # 目前已獲取的長度
    length = int(length) # headers里取得的值都是字符串兵志,要化成整數(shù)
    res = b'' # 目前已獲取的請求內(nèi)容
    for chunk in r.iter_content(chunk_size=chunk_size): # 每次取出一塊,注意chunk_size在requests中默認(rèn)為1宣肚,這里調(diào)大為1024想罕,節(jié)約for循環(huán)的成本
        dl += len(chunk)
        res = b''.join([res, chunk]) # 這里沒有直接加,速度更快
        callback(dl, length) # 執(zhí)行回調(diào)
    return res # 最終給出請求的內(nèi)容

測試結(jié)果如下:

In [63]: res = get_callback(url, None, 1048576, callback)
1048576 35984960
2097152 35984960
3145728 35984960
4194304 35984960
5242880 35984960
6291456 35984960
7340032 35984960
8388608 35984960
9437184 35984960
10485760 35984960
11534336 35984960
12582912 35984960
13631488 35984960
14680064 35984960
15728640 35984960
16777216 35984960
17825792 35984960
18874368 35984960
19922944 35984960
20971520 35984960
22020096 35984960
23068672 35984960
24117248 35984960
25165824 35984960
26214400 35984960
27262976 35984960
28311552 35984960
29360128 35984960
30408704 35984960
31457280 35984960
32505856 35984960
33554432 35984960
34603008 35984960
35651584 35984960
35984960 35984960

In [64]: len(res)
Out[64]: 35984960

如果我們需要時刻對某個變量賦值霉涨,而不是print按价,我們可以在callback中添加global語句,使回調(diào)函數(shù)可以修改外部全局變量笙瑟。

但如果是第三方進(jìn)度條模塊(比如說progressbar)楼镐,則可能需要對源代碼改進(jìn)較多,原因是進(jìn)度條的實(shí)現(xiàn)需要將iter_content視作某個函數(shù)的自變量往枷,并且進(jìn)度條模塊通常對生成器不友好(原因是生成器迭代次數(shù)無法確定框产,也就無法判斷最大循環(huán)次數(shù)。但是我們先前已經(jīng)得到了chunk_sizelength兩個變量错洁,而整個過程不包括其他的循環(huán)秉宿,故我們可以計算得出總共的循環(huán)次數(shù):

max_value = int(math.ceil(length / chunk_size))

以此我們得出回調(diào)的改進(jìn)版:

def get_callback(url, data=None, chunk_size=1024, callback=None):
    r = requests.get(url, data=data, stream=True)
    length = r.headers.get('content-length')
    if length == None:
        return r.content
    if not callback:
        callback = lambda now, tot: None
    dl = 0
    length = int(length)
    res = b''
    max_value = int(math.ceil(length / chunk_size))
    bar = progressbar.ProgressBar(max_value=max_value) # 設(shè)定進(jìn)度條的最大循環(huán)次數(shù)
    for chunk in bar(r.iter_content(chunk_size=chunk_size)):
        dl += len(chunk)
        res = b''.join([res, chunk])
        callback(dl, length)
    return res

由于進(jìn)度條并未占用callback參數(shù),我們依然可以另添一個回調(diào)函數(shù)完成其他任務(wù)屯碴。




全文完描睦。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市导而,隨后出現(xiàn)的幾起案子忱叭,更是在濱河造成了極大的恐慌,老刑警劉巖今艺,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件窑多,死亡現(xiàn)場離奇詭異,居然都是意外死亡洼滚,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進(jìn)店門技潘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來遥巴,“玉大人千康,你說我怎么就攤上這事〔” “怎么了拾弃?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長摆霉。 經(jīng)常有香客問我豪椿,道長,這世上最難降的妖魔是什么携栋? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任搭盾,我火速辦了婚禮,結(jié)果婚禮上婉支,老公的妹妹穿的比我還像新娘鸯隅。我一直安慰自己,他們只是感情好向挖,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布蝌以。 她就那樣靜靜地躺著,像睡著了一般何之。 火紅的嫁衣襯著肌膚如雪跟畅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天溶推,我揣著相機(jī)與錄音徊件,去河邊找鬼。 笑死悼潭,一個胖子當(dāng)著我的面吹牛庇忌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播舰褪,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼皆疹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了占拍?” 一聲冷哼從身側(cè)響起略就,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎晃酒,沒想到半個月后表牢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贝次,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年崔兴,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡敲茄,死狀恐怖位谋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情堰燎,我是刑警寧澤掏父,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站秆剪,受9級特大地震影響赊淑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜仅讽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一陶缺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧何什,春花似錦组哩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至罐栈,卻和暖如春黍衙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背荠诬。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工琅翻, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人柑贞。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓方椎,卻偏偏與公主長得像,于是被迫代替她去往敵國和親钧嘶。 傳聞我的和親對象是個殘疾皇子棠众,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評論 2 348

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