wake-up-neo.com

Holen Sie sich den gesamten Text in einem Tag in lxml

Ich möchte ein Code-Snippet schreiben, das den gesamten Text des <content>-Tags, in lxml, in allen drei folgenden Fällen einschließlich der Code-Tags erfasst. Ich habe tostring(getchildren()) ausprobiert, aber das würde den Text zwischen den Tags vermissen. Ich hatte nicht viel Glück, die API nach einer relevanten Funktion zu durchsuchen. Könntest du mir helfen?

<!--1-->
<content>
<div>Text inside tag</div>
</content>
#should return "<div>Text inside tag</div>

<!--2-->
<content>
Text with no tag
</content>
#should return "Text with no tag"


<!--3-->
<content>
Text outside tag <div>Text inside tag</div>
</content>
#should return "Text outside tag <div>Text inside tag</div>"
57
Kevin Burke

Versuchen:

def stringify_children(node):
    from lxml.etree import tostring
    from itertools import chain
    parts = ([node.text] +
            list(chain(*([c.text, tostring(c), c.tail] for c in node.getchildren()))) +
            [node.tail])
    # filter removes possible Nones in texts and tails
    return ''.join(filter(None, parts))

Beispiel:

from lxml import etree
node = etree.fromstring("""<content>
Text outside tag <div>Text <em>inside</em> tag</div>
</content>""")
stringify_children(node)

Produziert: '\nText outside tag <div>Text <em>inside</em> tag</div>\n'

39
albertov

Hat text_content () was Sie brauchen?

64
Ed Summers

Verwenden Sie einfach die node.itertext()-Methode, wie in:

 ''.join(node.itertext())
48
Arthur Debert

Das folgende Snippet, das python generators verwendet, funktioniert perfekt und ist sehr effizient.

''.join(node.itertext()).strip()

17
Sandeep

Eine Version von albertovs stringify-content , die die Fehler löst von hoju gemeldet:

def stringify_children(node):
    from lxml.etree import tostring
    from itertools import chain
    return ''.join(
        chunk for chunk in chain(
            (node.text,),
            chain(*((tostring(child, with_tail=False), child.tail) for child in node.getchildren())),
            (node.tail,)) if chunk)
16
anana
import urllib2
from lxml import etree
url = 'some_url'

uRL bekommen

test = urllib2.urlopen(url)
page = test.read()

den gesamten HTML-Code einschließlich table-Tag abrufen

tree = etree.HTML(page)

xPfad-Auswahl

table = tree.xpath("xpath_here")
res = etree.tostring(table)

res ist der HTML-Code der Tabelle Dies erledigte meine Arbeit.

so können Sie den Inhalt der Tags mit xpath_text () und Tags einschließlich deren Inhalt mithilfe von tostring () extrahieren.

div = tree.xpath("//div")
div_res = etree.tostring(div)
text = tree.xpath_text("//content") 

oder text = tree.xpath ("// content/text ()")

div_3 = tree.xpath("//content")
div_3_res = etree.tostring(div_3).strip('<content>').rstrip('</')

diese letzte Zeile mit Strip-Methode ist nicht schön, aber es funktioniert einfach

4
d3day

Das Definieren von stringify_children auf diese Weise kann weniger kompliziert sein:

from lxml import etree

def stringify_children(node):
    s = node.text
    if s is None:
        s = ''
    for child in node:
        s += etree.tostring(child, encoding='unicode')
    return s

oder in einer Zeile

return (node.text if node.text is not None else '') + ''.join((etree.tostring(child, encoding='unicode') for child in node))

Die Begründung ist dieselbe wie in diese Antwort : Die Serialisierung der untergeordneten Knoten sollte lxml sein. Der tail-Teil von node ist in diesem Fall nicht interessant, da er sich hinter dem End-Tag befindet. Beachten Sie, dass das Argument encoding je nach Bedarf geändert werden kann.

