我想解析像www.example.com/sitemap.xml.gz这样的压缩网站地图,并在不下载sitemap.xml.gz的情况下收集网站地图中的所有URL。
有一些方法可以在下载sitemap.xml.gz后对其进行解析,并在lxml
或beautifulsoup
等的帮助下对其进行解压缩。
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流的概念来解析它,这样我就不需要下载文件或将其写入文件?
如果唯一的要求是不写入磁盘,并且gzip’d文件没有任何扩展名,只有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
。。。
请注意,它不会"阻塞"迭代器,因为无论如何,您都可以在一个请求中获取整个文件。
使用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
保存到文件中,而是保存到StringIO
中。