[Python]requests模块:HTTP请求时的回调函数

前排提醒!

本文并非是“零起点”的。阅读下列文档之前,请保证你具有以下的知识:

  • 了解requests和grequests模块,并能够使用其建立HTTP请求;
  • 了解回调函数的含义。

背景

进行爬虫操作时,程序员除了想要获取请求的结果之外,有时还想在请求过程中获取相关的信息(比如说在某个时刻已请求数据的长度),以完成某些任务(比如说下载进度条)。我们知道Python内置模块urllib下的方法urllib.request.urlretrieve可以添加回调函数参数callback,但是在另外一个常用模块requests中,我们难以找到直接的回调函数参数,在国内互联网中也难以找到相关信息,但这不代表这个模块完不成这个任务。在此我查阅了一些资料和问答频道,并写成此文,以供参考。

研究

以下我们以添加GET请求的进度条的方式来展示改进方法。

使用的目标URL:http://wppkg.baidupcs.com/issue/netdisk/yunguanjia/BaiduNetdisk_6.8.9.1.exe(百度网盘Windows端安装包,大小约30MB,下载需时约10s)。

requests没有直接的callback参数(这一点待定),但是其具有一个参数“流”(stream)允许我们以流的形式进行请求。以流形式请求时,我们可以“一块”(chunk)“一块”地获取请求的内容,请求对象Response在此时不会再全部内容完全被获取时才能返回;并且Response支持生成器iter_content的方式来返回请求的内容。由以上二点,我们可以构造一个新的请求函数,来做出回调函数的效果:

注:本方法源自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') # 请求的总长度,便于回调
    if length == None: # 有的请求不带上面的头参数,故要特判
        return r.content # 直接返回
    if not callback: # 如果回调函数没有给定
        callback = lambda now, tot: None # 一个无用的函数,占位用,其中callback需要接受两个参数,分别对应已获取长度和总长度,后同
    dl = 0 # 目前已获取的长度
    length = int(length) # headers里取得的值都是字符串,要化成整数
    res = b'' # 目前已获取的请求内容
    for chunk in r.iter_content(chunk_size=chunk_size): # 每次取出一块,注意chunk_size在requests中默认为1,这里调大为1024,节约for循环的成本
        dl += len(chunk)
        res = b''.join([res, chunk]) # 这里没有直接加,速度更快
        callback(dl, length) # 执行回调
    return res # 最终给出请求的内容

测试结果如下:

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语句,使回调函数可以修改外部全局变量。

但如果是第三方进度条模块(比如说progressbar),则可能需要对源代码改进较多,原因是进度条的实现需要将iter_content视作某个函数的自变量,并且进度条模块通常对生成器不友好(原因是生成器迭代次数无法确定,也就无法判断最大循环次数。但是我们先前已经得到了chunk_sizelength两个变量,而整个过程不包括其他的循环,故我们可以计算得出总共的循环次数:

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

以此我们得出回调的改进版:

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) # 设定进度条的最大循环次数
    for chunk in bar(r.iter_content(chunk_size=chunk_size)):
        dl += len(chunk)
        res = b''.join([res, chunk])
        callback(dl, length)
    return res

由于进度条并未占用callback参数,我们依然可以另添一个回调函数完成其他任务。




全文完。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容