Как я могу заставить xml.sax использовать HTTP-прокси для своих запросов DTD? - PullRequest
3 голосов
/ 23 ноября 2010

Известной проблемой является то, что парсеры XML часто отправляют HTTP-запросы на выбор DTD , на которые есть ссылки в документах. В частности, Python's делает это . Это вызывает чрезмерный трафик для www.w3.org, на котором размещено множество этих DTD. В свою очередь, это делает синтаксический анализ XML очень длительным, а в некоторых случаях время ожидания. Это может быть серьезной проблемой, поскольку делает задачу, казалось бы, связанную только с обработкой текста зависимой от ненадежной третьей стороны.

Чтобы смягчить эту проблему (поскольку реальное решение очень сложно), я бы хотел установить кеширующий веб-прокси локально и попросить 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. Однако, оказывается, невозможно реализовать кэширующий EntityResolver , поскольку он не получает достаточно информации.

1 Ответ

2 голосов
/ 23 ноября 2010

Один быстрый и грязный способ сделать это - установить обезьяну на патч saxutils.prepare_input_source. Вы можете просто скопировать + вставить его и настроить ветку, которая вызывает urllib.urlopen, чтобы он получал UrlOpener из urllib2 с установленным прокси.

К сожалению, я думаю, что это единственный способ получить буквально желаемое поведение без изменения общесистемных настроек или создания собственного EntityResolver, который может кэшировать результаты.

Проблема в том, что saxutils.prepare_input_source довольно однозначно делает вызов urllib.urlopen и не имеет опций для изменения этого поведения. Таким образом, вам придется перенаправить его через прокси-сервер, что повлияет на всех других клиентов urllib


По Магнусу Хоффу: Рабочая реализация исправления обезьян:

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,
    )
...