python - 使用分块传输编码和 gzip 压缩的网页的未压缩大小

标签 python http gzip transfer-encoding

我正在编写一个应用程序来计算我们在网页上使用 gzip 后节省的费用。当用户输入使用 gzip 的网页的 URL 时,应用程序应该吐出由于 gzip 而节省​​的大小。

我该如何解决这个问题?

这是我在页面上获取的 GET 请求 header :

{
    'X-Powered-By': 'PHP/5.5.9-1ubuntu4.19',
    'Transfer-Encoding': 'chunked',
    'Content-Encoding': 'gzip',
    'Vary': 'Accept-Encoding', 
    'Server': 'nginx/1.4.6 (Ubuntu)',
    'Connection': 'keep-alive',
    'Date': 'Thu, 10 Nov 2016 09:49:58 GMT',
    'Content-Type': 'text/html'
}

我正在使用 requests 检索页面:

r  = requests.get(url, headers)
data = r.text
print "Webpage size : " , len(data)/1024

最佳答案

如果您已经下载了 URL(使用 requests GET 请求而不使用 stream 选项,那么您已经拥有两种尺寸,因为整个响应被下载并解压,原始长度在 header 中可用:

from __future__ import division

r = requests.get(url, headers=headers)
compressed_length = int(r.headers['content-length'])
decompressed_length = len(r.content)

ratio = compressed_length / decompressed_length

可以Accept-Encoding: identity HEAD 请求内容长度 header 与设置为 Accept-Encoding: gzip 的 header 进行比较:

no_gzip = {'Accept-Encoding': 'identity'}
no_gzip.update(headers)
uncompressed_length = int(requests.get(url, headers=no_gzip).headers['content-length'])
force_gzip = {'Accept-Encoding': 'gzip'}
force_gzip.update(headers)
compressed_length = int(requests.get(url, headers=force_gzip).headers['content-length'])

但是,这可能不适用于所有服务器,因为在这种情况下动态生成的内容服务器通常会使用 Content-Length header 以避免必须先呈现内容。

如果您请求 chunked transfer encoding资源, 不会有内容长度 header ,在这种情况下,HEAD 请求可能会也可能不会为您提供正确的信息。

在那种情况下,您必须流式传输整个响应并从流的末尾提取解压缩的大小(GZIP 格式将其作为小端 4 字节无符号整数包含在最后)。使用 stream() method在原始 urllib3 响应对象上:

import requests
from collections import deque

if hasattr(int, 'from_bytes'):
    # Python 3.2 and up
    _extract_size = lambda q: int.from_bytes(bytes(q), 'little')
else:
    import struct
    _le_int = struct.Struct('<I').unpack
    _extract_size = lambda q: _le_int(b''.join(q))[0]

def get_content_lengths(url, headers=None, chunk_size=2048):
    """Return the compressed and uncompressed lengths for a given URL

    Works for all resources accessible by GET, regardless of transfer-encoding
    and discrepancies between HEAD and GET responses. This does have
    to download the full request (streamed) to determine sizes.

    """
    only_gzip = {'Accept-Encoding': 'gzip'}
    only_gzip.update(headers or {})
    # Set `stream=True` to ensure we can access the original stream:
    r = requests.get(url, headers=only_gzip, stream=True)
    r.raise_for_status()
    if r.headers.get('Content-Encoding') != 'gzip':
        raise ValueError('Response not gzip-compressed')
    # we only need the very last 4 bytes of the data stream
    last_data = deque(maxlen=4)
    compressed_length = 0
    # stream directly from the urllib3 response so we can ensure the
    # data is not decompressed as we iterate
    for chunk in r.raw.stream(chunk_size, decode_content=False):
        compressed_length += len(chunk)
        last_data.extend(chunk)
    if compressed_length < 4:
        raise ValueError('Not enough data loaded to determine uncompressed size')
    return compressed_length, _extract_size(last_data)

演示:

>>> compressed_length, decompressed_length = get_content_lengths('http://httpbin.org/gzip')
>>> compressed_length
179
>>> decompressed_length
226
>>> compressed_length / decompressed_length
0.7920353982300885

关于python - 使用分块传输编码和 gzip 压缩的网页的未压缩大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40522871/

相关文章:

python - 仅从数据框中返回空白或零值

python - 在两个应用程序之间使用 url_for

perl - 我可以在 WWW::Mechanize 的 POST 中将空格编码为 %20 吗?

c - 如何在我的 gstreamer 中启用 httpsrc 插件?

python - 通过 Python 脚本启动/停止 Apache

python - 随机执行一个函数

Http 协议(protocol)机制和对象

c# - 为什么我的 GZipStream 不可写?

google-app-engine - 谷歌应用引擎: How do i configure it to send some resources as zipped to save bandwidth?

c# - gzip 格式流式传输