文档中引用的XML parsers often send out HTTP requests for fetching DTDs是一个已知问题。具体而言,Python's one does this。这会导致www.w3.org的流量过大,因为它承载了很多这些DTD。反过来,这使得XML解析需要很长时间,并且在某些情况下会超时。这可能是一个严重的问题,因为它使任务看起来只与依赖于不可靠的第三方的文本处理有关。
为了缓解这个问题(因为真正的解决方案非常困难),我想在本地安装一个缓存Web代理,并要求xml.sax通过此代理发送其请求。我特别不希望代理设置泄露给其他组件,因此系统范围的设置是不可能的。
如何让xml.sax使用HTTP代理?
我有:
handler = # instance of a subclass of xml.sax.handler.ContentHandler
parser = xml.sax.make_parser()
parser.setContentHandler(handler)
parser.parse(indata)
return handler.result()
一种方法是使用自定义EntityResolver。但是,结果是it is not possible to implement a caching EntityResolver,因为它没有获得足够的信息。
答案 0 :(得分:2)
执行此操作的一种快速而肮脏的方法是使用补丁saxutils.prepare_input_source
。你几乎可以复制+粘贴它并调整调用urllib.urlopen
的分支,以便在安装了代理的情况下从UrlOpener
获得urllib2
。
不幸的是,我认为这是您能够在不更改系统范围设置或创建自己的EntityResolver
可以缓存结果的情况下获得字面上所需行为的唯一方法。
问题在于saxutils.prepare_input_source
非常明确地调用了urllib.urlopen
并且没有修改此行为的选项。所以你必须通过你的代理路由它,这会影响urllib
的所有其他客户。
作者:Magnus Hoff:一个有效的猴子修补实现:
def make_caching_prepare_input_source(old_prepare_input_source, proxy):
def caching_prepare_input_source(source, base = None):
if isinstance(source, xmlreader.InputSource):
return source
full_uri = urlparse.urljoin(base or "", source)
if not full_uri.startswith('http:'):
args = (source,) if base == None else (source, base)
return old_prepare_input_source(*args)
r = urllib2.Request(full_uri)
r.set_proxy(proxy, 'http')
f = urllib2.urlopen(r)
i = xmlreader.InputSource()
i.setSystemId(source)
i.setByteStream(f)
return i
return caching_prepare_input_source
def enable_http_proxy(server):
saxutils.prepare_input_source = make_caching_prepare_input_source(
saxutils.prepare_input_source,
server,
)