1

我想解析像 www.example.com/sitemap.xml.gz 这样的压缩站点地图,并收集站点地图中的所有网址,而无需下载 sitemap.xml.gz。

lxml在下载 sitemap.xml.gz 并在orbeautifulsoup等 ​​的帮助下解压缩后,有一些方法可以解析它。

def parse_sitemap_gz(url):
    r = requests.get(url, stream=True)
    if 200 != r.status_code:
    return False
    file_name = url.split('/')[-1]

    # download the sitemap file
    with open(file_name, 'wb') as f:
    if not r.ok:
        print 'error in %s'%(url)
    for block in r.iter_content(1024):
        if not block:
           break
        f.write(block) # can I parse it without writing to file
        f.flush()

    # decompress gz file
    subprocess.call(['gunzip', '-f', file_name])

    # parse xml file
    page = lxml.html.parse(file_name[0:-3])
    all_urls = page.xpath('//url/loc/text()')
    #print all_urls

    # delete sitemap file now
    subprocess.call(['rm', '-rf', file_name[0:-3]])
    return all_urls

在这段代码中,我正在将压缩的站点地图写入文件。我的意图不是写任何东西到文件中。
为了学习和创建上述代码的智能版本,我如何用解压缩 gzip 流的概念来解析它,这样我就不需要下载文件或将其写入文件?

4

2 回答 2

9

如果唯一的要求是不写入磁盘,并且 gzip 文件没有任何扩展,只有gunzip实用程序支持并适合内存,那么您可以从以下开始:

import requests
import gzip
from StringIO import StringIO

r = requests.get('http://example.com/sitemap.xml.gz')
sitemap = gzip.GzipFile(fileobj=StringIO(r.content)).read()

然后按您的方式sitemap解析...lxml

请注意,它不会“分块”迭代器,因为无论如何您都可以在单个请求中获取整个文件。

于 2014-10-25T16:50:57.690 回答
4

您可以通过使用对象来避免将任何数据写入文件——它们只包含内存中的数据,但通过实现类文件对象StringIO的协议来表现得像文件。

为了解压缩流式 gzip 数据,您不能直接使用 Python 的“gzip”模块。一方面,因为它会尽早尝试查找文件末尾,并且尝试计算 ADLER32 校验和也会失败。

但是您可以通过简单地zlib直接使用并在它们到达时解压缩块来解决这个问题。我用于流式 zlib 解压缩的代码基于Shashank 的帖子

from functools import partial
from lxml import etree
from StringIO import StringIO
import requests
import zlib


READ_BLOCK_SIZE = 1024 * 8


def decompress_stream(fileobj):
    result = StringIO()

    d = zlib.decompressobj(16 + zlib.MAX_WBITS)
    for chunk in iter(partial(response.raw.read, READ_BLOCK_SIZE), ''):
        result.write(d.decompress(chunk))

    result.seek(0)
    return result


url = 'http://example.org/sitemap.xml.gz'
response = requests.get(url, stream=True)

sitemap_xml = decompress_stream(response.raw)
tree = etree.parse(sitemap_xml)

# Get default XML namespace
ns = tree.getroot().nsmap[None]

urls = tree.xpath('/s:urlset/s:url/s:loc/text()', namespaces={'s': ns})
for url in urls:
    print url

请注意,为了避免保存到磁盘上的本地文件,您不必读取流式响应或使用流式 zlib 解压缩。您需要做的就是不保存response.content到文件,而是保存到 a 中StringIO

于 2014-10-25T16:47:03.427 回答