Je travaille sur du contenu XML qui contient des éléments pouvant contenir du contenu XML/de type balisage (par exemple, HTML) potentiellement mal formé sous forme de texte. Par exemple:

<root>
    <data>
        <x>foo<y>bar</y>
    </data>
    <data>
        <z>foo<y>bar</y>
    </data>
</root>

Objectif : je souhaite que lxml.etree n'essaie pas d'analyser quoi que ce soit sous les éléments data en tant que XML, mais qu'il le renvoie simplement sous la forme bytes ou str ( peut être dans elem.text).

Les fichiers sont volumineux et je voulais utiliser lxml.etree.iterparse pour extraire le contenu trouvé dans data- éléments.

Idée initiale : un moyen simple d'obtenir simplement le contenu de l'élément (dans ce cas contenant les balises de début et de fin data) pourrait être :

data = BytesIO(b"""
<root>
    <data>
        <x>foo<y>bar</y>
    </data>
    <data>
        <z>foo<y>bar</y>
    </data>
</root>
""")

from lxml import etree

# see below why html=True
context = etree.iterparse(data, events=("end",), tag=("data",), html=True)
contents = []  # I don't keep lists in the "real" application
for event, elem in context:
    contents.append(etree.tostring(elem))  # get back the full content underneath data

Le problème avec ceci est que lxml.etree peut rencontrer des problèmes d'analyse des enfants de data (par exemple : j'ai déjà dû utiliser html=True pour ne pas rencontrer de problèmes lorsque les données html sont stockées sous data). Je sais qu'il existe des classes d'éléments personnalisés dans lxml mais d'après ce que je comprends de la documentation, ils ne modifient pas le comportement d'analyse de lxml.etree dicté par libxml2).

Existe-t-il un moyen simple de dire à lxml de ne pas tenter d'analyser le contenu de l'élément en tant qu'enfant. L'application elle-même bénéficie d'autres fonctionnalités lxml que je devrais répliquer si j'écrivais un extracteur personnalisé pour data seul.

Ou existe-t-il un moyen d'utiliser XSLT pour transformer d'abord l'entrée pour le traitement en lxml et pour lier plus tard les données ?

0
sim 6 févr. 2020 à 14:39

1 réponse

Meilleure réponse

Est-ce que cela fonctionne comme prévu ? Le XML est modifié en ajoutant DTD et CDATA pour spécifier que le contenu à l'intérieur de l'élément de données doit être traité comme des données de caractère.

data = io.BytesIO(B'''<?xml version='1.0' encoding='UTF-8'?>

<!DOCTYPE root [
<!ELEMENT root (data+)>
<!ELEMENT data (#PCDATA)>
]>

<root>
    <data>
        <![CDATA[
        <x>foo<y>bar</y>
        ]]>
    </data>
    <data>
        <![CDATA[
        <z>foo<y>bar</y>
        ]]>
    </data>
</root>

''')

from lxml import etree

# see below why html=True
context = etree.iterparse(data, events=("end",), tag=("data",), dtd_validation=True, load_dtd=True)
contents = []  # I don't keep lists in the "real" application
for event, elem in context:
    contents.append(etree.tostring(elem))  # get back the full content underneath data
1
Srinivas P 6 févr. 2020 à 14:05