2010-11-23 31 views
3

Il s'agit d'un problème connu XML parsers often send out HTTP requests for fetching DTDs référencé dans les documents. Plus précisément, Python's one does this. Cela provoque un trafic excessif pour www.w3.org, qui héberge beaucoup de ces DTD. À son tour, cela rend l'analyse XML prendre beaucoup de temps et, dans certains cas, expire. Cela peut être un problème sérieux, car cela rend une tâche apparemment uniquement liée au traitement de texte dépendant d'une tierce partie non fiable. Pour pallier ce problème (une vraie solution étant très difficile), je voudrais installer un proxy Web de mise en cache localement et demander à xml.sax d'envoyer ses requêtes via ce proxy. Je ne veux pas que les paramètres de proxy fuient vers d'autres composants, donc les paramètres système sont hors de question.Comment faire pour que xml.sax utilise un proxy HTTP pour ses requêtes DTD?

Comment faire pour que xml.sax utilise un proxy HTTP?

J'ai:

handler = # instance of a subclass of xml.sax.handler.ContentHandler 

parser = xml.sax.make_parser() 
parser.setContentHandler(handler) 
parser.parse(indata) 
return handler.result() 

Une approche consiste à utiliser un EntityResolver personnalisé. Cependant, il s'avère it is not possible to implement a caching EntityResolver, parce qu'il ne reçoit pas assez d'informations.

+0

Quelle version de Python? – aaronasterling

+0

@aaronasterling: 2,5 préféré, mais 2,6 et 2,7 sont également acceptables. –

Répondre

2

Un moyen rapide et sale de le faire serait de corriger le singe saxutils.prepare_input_source. Vous pouvez à peu près simplement copier + coller et tordre la branche qui appelle urllib.urlopen afin qu'il obtienne un UrlOpener de urllib2 avec votre proxy installé. Malheureusement, je pense que c'est la seule façon que vous allez obtenir votre comportement littéralement désiré sans changer les paramètres du système ou en créant votre propre EntityResolver qui pourrait mettre en cache les résultats.

Le problème est que saxutils.prepare_input_source fait un appel sans ambiguïté à urllib.urlopen et sans aucune option pour modifier ce comportement. Donc, vous devrez acheminer cela via votre proxy qui affecterait tous les autres clients de urllib.


Magnus Hoff: Une implémentation ragréage singe de travail:

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, 
    ) 
+0

Hmm ... Etes-vous sûr? Dans python 2.5.1, les entités sont résolues à l'intérieur de 'expat.c', et' urlopen' est appelé juste pour la source. – khachik

+0

@khachik. Oui, mais obtenir la source est toute la question. Si cela pouvait être mis en cache (en étant acheminé via un proxy), alors l'objectif serait atteint. Ou est-ce que je manque quelque chose? – aaronasterling

+0

Je travaille sur un EntityResolver maintenant, donc toute idée dans cette direction est la bienvenue aussi :) Apparemment, je n'ai pas l'argument 'base' qui est inclus pour' saxutils.prepare_input_source'. –