Muszę całkowicie usunąć elementy na podstawie zawartości atrybutu, używając lxml języka Python. Przykład:
import lxml.etree as et
xml="""
<groceries>
<fruit state="rotten">apple</fruit>
<fruit state="fresh">pear</fruit>
<fruit state="fresh">starfruit</fruit>
<fruit state="rotten">mango</fruit>
<fruit state="fresh">peach</fruit>
</groceries>
"""
tree=et.fromstring(xml)
for bad in tree.xpath("//fruit[@state=\'rotten\']"):
#remove this element from the tree
print et.tostring(tree, pretty_print=True)
Chciałbym to wydrukować:
<groceries>
<fruit state="fresh">pear</fruit>
<fruit state="fresh">starfruit</fruit>
<fruit state="fresh">peach</fruit>
</groceries>
Czy istnieje sposób, aby to zrobić bez przechowywania zmiennej tymczasowej i ręcznego drukowania do niej, jak:
newxml="<groceries>\n"
for elt in tree.xpath('//fruit[@state=\'fresh\']'):
newxml+=et.tostring(elt)
newxml+="</groceries>"
Szukasz
remove
funkcji. Wywołaj metodę usuwania drzewa i przekaż jej element podrzędny do usunięcia.import lxml.etree as et xml=""" <groceries> <fruit state="rotten">apple</fruit> <fruit state="fresh">pear</fruit> <punnet> <fruit state="rotten">strawberry</fruit> <fruit state="fresh">blueberry</fruit> </punnet> <fruit state="fresh">starfruit</fruit> <fruit state="rotten">mango</fruit> <fruit state="fresh">peach</fruit> </groceries> """ tree=et.fromstring(xml) for bad in tree.xpath("//fruit[@state='rotten']"): bad.getparent().remove(bad) print et.tostring(tree, pretty_print=True)
Wynik:
<groceries> <fruit state="fresh">pear</fruit> <fruit state="fresh">starfruit</fruit> <fruit state="fresh">peach</fruit> </groceries>
źródło
.remove()
element wymaga, aby był dzieckiem elementu, do którego go przywołujesz. Musisz więc wywołać to na rodzicu elementu, który chcesz usunąć. Odpowiedź poprawiona.Spotkałem jedną sytuację:
<div> <script> some code </script> text here </div>
div.remove(script)
usunietext here
część, której nie chciałem.podążając za odpowiedzią tutaj stwierdziłem, że
etree.strip_elements
jest to lepsze rozwiązanie dla mnie, które możesz kontrolować, czy usuniesz tekst za pomocąwith_tail=(bool)
param.Ale nadal nie wiem, czy to może używać filtru xpath dla tagu. Po prostu umieść to dla poinformowania.
Oto dokument:
źródło
Jak już wspomniano, możesz użyć
remove()
metody do usunięcia (pod) elementów z drzewa:for bad in tree.xpath("//fruit[@state=\'rotten\']"): bad.getparent().remove(bad)
Ale usuwa element, w tym jego
tail
, co jest problemem, jeśli przetwarzasz dokumenty o mieszanej zawartości, takie jak HTML:<div><fruit state="rotten">avocado</fruit> Hello!</div>
Staje się
<div></div>
To jest chyba to, czego nie zawsze chcesz :) Stworzyłem funkcję pomocniczą, aby usunąć tylko element i zachować jego ogon:
def remove_element(el): parent = el.getparent() if el.tail.strip(): prev = el.getprevious() if prev: prev.tail = (prev.tail or '') + el.tail else: parent.text = (parent.text or '') + el.tail parent.remove(el) for bad in tree.xpath("//fruit[@state=\'rotten\']"): remove_element(bad)
W ten sposób zachowa tekst ogona:
<div> Hello!</div>
źródło
el.tail is not None
, bo może być taki przypadek.Możesz również użyć html z lxml, aby rozwiązać ten problem:
from lxml import html xml=""" <groceries> <fruit state="rotten">apple</fruit> <fruit state="fresh">pear</fruit> <fruit state="fresh">starfruit</fruit> <fruit state="rotten">mango</fruit> <fruit state="fresh">peach</fruit> </groceries> """ tree = html.fromstring(xml) print("//BEFORE") print(html.tostring(tree, pretty_print=True).decode("utf-8")) for i in tree.xpath("//fruit[@state='rotten']"): i.drop_tree() print("//AFTER") print(html.tostring(tree, pretty_print=True).decode("utf-8"))
Powinien to wypisać:
//BEFORE <groceries> <fruit state="rotten">apple</fruit> <fruit state="fresh">pear</fruit> <fruit state="fresh">starfruit</fruit> <fruit state="rotten">mango</fruit> <fruit state="fresh">peach</fruit> </groceries> //AFTER <groceries> <fruit state="fresh">pear</fruit> <fruit state="fresh">starfruit</fruit> <fruit state="fresh">peach</fruit> </groceries>
źródło