Fügen Sie Tags in ElementTree -Text ein
-
21-09-2019 - |
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('>', '>').replace('<', '<')
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!
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'