这里的答案 ( Size of raw response in bytes ) 说:
Just take the
len()
of the content of the response:>>> response = requests.get('https://github.com/') >>> len(response.content) 51671
但是这样做并不能获得准确的内容长度。例如检查这个 python 代码:
import sys
import requests
def proccessUrl(url):
try:
r = requests.get(url)
print("Correct Content Length: "+r.headers['Content-Length'])
print("bytes of r.text : "+str(sys.getsizeof(r.text)))
print("bytes of r.content : "+str(sys.getsizeof(r.content)))
print("len r.text : "+str(len(r.text)))
print("len r.content : "+str(len(r.content)))
except Exception as e:
print(str(e))
#this url contains a content-length header, we will use that to see if the content length we calculate is the same.
proccessUrl("https://stackoverflow.com")
如果我们尝试手动计算内容长度并将其与 header 中的内容进行比较,我们会得到一个更大的答案?
Correct Content Length: 51504
bytes of r.text : 515142
bytes of r.content : 257623
len r.text : 257552
len r.content : 257606
为什么 len(r.content)
没有返回正确的内容长度?如果缺少表头,我们如何手动准确计算呢?
最佳答案
Content-Length
header 反射(reflect)了响应的主体。这与 text
或 content
属性的长度不同,因为响应可以被压缩。 requests
为您解压缩响应。
您必须绕过大量内部管道才能获得原始的、压缩的、原始的内容,然后如果您希望 response
对象仍能正常工作,则必须访问更多的内部结构. “最简单”的方法是启用流式传输,然后从原始套接字读取:
from io import BytesIO
r = requests.get(url, stream=True)
# read directly from the raw urllib3 connection
raw_content = r.raw.read()
content_length = len(raw_content)
# replace the internal file-object to serve the data again
r.raw._fp = BytesIO(raw_content)
演示:
>>> import requests
>>> from io import BytesIO
>>> url = "https://stackoverflow.com"
>>> r = requests.get(url, stream=True)
>>> r.headers['Content-Encoding'] # a compressed response
'gzip'
>>> r.headers['Content-Length'] # the raw response contains 52055 bytes of compressed data
'52055'
>>> r.headers['Content-Type'] # we are served UTF-8 HTML data
'text/html; charset=utf-8'
>>> raw_content = r.raw.read()
>>> len(raw_content) # the raw content body length
52055
>>> r.raw._fp = BytesIO(raw_content)
>>> len(r.content) # the decompressed binary content, byte count
258719
>>> len(r.text) # the Unicode content decoded from UTF-8, character count
258658
这会将完整的响应读入内存,所以如果您期望有大量响应,请不要使用它!在这种情况下,您可以改为使用 shutil.copyfileobj()
将数据从 r.raw
文件复制到 spooled temporary file。 (一旦达到一定大小,它将切换到磁盘文件),获取该文件的文件大小,然后将该文件填充到 r.raw._fp
。
向任何缺少该 header 的请求添加 Content-Type
header 的函数如下所示:
import requests
import shutil
import tempfile
def ensure_content_length(
url, *args, method='GET', session=None, max_size=2**20, # 1Mb
**kwargs
):
kwargs['stream'] = True
session = session or requests.Session()
r = session.request(method, url, *args, **kwargs)
if 'Content-Length' not in r.headers:
# stream content into a temporary file so we can get the real size
spool = tempfile.SpooledTemporaryFile(max_size)
shutil.copyfileobj(r.raw, spool)
r.headers['Content-Length'] = str(spool.tell())
spool.seek(0)
# replace the original socket with our temporary file
r.raw._fp.close()
r.raw._fp = spool
return r
这接受现有的 session ,并允许您指定请求方法。根据内存限制的需要调整 max_size
。 https://github.com
上的演示,缺少 Content-Length
header :
>>> r = ensure_content_length('https://github.com/')
>>> r
<Response [200]>
>>> r.headers['Content-Length']
'14490'
>>> len(r.content)
54814
请注意,如果不存在 Content-Encoding
header 或该 header 的值设置为 identity
,并且 Content-Length
可用,那么您就可以依赖 Content-Length
作为响应的完整大小。那是因为显然没有应用压缩。
作为旁注:如果您想要的是 bytes
或 str
的长度,则不应使用 sys.getsizeof()
> 对象(该对象中的字节数或字符数)。 sys.getsizeof()
为您提供 Python 对象的内部内存占用量,它不仅仅涵盖该对象中的字节数或字符数。参见 What is the difference between len() and sys.getsizeof() methods in python?
关于python - Content-length header 和手动计算的时候不一样吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50825528/