개발/Python

lxml의 태그 안의 모든 텍스트 가져 오기

MinorMan 2020. 9. 19. 02:17
반응형

<질문>

내부의 모든 텍스트를 가져 오는 코드 스 니펫을 작성하고 싶습니다. 코드 태그를 포함하여 아래 세 가지 인스턴스 모두에서 lxml의 태그. tostring (getchildren ()) 시도했지만 태그 사이의 텍스트를 놓칠 것입니다. API에서 관련 기능을 검색하는 데 큰 행운이 없었습니다. 좀 도와 주 시겠어요?



Text inside tag
#should return "
Text inside tag
Text with no tag #should return "Text with no tag" Text outside tag
Text inside tag
#should return "Text outside tag
Text inside tag
"

<답변1>

시험:

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

예:

from lxml import etree
node = etree.fromstring("""
Text outside tag 
Text inside tag
""") stringify_children(node)

생성 : '\ n 태그 외부 텍스트

태그 안의 텍스트
\엔'


<답변2>

text_content ()가 필요한 작업을 수행합니까?


<답변3>

다음과 같이 node.itertext () 메서드를 사용하십시오.

 ''.join(node.itertext())

<답변4>

파이썬 생성기를 사용하는 다음 스 니펫은 완벽하게 작동하며 매우 효율적입니다.

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


<답변5>

hoju가보고 한 버그를 해결하는 albertov의 stringify-content 버전 :

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)

<답변6>

이런 식으로 stringify_children을 정의하는 것은 덜 복잡 할 수 있습니다.

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

또는 한 줄로

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

이론적 근거는이 답변과 동일합니다. 자식 노드의 직렬화를 lxml로 남겨 둡니다. 이 경우 노드의 꼬리 부분은 종료 태그 "뒤"에 있으므로 흥미롭지 않습니다. 인코딩 인수는 필요에 따라 변경 될 수 있습니다.

또 다른 가능한 해결책은 노드 자체를 직렬화하고 나중에 시작 및 종료 태그를 제거하는 것입니다.

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]

다소 끔찍합니다. 이 코드는 노드에 속성이없는 경우에만 정확하며, 그때에도 아무도 사용하고 싶지 않을 것이라고 생각합니다.


<답변7>

import urllib2
from lxml import etree
url = 'some_url'

URL 가져 오기

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

테이블 태그를 포함하여 모든 HTML 코드 가져 오기

tree = etree.HTML(page)

xpath 선택기

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

res는 이것이 나를 위해 일하는 테이블의 html 코드입니다.

따라서 xpath_text ()로 태그 내용을 추출하고 tostring ()을 사용하여 내용을 포함하는 태그를 추출 할 수 있습니다.

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

또는 text = tree.xpath ( "// content / text ()")

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

스트립 방법을 사용하는 마지막 줄은 좋지 않지만 작동합니다.


<답변8>

위의 @Richard의 의견에 대한 응답으로 stringify_children을 패치하면 다음을 읽을 수 있습니다.

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

그가 언급 한 중복을 피하는 것 같습니다.


<답변9>

실제로 저에게 도움이되었으며 http://lxml.de/tutorial.html#using-xpath-to-find-text의 문서에 따라 실제로 작동 한 가장 간단한 코드 조각 중 하나는 다음과 같습니다.

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

여기서 etree는 전체 텍스트를 읽으려는 노드 / 태그입니다. 그래도 스크립트 및 스타일 태그를 제거하지 않습니다.


<답변10>

나는 이것이 오래된 질문이라는 것을 알고 있지만 이것은 일반적인 문제이며 지금까지 제안한 것보다 더 간단한 해결책이 있습니다.

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

       >>> html = "

Sample sentence with tags.

" >>> node = lxml.html.fragment_fromstring(html) >>> extract_html_content(node) "Sample sentence 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]

이 질문에 대한 다른 답변과 달리이 솔루션은 그 안에 포함 된 모든 태그를 보존하고 다른 작업 솔루션과 다른 각도에서 문제를 공격합니다.


<답변11>

다음은 작동하는 솔루션입니다. 부모 태그가있는 콘텐츠를 가져온 다음 출력에서 부모 태그를 잘라낼 수 있습니다.

import re
from lxml import etree

def _tostr_with_tags(parent_element, html_entities=False):
    RE_CUT = r'^<([\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에는 요소 유형이 있어야합니다.

텍스트 콘텐츠 (텍스트의 html 엔티티가 아님)를 원하는 경우 html_entities 매개 변수를 False로 두십시오.


<답변12>

lxml에는 다음과 같은 방법이 있습니다.

node.text_content()

<답변13>

대답이 주어 졌을 때 빠르게 향상되었습니다. 내부 텍스트를 정리하려면 :

clean_string = ' '.join([n.strip() for n in node.itertext()]).strip()

<답변14>

태그 인 경우 다음을 시도 할 수 있습니다.

node.values()

<답변15>

import re
from lxml import etree

node = etree.fromstring("""
Text before inner tag
    
Text inside tag
Text after inner tag
""") print re.search("\A<[^<>]*>(.*)]*>\Z", etree.tostring(node), re.DOTALL).group(1)
반응형