如何让xml.sax为其DTD请求使用HTTP代理?

时间:2010-11-23 09:56:19

标签: python xml sax

文档中引用的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,因为它没有获得足够的信息。

1 个答案:

答案 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,
    )