es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

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'])
Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    Para implementar la iteración de postorden utilizando lxml.etree, se puede utilizar el método iter() con el parámetro reverse establecido en True 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étodo findall() 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:

    def _iter_postorder(tree: ElementTree) -> List[str]:
        root = tree.getroot()
        result = []
        for el in root.findall('.//*[@class]'):
            result.extend([child.attrib['class'] for child in el.iterdescendants()])
            result.append(el.attrib['class'])
        return result
    

    En esta implementación, root.findall('.//*[@class]') selecciona todos los elementos que tienen un atributo class, y para cada uno de estos elementos, utilizamos el.iterdescendants() para obtener todos sus elementos descendientes (excepto el elemento en sí) en un recorrido de preorden, y luego añadimos el atributo class 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.

Comments are closed.