سؤال

أنا أستخدم بيثون Elementtree وحدة لمعالجة HTML. أريد التأكيد على كلمات معينة ، وحالي الحالي هو:

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;', '<')

هذا يجعلني غير مرتاح لذلك أريد أن أفعل ذلك بشكل صحيح. ومع ذلك ، لتضمين عنصر جديد ، سأحتاج إلى التحول حول سمات "النص" و "الذيل" بحيث ظهر النص التأكيد في نفس الموضع. وهذا سيكون صعبًا حقًا عند التكرار أعلاه.

أي نصيحة كيفية القيام بذلك بشكل صحيح سيكون موضع تقدير. أنا متأكد من وجود شيء فاتني في واجهة برمجة التطبيقات!

هل كانت مفيدة؟

المحلول

يمكنك أيضًا استخدام 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()


نصائح أخرى

على الرغم من أن ElementTree سهل الاستخدام في معظم مهام معالجة XML ، إلا أنه غير مريح أيضًا للمحتوى المختلط. أقترح استخدام DOM Parser:

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)

كما استخدمت regexp لتقسيم الكلمات لتجنب الالتصاق ببعضها البعض:

>>> ' '.join(ws_split('a b '))
'a b '
>>> ' '.join('a b '.split())
'a b'
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top