Skip to main content
 首页 » 编程设计

python之Content-length header 和手动计算的时候不一样吗

2025年05月04日16虾米姐

这里的答案 ( 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?