2009-12-29 5 views
1

Я использую модуль Python ElementTree для управления HTML. Я хочу подчеркнуть некоторые слова, и мое текущее решение:Вставить теги в текст ElementTree

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

выше анализирует текст каждого элемента и подчеркивает важные слова, которые он находит. Однако он делает это путем встраивания HTML-тегов в тексте атрибутов, который сбежавший при рендеринге, так что мне нужно противопоставить:

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

Это делает меня неудобным, поэтому я хочу, чтобы сделать это правильно. Однако для встраивания нового элемента мне нужно было бы сдвинуть атрибуты «текст» и «хвост», чтобы выделенный текст появился в том же положении. И это было бы очень сложно при повторении, как описано выше.

Любой совет, как это сделать должным образом, будет оценен по достоинству. Я уверен, что в API есть что-то, чего я пропустил!

ответ

3

Вы также можете использовать XSLT и пользовательские функции XPath, чтобы сделать это.

Приведенный ниже пример. Он по-прежнему нуждается в некоторой работе, например, очистке лишних пробелов в конце элементов и обработке смешанного текста, но это еще одна идея.

дал этот вход:


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

и глоссарий содержит два слова: некоторые, смелые

тогда пример вывода является:


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

Вот код, у меня тоже размещено на 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() 

 
1

Хотя ElementTree очень прост в использовании для большинства задач обработки XML, это также неудобно для смешанного контента. Я предлагаю использовать 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) 

Также я использовал регулярное выражение для разделения слов, чтобы избежать их слипание:

>>> ' '.join(ws_split('a b ')) 
'a b ' 
>>> ' '.join('a b '.split()) 
'a b' 

 Смежные вопросы

  • Нет связанных вопросов^_^