1、requests模块的响应体内容工作流的运用
API引述
默认情况下,当你进行网络请求后,响应体会立即被下载。你可以通过 stream 参数覆盖这个行为,
推迟下载响应体直到访问 Response.content 属性:
tarball_url = 'https://github.com/kennethreitz/requests/tarball/master'
r = requests.get(tarball_url, stream=True)
此时仅有响应头被下载下来了,连接保持打开状态,因此允许我们根据条件获取内容:
if int(r.headers['content-length']) < TOO_LONG:
content = r.content
...
你可以进一步使用 Response.iter_content 和 Response.iter_lines 方法来控制工作流,或者以 Response.raw 从底层 urllib3 的 urllib3.HTTPResponse <urllib3.response.HTTPResponse 读取未解码的响应体。
如果你在请求中把 stream 设为 True,Requests 无法将连接释放回连接池,除非你 消耗了所有的数据,或者调用了 Response.close。 这样会带来连接效率低下的问题。如果你发现你在使用 stream=True 的同时还在部分读取请求的 body(或者完全没有读取 body),那么你就应该考虑使用 with 语句发送请求,这样可以保证请求一定会被关闭:
with requests.get('http://httpbin.org/get', stream=True) as r:
# 在此处理响应。
评注
这种方法与过去我们直接使用requests.get(url)方法的区别在于:一般的请求上,我们都是直接将数据一次性拿回来,而使用工作流的方式,我们可以先取响应头。再对数据进行后续操作。
这让我们可以根据响应头的信息(响应内容长度、响应的形式等),决定是否需要获取数据(下载操作)
因此传统的操作,被我们分拆成如下的操作:
import requests
url = 'target_url'
response = requests.get(url,stream=True) # 此时我们已经下载了响应头和响应内容
#-------------------------------------
#-------------------------------------
import requests
url = 'target_url'
response = requests.get(url,stream=True) # 此时我们只下载了响应头
if response.headers['字段信息'] 满足 条件x:
print(response.content) # 此时才开始下载响应内容
response.close() #这一步不要遗漏
这种使用方法的运用还不仅于此,我们如果再结合API提供的函数requests.response.iter_content()对工作流进行操作,就可以实现两种目的:
- 实时下载进度的更新
- 避免目标数据过大一次性读入内存处理的问题
代码实例:
# -*- coding: utf-8 -*-
"""
Created on Mon Oct 15 14:05:06 2018
Description:
Version:
@author: HJY
"""
import time
import requests
try:
length_star = 50 # 总的星星数
start = time.time()
filename = 'python_guide.zip'
url = 'https://codeload.github.com/kennethreitz/python-guide/zip/master'
response = requests.get(url,stream=True)
print(response.headers)
content_length = int(response.headers['content-length'])
size = 0
switch = int(content_length/50)
print('[文件大小]:{:.2f}MB'.format(content_length/(1024*1024)))
with open(filename,'wb') as f:
for data in response.iter_content(chunk_size=1024):
f.write(data)
size += len(data)
star_num = size//switch
percent = size/content_length
progress = "\r[" + '>'* star_num + ' '*(length_star-star_num) +']' +'--下载进度:{:.0f}%'.format(percent * 100)
print(progress,end='')
except Exception as e:
print(e)
else:
print('\n[下载耗时]:%ss' % (time.time()-start))
finally:
response.close()
效果图:
2、requests模块的其他用法
两篇Rytis Sileika的博客:
1、Quick way to check for URL redirects
这篇博客主要讲了一个关于如何判断一个网页链接是否会引发重定向的python脚本实现。代码如下:
import sys
import requests
def check_for_redirects(url):
try:
r = requests.get(url,allow_redirects=False,
timeout=0.5)
if 300 <= r.status_code < 400:
return r.headers['location']
else:
return '[no redirect]'
except requests.exceptions.Timeout:
return '[timeout]'
except requests.exceptions.ConnectionError:
return '[connection error]'
def check_domain(urls):
for url in urls:
url_to_check = url if url.startswith('http') else "http://%s" % url
redirect_url = check_for_redirects(url_to_check)
print("%s => %s" % (url_to_check,redirect_url))
if __name__ == '__main__':
fname = 'domains.txt'
try:
fname = sys.argv[1]
except IndexError:
pass
urls = (l.strip() for l in open(fname).readlines())
check_domain(urls)
domains.txt文件
google.com
http://gmail.com
www.ibm.com
redhat.com
example.com
i-believe-this-domain-does-not-exist-123abc.com
显示结果

