Frage

Ich benutze den Python Elementtree Modul zur Manipulation von HTML. Ich möchte bestimmte Wörter hervorheben, und meine aktuelle Lösung lautet:

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))

Der obige untersucht den Text jedes Elements und betont die wichtigen Wörter, die es findet. Dies geschieht jedoch durch Einbetten von HTML -Tags in die Textattribute, die beim Rendern entkommen sind, damit ich mit:

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

Das macht mich unangenehm, also möchte ich es richtig machen. Um jedoch ein neues Element einzubetten, müsste ich mich um die Attribute "Text" und "Schwanz" umziehen, damit der betonte Text an derselben Position erschien. Und das wäre wirklich schwierig, wenn es wie oben iteriert wird.

Jeder Rat, wie man dies richtig macht, wäre geschätzt. Ich bin sicher, dass ich in der API etwas verpasst habe!

War es hilfreich?

Lösung

Zu diesem Zweck können Sie auch XSLT und eine benutzerdefinierte XPath -Funktion verwenden.

Nachfolgend ist ein Beispiel gezeigt. Es braucht noch etwas Arbeit, zum Beispiel, das zusätzliche Weißespace am Ende von Elementen und den Umgang mit gemischtem Text aufzuräumen, aber es ist eine andere Idee.

Angesichts dieser Eingabe:


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

und Glossar enthält die beiden Wörter: Einige, mutig

Dann ist die Beispielausgabe:


<?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>

Hier ist der Code, ich habe ihn auch gepostet bei 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()


Andere Tipps

Obwohl ElementTree bei den meisten XML -Verarbeitungsaufgaben sehr einfach zu verwenden ist, ist es auch für gemischte Inhalte unpraktisch. Ich schlage vor, DOM -Parser zu verwenden:

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)

Außerdem habe ich Regexp verwendet, um Wörter zu teilen, um zu vermeiden, dass sie zusammenkleben:

>>> ' '.join(ws_split('a b '))
'a b '
>>> ' '.join('a b '.split())
'a b'
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top