Заголовок Content-Length
отражает тело ответа.Это не то же самое, что длина атрибутов 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
во временный буферный временный файл (который переключится на файл на диске при достижении определенного размера),получить размер файла этого файла, а затем вставить этот файл в r.raw._fp
.
Функция, которая добавляет заголовок Content-Type
к любому запросу, в котором этот заголовок отсутствует, будет выглядеть так:
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
Принимает существующий сеанс и позволяет также указать метод запроса.Отрегулируйте max_size
по мере необходимости для ваших ограничений памяти.Демонстрация по https://github.com
, в которой отсутствует заголовок Content-Length
:
>>> r = ensure_content_length('https://github.com/')
>>> r
<Response [200]>
>>> r.headers['Content-Length']
'14490'
>>> len(r.content)
54814
Обратите внимание, что если отсутствует заголовок Content-Encoding
или значение для этого заголовка установлено на identity
, и Content-Length
доступно, тогда только вы можете положиться на Content-Length
, являющийся полным размером ответа.Это потому, что тогда, очевидно, сжатие не применяется.
В качестве примечания: вы не должны использовать sys.getsizeof()
, если то, что вы ищете, это длина объекта bytes
или str
(числобайты или символы в этом объекте).sys.getsizeof()
дает вам объем внутренней памяти объекта Python, который охватывает не только количество байтов или символов в этом объекте.См. В чем разница между методами len () и sys.getsizeof () в python?