Eine andere mögliche Lösung besteht darin, den Knoten selbst zu serialisieren und anschließend das Start- und End-Tag zu entfernen:

def stringify_children(node):
    s = etree.tostring(node, encoding='unicode', with_tail=False)
    return s[s.index(node.tag) + 1 + len(node.tag): s.rindex(node.tag) - 2]

das ist etwas schrecklich. Dieser Code ist nur dann korrekt, wenn node keine Attribute hat, und ich glaube nicht, dass jemand ihn auch dann verwenden möchte.

3

Einer der einfachsten Codeausschnitte, der eigentlich für mich funktionierte und laut Dokumentation unter http://lxml.de/tutorial.html#using-xpath-to-find-text liegt

etree.tostring(html, method="text")

dabei ist etree ein Knoten/Tag, dessen vollständiger Text Sie zu lesen versuchen. Beachten Sie jedoch, dass Skript- und Stil-Tags nicht entfernt werden.

2

Als Antwort auf den Kommentar von @ Richard oben, wenn Sie den Befehl stringify_children mit einem Patch versehen:

 parts = ([node.text] +
--            list(chain(*([c.text, tostring(c), c.tail] for c in node.getchildren()))) +
++            list(chain(*([tostring(c)] for c in node.getchildren()))) +
           [node.tail])

es scheint die Verdoppelung zu vermeiden, auf die er sich bezieht.

2
bwingenroth

Ich weiß, dass dies eine alte Frage ist, aber dies ist ein allgemeines Problem und ich habe eine Lösung, die einfacher scheint als die bisher vorgeschlagenen:

def stringify_children(node):
    """Given a LXML tag, return contents as a string

       >>> html = "<p><strong>Sample sentence</strong> with tags.</p>"
       >>> node = lxml.html.fragment_fromstring(html)
       >>> extract_html_content(node)
       "<strong>Sample sentence</strong> with tags."
    """
    if node is None or (len(node) == 0 and not getattr(node, 'text', None)):
        return ""
    node.attrib.clear()
    opening_tag = len(node.tag) + 2
    closing_tag = -(len(node.tag) + 3)
    return lxml.html.tostring(node)[opening_tag:closing_tag]

Im Gegensatz zu einigen anderen Antworten auf diese Frage behält diese Lösung alle darin enthaltenen Tags bei und greift das Problem aus einem anderen Blickwinkel an als die anderen funktionierenden Lösungen.

1
Joshmaker

lxml hat eine Methode dafür:

node.text_content()
0
Hrabal

Hier ist eine funktionierende Lösung. Wir können Inhalte mit einem übergeordneten Tag abrufen und dann das übergeordnete Tag aus der Ausgabe ausschneiden.

import re
from lxml import etree

def _tostr_with_tags(parent_element, html_entities=False):
    RE_CUT = r'^<([\w-]+)>(.*)</([\w-]+)>$' 
    content_with_parent = etree.tostring(parent_element)    

    def _replace_html_entities(s):
        RE_ENTITY = r'&#(\d+);'

        def repl(m):
            return unichr(int(m.group(1)))

        replaced = re.sub(RE_ENTITY, repl, s, flags=re.MULTILINE|re.UNICODE)

        return replaced

    if not html_entities:
        content_with_parent = _replace_html_entities(content_with_parent)

    content_with_parent = content_with_parent.strip() # remove 'white' characters on margins

    start_tag, content_without_parent, end_tag = re.findall(RE_CUT, content_with_parent, flags=re.UNICODE|re.MULTILINE|re.DOTALL)[0]

    if start_tag != end_tag:
        raise Exception('Start tag does not match to end tag while getting content with tags.')

    return content_without_parent

parent_element muss vom Typ Element sein.

Bitte beachten Sie , wenn Sie Textinhalte (nicht HTML-Entitäten im Text) haben möchten, lassen Sie den html_entities-Parameter als False.

0
sergzach