Je travaille avec XML en utilisant la bibliothèque python lxml.

J'ai un paragraphe de texte comme ça,

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer facilisis elit eget
condimentum efficitur. Donec eu dignissim lectus. Integer tortor
lacus, porttitor at ipsum quis, tempus dignissim dui. Curabitur cursus
quis arcu in pellentesque. Aenean volutpat, tortor a commodo interdum,
lorem est convallis dui, sodales imperdiet ligula ligula non felis.</p>

Supposons que je veuille marquer un texte spécifique comme " tortor lacus, porttitor at ipsum quis, tempus " qui existe à l'intérieur du paragraphe ci-dessus, avec la balise. Comment pourrais-je procéder avec lxml. En ce moment, j'utilise le remplacement de texte, mais je pense que ce n'est pas la bonne façon de procéder.

C'est-à-dire que le résultat que je recherche serait

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer facilisis elit eget
condimentum efficitur. Donec eu dignissim lectus. Integer <foobar>tortor
lacus, porttitor at ipsum quis, tempus</foobar> dignissim dui. Curabitur cursus 
quis arcu in pellentesque. Aenean volutpat, tortor a commodo interdum,
lorem est convallis dui, sodales imperdiet ligula ligula non felis.</p>
1
DisneylandSC 26 août 2020 à 12:50

3 réponses

Meilleure réponse

Le remplacement du texte par un élément réel est délicat dans lxml; surtout si vous avez un contenu mixte (mélange de texte et d'éléments enfants).

La partie la plus délicate est de savoir quoi faire avec le texte restant et où insérer l'élément. Le texte restant doit-il faire partie du .text parent? Doit-il faire partie du .tail du frère précédent? Doit-il faire partie du .tail du nouvel élément?

Ce que j'ai fait dans le passé, c'est de traiter tous les nœuds text () et d'ajouter des chaînes d'espace réservé au texte (que ce soit .text ou .tail). Je sérialise ensuite l'arborescence en une chaîne et fais une recherche et un remplacement sur les espaces réservés. Après cela, j'analyse la chaîne au format XML pour créer un nouvel arbre (pour un traitement ultérieur, une validation, une analyse, etc.) ou je l'écris dans un fichier.

Veuillez consulter ma question connexe / réponse pour plus d'informations sur .text / .tail dans ce contexte.

Voici un exemple basé sur ma réponse à la question ci-dessus.

Remarques:

  • J'ai ajouté gotcha éléments pour montrer comment il gère le contenu mixte.
  • J'ai ajouté une deuxième chaîne de recherche (Aenean volutpat) pour montrer le remplacement de plus d'une chaîne.
  • Dans cet exemple, je ne traite que les nœuds text () qui sont des enfants de p.

Python

import re
from lxml import etree

xml = """<doc>
<p>Lorem ipsum dolor <gotcha>sit amet</gotcha>, consectetur adipiscing elit. Integer facilisis elit eget
condimentum efficitur. Donec eu dignissim lectus. Integer tortor
lacus, porttitor at ipsum quis, tempus dignissim dui. Curabitur cursus
quis arcu <gotcha>in pellentesque</gotcha>. Aenean volutpat, tortor a commodo interdum,
lorem est convallis dui, sodales imperdiet ligula ligula non felis.</p>
</doc>
"""


def update_text(orig_text, phrase_list, elemname):
    new_text = orig_text
    for phrase in phrase_list:
        if phrase in new_text:
            # Add placeholders for the new start/end tags.
            new_text = new_text.replace(phrase, f"[elemstart:{elemname}]{phrase}[elemend:{elemname}]")
        else:
            new_text = new_text
    return new_text


root = etree.fromstring(xml)

foobar_phrases = {"tortor lacus, porttitor at ipsum quis, tempus", "Aenean volutpat"}

for text in root.xpath("//p/text()"):
    parent = text.getparent()
    updated_text = update_text(text.replace("\n", " "), foobar_phrases, "foobar")
    if text.is_text:
        parent.text = updated_text
    elif text.is_tail:
        parent.tail = updated_text

# Serialze the tree to a string so we can replace the placeholders with proper tags.
serialized_tree = etree.tostring(root, encoding="utf-8").decode()
serialized_tree = re.sub(r"\[elemstart:([^\]]+)\]", r"<\1>", serialized_tree)
serialized_tree = re.sub(r"\[elemend:([^\]]+)\]", r"</\1>", serialized_tree)

# Now we can either parse the string back into a tree (for additional processing, validation, etc.),
# print it, write it to a file, etc.
print(serialized_tree)

Sortie imprimée (sauts de ligne ajoutés pour plus de lisibilité)

<doc>
<p>Lorem ipsum dolor <gotcha>sit amet</gotcha>, consectetur adipiscing elit. 
Integer facilisis elit eget condimentum efficitur. Donec eu dignissim lectus.
Integer <foobar>tortor lacus, porttitor at ipsum quis, tempus</foobar> dignissim dui.
Curabitur cursus quis arcu <gotcha>in pellentesque</gotcha>. <foobar>Aenean volutpat</foobar>, 
tortor a commodo interdum, lorem est convallis dui, sodales imperdiet ligula ligula non felis.</p>
</doc>
1
Daniel Haley 28 août 2020 à 16:49

Vous pouvez vérifier comme ceci s'il y a des enfants:

from lxml import etree

root = etree.parse("test.xml").getroot()
paragraphs = root.findall("p")

print(f"Found {len(paragraphs)} paragraphs")

for i in range(len(paragraphs)):
    if len(list(paragraphs[i])) > 0:
        print(f"Paragraph {i} has children")
    else:
        print(f"Paragraph {i} has no children")

Tout d'abord, le code filtre tous les paragraphes, puis vérifie si le paragraphe a des enfants.

Maintenant, si vous n'avez pas d'enfants, vous pouvez simplement remplacer le texte comme avant et si vous avez des enfants, vous pouvez remplacer tout l'enfant

0
andy meissner 26 août 2020 à 10:58

Si la balise <p> ne sera pas imbriquée dans une autre <p>, vous pouvez envisager de remplacer l'expression régulière

import re

a="""
other lines here that may contain foo
<p>
this is a foo inside para
and this is new line in this foo para
</p>
excess lines here that also may contain foo in it.
"""

search="foo"
newtagname="bar"

b=re.sub("("+search+")(?=[^><]*?</p>)","<"+newtagname+">\\1</"+newtagname+">",a)

print(b)

Cela imprime

other lines here that may contain foo
<p>
this is a <bar>foo</bar> inside para
and this is new line in this <bar>foo</bar> para
</p>
excess lines here that also may contain foo in it.
0
Liju 26 août 2020 à 11:34