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

前排提醒!

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

了解requests和grequests模块,并能够使用其建立HTTP请求;

了解回调函数的含义。

背景

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

研究

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

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_size和length两个变量,而整个过程不包括其他的循环,故我们可以计算得出总共的循环次数:

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参数,我们依然可以另添一个回调函数完成其他任务。

全文完。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值