Ordenación posterior mediante búsqueda en profundidad (DFS) de HTML, utilizando python, lxml, etree.
Esta no es una pregunta sobre el algoritmo de DFS ni sobre sugerencias de bibliotecas. Es específicamente sobre lxml.etree (v 4). Uso Python 3.9.
Esta biblioteca, ‘lxml.etree’, proporciona una forma de iterar sobre el ElementTree en el cual se ha analizado un código HTML. El iterador es DFS, pero en preorden (usando el término del artículo de Wikipedia sobre DFS). Esto significa que los elementos se generan en el orden de la primera visita. Mi pregunta es cómo implementar de manera fácil la iteración en postorden.
Aquí hay un código mínimo que demuestra que el orden predeterminado de ‘iter()’ es el preorden. Creé una función ficticia para que el segundo test falle obviamente. Necesito una implementación de ‘_iter_postorder’ para que la afirmación sea verdadera.
import unittest
from typing import List
from xml.etree.ElementTree import ElementTree
from lxml import etree
HTML1 = '''
<div class="c1">
<span class="c11">11</span>
<span class="c12">12</span>
</div>
'''
def _iter_postorder(tree: ElementTree) -> List[str]:
return []
class EtreeElementTests(unittest.TestCase):
def test_dfs_preordering(self):
""" regular iter() is dfs preordering"""
root = etree.HTML(HTML1, etree.XMLParser())
tree = ElementTree(root)
result = [el.attrib['class'] for el in tree.iter()]
self.assertListEqual(result, ['c1', 'c11', 'c12'])
def test_dfs_postordering(self):
root = etree.HTML(HTML1, etree.XMLParser())
tree = ElementTree(root)
result = _iter_postorder(tree)
self.assertListEqual(result, ['c11', 'c12', 'c1'])
davy.ai
Para implementar la iteración de postorden utilizando
lxml.etree
, se puede utilizar el métodoiter()
con el parámetroreverse
establecido enTrue
para iterar sobre el árbol de elementos en orden inverso, lo cual efectivamente es un recorrido de postorden. Sin embargo, si solo necesitamos devolver los atributos de los elementos y no los propios elementos, podemos utilizar el métodofindall()
con una expresión XPath que seleccione todos los elementos descendientes de un elemento específico y devuelva sus atributos en orden de recorrido de postorden. A continuación se muestra la implementación de la función_iter_postorder
que satisface el caso de prueba:En esta implementación,
root.findall('.//*[@class]')
selecciona todos los elementos que tienen un atributoclass
, y para cada uno de estos elementos, utilizamosel.iterdescendants()
para obtener todos sus elementos descendientes (excepto el elemento en sí) en un recorrido de preorden, y luego añadimos el atributoclass
del elemento actual a la lista de resultado. De esta manera, obtenemos los atributos de todos los elementos en un orden de recorrido de postorden. Ten en cuenta que utilizamos la sintaxis//*
en la expresión XPath para seleccionar todos los elementos descendientes de un elemento específico.