Pregunta

Estoy usando Python ElementoÁrbol Módulo para manipular HTML.Quiero enfatizar ciertas palabras y mi solución actual es:

for e in tree.getiterator():
    for attr in 'text', 'tail':
        words = (getattr(e, attr) or '').split()
        change = False
        for i, word in enumerate(words):
            word = clean_word.sub('', word)
            if word.lower() in glossary:
                change = True
                words[i] = word.replace(word, '<b>' + word + '</b>')
        if change:
            setattr(e, attr, ' '.join(words))

Lo anterior examina el texto de cada elemento y enfatiza las palabras importantes que encuentra.Sin embargo, lo hace incrustando etiquetas HTML en los atributos de texto, que se escapan al renderizar, por lo que necesito contrarrestar con:

html = etree.tostring(tree).replace('&gt;', '>').replace('&lt;', '<')

Esto me incomoda así que quiero hacerlo correctamente.Sin embargo, para incrustar un nuevo elemento necesitaría cambiar los atributos 'texto' y 'cola' para que el texto enfatizado apareciera en la misma posición.Y esto sería realmente complicado al iterar como se indicó anteriormente.

Se agradecería cualquier consejo sobre cómo hacer esto correctamente.¡Estoy seguro de que hay algo que me he perdido en la API!

¿Fue útil?

Solución

También puedes usar xslt y una función xpath personalizada para hacer esto.

A continuación se muestra un ejemplo.Todavía necesita algo de trabajo, por ejemplo, limpiar espacios en blanco adicionales al final de los elementos y manejar texto mixto, pero es otra idea.

dada esta entrada:


<html>
<head>
</head>
<body>
<p>here is some text to bold</p>
<p>and some more</p>
</body>
</html>

y el glosario contiene las dos palabras: algunos, negrita

entonces el resultado del ejemplo es:


<?xml version="1.0"?>
<html>
<head/>
<body>
<p>here is <b>some</b> text to <b>bold</b> </p>
<p>and <b>some</b> more </p>
</body>
</html>

Aquí está el código, también lo publiqué en http://bkc.pastebin.com/f545a8e1d


from lxml import etree

stylesheet = etree.XML("""
    <xsl:stylesheet version="1.0"
         xmlns:btest="uri:bolder"
         xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

        <xsl:template match="@*">
            <xsl:copy />
        </xsl:template>

        <xsl:template match="*">
            <xsl:element name="{name(.)}">
                <xsl:copy-of select="@*" />
                <xsl:apply-templates select="text()" />
                <xsl:apply-templates select="./*" />
            </xsl:element>
        </xsl:template>

        <xsl:template match="text()">
            <xsl:copy-of select="btest:bolder(.)/node()" />
        </xsl:template>         
     </xsl:stylesheet>
""")

glossary = ['some', 'bold']

def bolder(context, s):
    results = []
    r = None
    for word in s[0].split():
        if word in glossary:
            if r is not None:
                results.append(r)
            r = etree.Element('r')
            b = etree.SubElement(r, 'b')
            b.text = word
            b.tail = ' '
            results.append(r)
            r = None
        else:
            if r is None:
                r = etree.Element('r')
            r.text = '%s%s ' % (r.text or '', word)

        if r is not None:
            results.append(r)
    return results

def test():
    ns = etree.FunctionNamespace('uri:bolder') # register global namespace
    ns['bolder'] = bolder # define function in new global namespace
    transform = etree.XSLT(stylesheet)
    print str(transform(etree.XML("""<html><head></head><body><p>here is some text to bold</p><p>and some more</p></body></html>""")))

if __name__ == "__main__":
    test()


Otros consejos

A pesar de elementtree es muy fácil de usar para la mayoría de las tareas de procesamiento XML, también es un inconveniente para contenido mixto. Sugiero usar analizador DOM:

from xml.dom import minidom
import re

ws_split = re.compile(r'\s+', re.U).split

def processNode(parent):
    doc = parent.ownerDocument
    for node in parent.childNodes[:]:
        if node.nodeType==node.TEXT_NODE:
            words = ws_split(node.nodeValue)
            new_words = []
            changed = False
            for word in words:
                if word in glossary:
                    text = ' '.join(new_words+[''])
                    parent.insertBefore(doc.createTextNode(text), node)
                    b = doc.createElement('b')
                    b.appendChild(doc.createTextNode(word))
                    parent.insertBefore(b, node)
                    new_words = ['']
                    changed = True
                else:
                    new_words.append(word)
            if changed:
                text = ' '.join(new_words)
                print text
                parent.replaceChild(doc.createTextNode(text), node)
        else:
            processNode(node)

También he utilizado expresiones regulares para separar palabras para evitar que se peguen entre sí:

>>> ' '.join(ws_split('a b '))
'a b '
>>> ' '.join('a b '.split())
'a b'
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top