实现思路
- 用requests模块发起GET请求,利用get方法的
allow_redirect属性阻止重定向 - 利用状态码判断
- 利用响应头中的
location属性获取重定向链接
第一点
关于如何知道allow_redirect这个属性,查看Requests API并没看到有关于这个属性的用法,因而只能查看源码。
查看模块requests模块文档路径
import requests
help(requets)
查看requests.get的源代码路径:
import requests
help(requests.get)
显示结果:
Help on function get in module requests.api:
故而在requests包中的api.py文件.
def get(url, params=None, **kwargs):
"""Sends a GET request.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`.
:param \*\*kwargs: Optional arguments that ``request`` takes.
:return: :class:`Response <Response>` object
:rtype: requests.Response
"""
其中**kwargs参数:
Optional arguments that ``request`` takes
转而查看文件中的request函数,可以看到参数说明:
:param allow_redirects: (optional) Boolean.
Set to True if POST/PUT/DELETE redirect following is allowed.
这个参数设置是否允许重定向。同时我们也可以学习到还有很多文档中没有指出的参数。
第二点
查阅响应码:
300——请求的资源可在多处得到
301——删除请求数据
302——在其他地址发现了请求数据
303——建议客户访问其他URL或访问方式
304——客户端已经执行了GET,但文件未变化
305——请求的资源必须从服务器指定的地址得到
306——前一版本HTTP中使用的代码,现行版本中不再使用
307——申明请求的资源临时性删除
可以看出这个3xx的响应码基本与重定向有关
2、How to check if a web page exists
这篇博客主要讲了如何判断网页是否存在,代码上没什么稀奇,实际上我们会说直接用网络状态码是200还是404就可以了,但这提供的信息并不详细,作者的方式让我注意到HEAD请求的存在。博主使用python 2.7的httlplib包实现,在这个基础上我补充了3.x版本的httplib包的实现以及Requests模块的简单实现。
# -*- coding: utf-8 -*-
"""
Created on Wed Jan 16 16:00:48 2019
@author: HJY
"""
#python2.7 version
#----------------------------------
import httplib
conn = httplib.HTTPConnection('www.w3.org')
conn.request('HEAD','/Protocols/rfc2616/rfc2616-sec9.html')
resp = conn.getresponse()
data = resp.read()
# as you can see, there is no data,
# this is because we've used HEAD method
for h in resp.getheaders():
print h
print resp.status
#-----------------------------------
# python 3.x http.client
import http.client
conn = http.client.HTTPConnection('www.w3.org')
conn.request('HEAD',"/Protocols/rfc2616/rfc2616-sec9.html")
resp = conn.getresponse()
data = resp.read()
for h in resp.getheaders():
print(h)
print(resp.status)
#-----------------------------------
# Python 3.x Requests
import requests
url = 'http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html'
r = requests.head(url)
print(r.content)
print(r.headers)
print(r.status_code)
以往我使用Request模块只注意到有.get()、.post()方法,全然不知有.head方法,在只想要响应头时,使用.head而不用.get获取全部,我想这其中是有效率和内存之分的,关于这点暂没深究。
另外也让我学习了下httplib模块的API,这应该也是一种收获吧。
本文详细介绍了Python Requests模块的响应体内容工作流及其优势,包括如何控制响应下载和利用工作流实现实时下载进度更新及避免大文件内存问题。此外,还探讨了Requests模块的其他用法,如检查URL重定向和验证网页存在,提供了相关代码示例和HTTP状态码的知识。


1084

被折叠的 条评论
为什么被折叠?



