Analyse de XML brisé avec lxml.etree.iterparse
Je suis en train d'analyser un énorme fichier xml avec lxml dans une mémoire de manière efficace (c'est à dire streaming paresseusement à partir du disque au lieu de charger tout le fichier en mémoire). Malheureusement, le fichier contient des mauvais caractères ascii qui cassent la valeur par défaut de l'analyseur. L'analyseur fonctionne si je l'ai mis récupérer=Vrai, mais la iterparse méthode ne prend pas la récupérer paramètre ou d'un parseur personnalisé objet. Personne ne sait comment les utiliser iterparse pour analyser cassé xml?
#this works, but loads the whole file into memory
parser = lxml.etree.XMLParser(recover=True) #recovers from bad characters.
tree = lxml.etree.parse(filename, parser)
#how do I do the equivalent with iterparse? (using iterparse so the file can be streamed lazily from disk)
context = lxml.etree.iterparse(filename, tag='RECORD')
#record contains 6 elements that I need to extract the text from
Merci pour votre aide!
EDIT -- Voici un exemple des types d'erreurs de codage, je suis en cours d'exécution dans:
In [17]: data
Out[17]: '\t<articletext><p>The cafeteria rang with excited voices. Our barbershop quartet, The Bell \r Tones was asked to perform at the local Home for the Blind in the next town. We, of course, were glad to entertain such a worthy group and immediately agreed . One wag joked, "Which uniform should we wear?" followed with, "Oh, that\'s right, they\'ll never notice." The others didn\'t respond to this, in fact, one said that we should wear the nicest outfit we had.</p><p>A small stage was set up for us and a pretty decent P.A. system was donated for the occasion. The audience was made up of blind persons of every age, from the thirties to the nineties. Some sported sighted companions or nurses who stood or sat by their side, sharing the moment equally. I observed several German shepherds lying at their feet, adoration showing in their eyes as they wondered what was going on. After a short introduction in which we identified ourselves, stating our voice part and a little about our livelihood, we began our program. Some songs were completely familiar and others, called "Oh, yeah" songs, only the chorus came to mind. We didn\'t mind at all that some sang along \x1e they enjoyed it so much.</p><p>In fact, a popular part of our program is when the audience gets to sing some of the old favorites. The harmony parts were quite evident as they tried their voices to the different parts. I think there was more group singing in the old days than there is now, but to blind people, sound and music is more important. We received a big hand at the finale and were made to promise to return the following year. Everyone was treated to coffee and cake, our quartet going around to the different circles of friends to sing a favorite song up close and personal. As we approached a new group, one blind lady amazed me by turning to me saying, "You\'re the baritone, aren\'t you?" Previously no one had ever been able to tell which singer sang which part but this lady was listening with her whole heart.</p><p>Retired portrait photographer. Main hobby - quartet singing.</p></articletext>\n'
In [18]: lxml.etree.from
lxml.etree.fromstring lxml.etree.fromstringlist
In [18]: lxml.etree.fromstring(data)
---------------------------------------------------------------------------
XMLSyntaxError Traceback (most recent call last)
/mnt/articles/<ipython console> in <module>()
/usr/lib/python2.5/site-packages/lxml-2.2.4-py2.5-linux-i686.egg/lxml/etree.so in lxml.etree.fromstring (src/lxml/lxml.etree.c:48270)()
/usr/lib/python2.5/site-packages/lxml-2.2.4-py2.5-linux-i686.egg/lxml/etree.so in lxml.etree._parseMemoryDocument (src/lxml/lxml.etree.c:71812)()
/usr/lib/python2.5/site-packages/lxml-2.2.4-py2.5-linux-i686.egg/lxml/etree.so in lxml.etree._parseDoc (src/lxml/lxml.etree.c:70673)()
/usr/lib/python2.5/site-packages/lxml-2.2.4-py2.5-linux-i686.egg/lxml/etree.so in lxml.etree._BaseParser._parseDoc (src/lxml/lxml.etree.c:67442)()
/usr/lib/python2.5/site-packages/lxml-2.2.4-py2.5-linux-i686.egg/lxml/etree.so in lxml.etree._ParserContext._handleParseResultDoc (src/lxml/lxml.etree.c:63824)()
/usr/lib/python2.5/site-packages/lxml-2.2.4-py2.5-linux-i686.egg/lxml/etree.so in lxml.etree._handleParseResult (src/lxml/lxml.etree.c:64745)()
/usr/lib/python2.5/site-packages/lxml-2.2.4-py2.5-linux-i686.egg/lxml/etree.so in lxml.etree._raiseParseError (src/lxml/lxml.etree.c:64088)()
XMLSyntaxError: PCDATA invalid Char value 30, line 1, column 1190
In [19]: chardet.detect(data)
Out[19]: {'confidence': 1.0, 'encoding': 'ascii'}
Comme vous pouvez le voir, chardet pense que c'est un fichier ascii, mais il y a un "\x1e au catalogue" droit au milieu de cet exemple, qui rend de lxml lever une exception.
source d'informationauteur erikcw
Vous devez vous connecter pour publier un commentaire.
Edit:
Ceci est une ancienne réponse et je l'aurais fait différemment aujourd'hui. Et je ne parle pas seulement de la muette snark ... depuis BeutifulSoup4 est disponible et il est vraiment très gentil. Je recommande à toute personne qui trébuche sur ici.
Actuellement accepté réponse est, bien, pas ce que l'on devrait faire.
La question elle-même a également une mauvaise hypothèse:
Fait
recover=True
est pour la récupération des de les XML. Cependant, il existe une "codage" option qui aurait résolu votre problème.C'est ça, c'est la solution.
BTW -- Pour quiconque est aux prises avec l'analyse de XML en python, en particulier à partir de sources tierces. Je sais, je sais, la documentation est mauvais et il y a beaucoup de fausses pistes; beaucoup de mauvais conseils.
les balises de fermeture
Voici un secret - HTMLParser() est... un Analyseur à le récupérer=True
Vous pourriez être en train d'essayer toutes sortes de choses à partir de ce qui est disponible en ligne. lxml documentation pourrait être mieux. Le code ci-dessus est ce que vous avez besoin pour 90% de votre analyse XML cas. Ici, je vais vous le déclare:
Vous êtes les bienvenus. Mes maux de tête == votre santé mentale. Plus il a d'autres caractéristiques que vous pourriez avoir besoin pour, vous savez, XML.
J'ai résolu le problème en créant une classe avec un Fichier comme interface d'un objet. La classe de méthode read() lit une ligne dans le fichier et remplace tous les "méchants", avant de revenir à la ligne pour iterparse.
J'ai dû modifier le myFile de catégorie a quelques temps d'ajouter un peu plus de replace() appelle quelques autres personnages qui ont fait lxml de s'étouffer. Je pense que lxml du SAX analyse aurait fonctionné aussi bien (et qu'il semble à l'appui de la récupérer en option), mais cette solution a fonctionné comme un charme!
De modifier une question, en indiquant ce qui se passe (message d'erreur exact et traceback (copier/coller, ne tapez pas de mémoire)) pour vous faire croire que les "mauvaises unicode" est le problème.
Obtenir chardet et nourrir votre dump MySQL. Dites-nous ce qu'il dit.
Nous montrer la première de 200 à 300 octets de votre dump, par exemple à l'aide
print repr(dump[:300])
Mise à jour Vous avez écrit """Comme vous pouvez le voir, chardet pense que c'est un fichier ascii, mais il y a un "\x1e au catalogue" droit au milieu de cet exemple, qui rend de lxml lever une exception."""
Je ne vois pas de "mauvais unicode" ici.
chardet est correct. Ce qui vous fait penser que "\x1e au catalogue" n'est pas en ASCII? C'est un caractère ASCII, un C0 contrôle personnage nommé "le SÉPARATEUR d'ENREGISTREMENT".
Le message d'erreur indique que vous avez un caractère non valide. C'est également correcte. Les seuls caractères de contrôle qui sont valides dans le XML sont
"\t"
"\r"
et"\n"
. MySQL doit être grogne à ce sujet et/ou de vous offrir un moyen de s'en échapper, par exemple_x001e_
(beurk!)Étant donné le contexte, il semble que ce personnage pourrait être supprimé sans perte de qualité. Vous pouvez fixer votre base de données ou vous pouvez supprimer les autres personnages de votre dump (après avoir vérifié qu'ils sont tous vanishable) ou vous pouvez choisir un moins difficile et moins volumnious format de sortie que XML.
Mise à jour 2 Vous voulez sans doute à l'utilisateur
iterparse()
pas parce que c'est votre but final, mais parce que vous voulez économiser de la mémoire. Si vous avez utilisé un format CSV, vous n'auriez pas un problème de mémoire.Mise à jour 3 En réponse à un commentaire de @Purrell:
Voici le contenu de la pastie; il mérite de conservation:
Pour le faire fonctionner, l'un d'importation doit être corrigé, et un autre fourni. Les données sont monstrueux. Il n'y a pas de sortie pour afficher le résultat. Voici un remplacement avec les données de couper vers le bas pour l'essentiel. Les 5 morceaux de texte ASCII (à l'exclusion des
<
et>
) qui sont tous les caractères XML valides sont remplacés part1
...,t5
. La délinquance\x1e
est flanquée part2
ett3
.Pas ce que je qualifierais de "récupération"; après les mauvais caractère, le
<
et>
personnages disparaissent.La pastie était en réponse à ma question "Ce qui vous donne l'idée que encoding='utf-8' va résoudre son problème?". Cela a été déclenchée par l'énoncé " Il n'y a cependant un "codage" de l'option qui aurait résolu votre problème." Mais encoding=ascii produit le même résultat. Donc, n'omettant le codage arg. Ce n'est PAS un problème d'encodage. L'affaire est close.