[python] 파이썬에서 문자열에서 HTML 제거

from mechanize import Browser
br = Browser()
br.open('http://somewebpage')
html = br.response().readlines()
for line in html:
  print line

HTML 파일에 줄을 인쇄 할 때 서식 자체가 아닌 각 HTML 요소의 내용 만 표시하는 방법을 찾으려고합니다. 이 발견 '<a href="whatever.com">some text</a>'되면 ‘일부 텍스트’만 인쇄하고 '<b>hello</b>'‘hello’등을 인쇄합니다. 어떻게하면 좋을까요?



답변

필자는 파이썬 stdlib 만 필요하기 때문에 항상이 함수를 사용하여 HTML 태그를 제거했습니다.

파이썬 3의 경우 :

from io import StringIO
from html.parser import HTMLParser

class MLStripper(HTMLParser):
    def __init__(self):
        super().__init__()
        self.reset()
        self.strict = False
        self.convert_charrefs= True
        self.text = StringIO()
    def handle_data(self, d):
        self.text.write(d)
    def get_data(self):
        return self.text.getvalue()

def strip_tags(html):
    s = MLStripper()
    s.feed(html)
    return s.get_data()

파이썬 2의 경우 :

from HTMLParser import HTMLParser
from StringIO import StringIO

class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.text = StringIO()
    def handle_data(self, d):
        self.text.write(d)
    def get_data(self):
        return self.text.getvalue()

def strip_tags(html):
    s = MLStripper()
    s.feed(html)
    return s.get_data()


답변

나는 그것이 놓칠 사건에 대해 많이 생각하지 않았지만 간단한 정규 표현식을 수행 할 수 있습니다.

re.sub('<[^<]+?>', '', text)

정규 표현식을 이해하지 못하는 사람들을 위해 <...>내부 컨텐츠 +가 아닌 하나 이상의 문자 로 구성된 string을 검색 합니다 <. ?수단은 그것을 찾을 수있는 작은 문자열과 일치하는 것이다. 예를 들어 주어진를 들어 <p>Hello</p>, 그것은 일치 <'p></p>별도로 ?. 그것이 없으면 전체 문자열과 일치합니다 <..Hello..>.

비 태그 <가 html (예 :)로 표시 되면 어쨌든 2 < 3이스케이프 시퀀스로 작성해야 &...하므로 ^<불필요 할 수 있습니다.


답변

BeautifulSoup get_text()기능을 사용할 수 있습니다 .

from bs4 import BeautifulSoup

html_str = '''
<td><a href="http://www.fakewebsite.com">Please can you strip me?</a>
<br/><a href="http://www.fakewebsite.com">I am waiting....</a>
</td>
'''
soup = BeautifulSoup(html_str)

print(soup.get_text())
#or via attribute of Soup Object: print(soup.text)

출력을 재현 할 수 있도록 구문 분석기 를 명시 적으로 지정하는 것이 좋습니다 ( 예 BeautifulSoup(html_str, features="html.parser"):).


답변

짧은 버전!

import re, cgi
tag_re = re.compile(r'(<!--.*?-->|<[^>]*>)')

# Remove well-formed tags, fixing mistakes by legitimate users
no_tags = tag_re.sub('', user_input)

# Clean up anything else by escaping
ready_for_web = cgi.escape(no_tags)

정규식 소스 : MarkupSafe . 그들의 버전은 HTML 엔터티도 처리하지만이 빠른 엔터티는 그렇지 않습니다.

왜 태그를 제거하고 그대로 둘 수 없습니까?

떠 다니지 <i>italicizing</i>않고 사람들을 사물 로부터 지키는 것이 한 가지 i입니다. 그러나 임의의 입력을 취하고 완전히 무해하게 만드는 것은 또 다른 방법입니다. 이 페이지의 대부분의 기술은 닫히지 않은 주석 ( <!--) 및 태그 ( blah <<<><blah)의 일부가 아닌 꺾쇠 괄호와 같은 것을 그대로 둡니다 . HTMLParser 버전은 닫지 않은 주석 안에 있으면 완전한 태그를 그대로 둘 수도 있습니다.

템플릿이 {{ firstname }} {{ lastname }}어떻게 되나요? firstname = '<a'그리고 lastname = 'href="http://evil.com/">'될 것입니다이 페이지의 모든 태그 스트리퍼에 의해 통해하자 (@Medeiros 제외!), 그들은 자신에 완료되지 태그이기 때문에. 일반적인 HTML 태그를 제거하는 것만으로는 충분하지 않습니다.

strip_tags이 질문에 대한 최상위 답변의 개선 된 (다음 제목 참조) 버전 인 Django ‘s 는 다음 경고를 제공합니다.

결과 문자열이 HTML에 안전하다는 보장은 없습니다. 따라서 strip_tags통화 결과를 먼저 이스케이프 처리하지 않고 표시 하지 마십시오 ( 예 🙂 escape().

그들의 충고를 따르십시오!

HTMLParser로 태그를 제거하려면 여러 번 실행해야합니다.

이 질문에 대한 최고 답변을 우회하는 것은 쉽습니다.

이 문자열을보십시오 ( source and discussion ) :

<img<!-- --> src=x onerror=alert(1);//><!-- -->

HTMLParser가 처음 볼 때, <img...>이 태그 임을 알 수 없습니다 . 그것은 깨져서 HTMLParser는 그것을 제거하지 않습니다. 그것은 밖으로 데리고 <!-- comments -->, 당신을 떠나

<img src=x onerror=alert(1);//>

이 문제는 2014 년 3 월 Django 프로젝트에 공개되었습니다. 그들의 문제 strip_tags는 본질적으로이 질문에 대한 최고 답변과 동일합니다. 새 버전은 기본적으로 다시 실행해도 문자열이 변경되지 않을 때까지 루프에서 실행됩니다.

# _strip_once runs HTMLParser once, pulling out just the text of all the nodes.

def strip_tags(value):
    """Returns the given HTML with all tags stripped."""
    # Note: in typical case this loop executes _strip_once once. Loop condition
    # is redundant, but helps to reduce number of executions of _strip_once.
    while '<' in value and '>' in value:
        new_value = _strip_once(value)
        if len(new_value) >= len(value):
            # _strip_once was not able to detect more tags
            break
        value = new_value
    return value

물론 항상의 결과를 벗어나면 문제가되지 않습니다 strip_tags().

2015 년 3 월 19 일 업데이트 : 1.4.20, 1.6.11, 1.7.7 및 1.8c1 이전의 Django 버전에 버그가있었습니다. 이 버전들은 strip_tags () 함수에 무한 루프를 입력 할 수 있습니다. 고정 버전은 위에서 재현되었습니다. 자세한 내용은 여기를 참조하십시오 .

복사하거나 사용하기에 좋은 것들

내 예제 코드는 HTML 엔터티를 처리하지 않습니다. Django 및 MarkupSafe 패키지 버전이 있습니다.

내 예제 코드는 교차 사이트 스크립팅 방지를 위해 뛰어난 MarkupSafe 라이브러리 에서 가져 왔습니다 . 편리하고 빠릅니다 (C 속도를 기본 Python 버전으로 향상). 그것은에 포함 된 구글 앱 엔진 , 그리고에 의해 사용 Jinja2 (2.7 이상) , 마코, 철탑, 그리고 더. Django 1.7의 Django 템플릿과 쉽게 작동합니다.

Django의 strip_tags 및 최신 버전의 다른 html 유틸리티 는 좋지만 MarkupSafe보다 편리하지 않습니다. 그것들은 꽤 독립적이며이 파일 에서 필요한 것을 복사 할 수 있습니다 .

거의 모든 태그 를 제거해야하는 경우 Bleach 라이브러리가 적합합니다. “사용자가 이탤릭체를 사용할 수 있지만 iframe을 만들 수는 없습니다”와 같은 규칙을 적용 할 수 있습니다.

태그 스트리퍼의 속성을 이해하십시오! 퍼지 테스트를 실행하십시오! 이 답변에 대한 연구를 수행하는 데 사용한 코드는 다음과 같습니다 .

sheepish note- 질문 자체는 콘솔로 인쇄하는 것에 관한 것이지만 이것이 “python strip html from string”에 대한 Google의 최고 결과이므로,이 답변이 웹에 대해 99 % 인 이유입니다.


답변

태그를 제거 하고 HTML 엔터티를 일반 텍스트로 디코딩 하는 방법이 필요했습니다 . 다음 솔루션은 Eloff의 답변을 기반으로합니다 (엔터티를 제거하기 때문에 사용할 수 없었습니다).

from HTMLParser import HTMLParser
import htmlentitydefs

class HTMLTextExtractor(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.result = [ ]

    def handle_data(self, d):
        self.result.append(d)

    def handle_charref(self, number):
        codepoint = int(number[1:], 16) if number[0] in (u'x', u'X') else int(number)
        self.result.append(unichr(codepoint))

    def handle_entityref(self, name):
        codepoint = htmlentitydefs.name2codepoint[name]
        self.result.append(unichr(codepoint))

    def get_text(self):
        return u''.join(self.result)

def html_to_text(html):
    s = HTMLTextExtractor()
    s.feed(html)
    return s.get_text()

빠른 테스트 :

html = u'<a href="#">Demo <em>(&not; \u0394&#x03b7;&#956;&#x03CE;)</em></a>'
print repr(html_to_text(html))

결과:

u'Demo (\xac \u0394\u03b7\u03bc\u03ce)'

오류 처리:

  • 잘못된 HTML 구조로 인해 HTMLParseError 가 발생할 수 있습니다 .
  • 잘못된 명명 된 HTML 엔터티 (예 : &#apos;XML 및 XHTML에서는 유효하지만 일반 HTML에서는 유효하지 않음)는 ValueError예외를 발생시킵니다.
  • Python에서 허용하는 유니 코드 범위 밖의 코드 포인트를 지정하는 숫자 HTML 엔터티 (예 : 일부 시스템에서는 Basic Multilingual Plane 외부의 문자 )에서 ValueError예외 가 발생합니다 .

보안 참고 사항 : HTML 제거 (HTML을 일반 텍스트로 변환)와 HTML 살균 (일반 텍스트를 HTML로 변환)과 혼동하지 마십시오. 이 답변은 HTML을 제거하고 엔터티를 일반 텍스트로 디코딩하므로 결과를 HTML 컨텍스트에서 안전하게 사용할 수 없습니다.

예 : &lt;script&gt;alert("Hello");&lt;/script&gt;로 변환됩니다 <script>alert("Hello");</script>. 이는 100 % 올바른 동작이지만 결과 일반 텍스트를 HTML 페이지에 그대로 삽입하면 충분하지 않습니다.

규칙은 어렵지 않다 : 때마다 당신은 HTML 출력에 일반 텍스트 문자열을 삽입, 당신이해야 항상 HTML (사용하여 탈출 cgi.escape(s, True)이 HTML을 포함하지 않는 것을 당신이 “알고있는”경우에도) (예를 들어, 당신은 HTML 콘텐츠를 제거하기 때문에) .

그러나 OP는 콘솔에 결과를 인쇄하도록 요청했으며이 경우 HTML 이스케이프가 필요하지 않습니다.

파이썬 3.4+ 버전 : (doctest와 함께!)

import html.parser

class HTMLTextExtractor(html.parser.HTMLParser):
    def __init__(self):
        super(HTMLTextExtractor, self).__init__()
        self.result = [ ]

    def handle_data(self, d):
        self.result.append(d)

    def get_text(self):
        return ''.join(self.result)

def html_to_text(html):
    """Converts HTML to plain text (stripping tags and converting entities).
    >>> html_to_text('<a href="#">Demo<!--...--> <em>(&not; \u0394&#x03b7;&#956;&#x03CE;)</em></a>')
    'Demo (\xac \u0394\u03b7\u03bc\u03ce)'

    "Plain text" doesn't mean result can safely be used as-is in HTML.
    >>> html_to_text('&lt;script&gt;alert("Hello");&lt;/script&gt;')
    '<script>alert("Hello");</script>'

    Always use html.escape to sanitize text before using in an HTML context!

    HTMLParser will do its best to make sense of invalid HTML.
    >>> html_to_text('x < y &lt z <!--b')
    'x < y < z '

    Unrecognized named entities are included as-is. '&apos;' is recognized,
    despite being XML only.
    >>> html_to_text('&nosuchentity; &apos; ')
    "&nosuchentity; ' "
    """
    s = HTMLTextExtractor()
    s.feed(html)
    return s.get_text()

HTMLParser는 Python 3에서 개선되었습니다 (코드가 적고 오류 처리가 향상됨).


답변

간단한 방법이 있습니다.

def remove_html_markup(s):
    tag = False
    quote = False
    out = ""

    for c in s:
            if c == '<' and not quote:
                tag = True
            elif c == '>' and not quote:
                tag = False
            elif (c == '"' or c == "'") and tag:
                quote = not quote
            elif not tag:
                out = out + c

    return out

아이디어는 여기에 설명되어 있습니다 : http://youtu.be/2tu9LTDujbw

여기에서 작동하는 것을 볼 수 있습니다 : http://youtu.be/HPkNPcYed9M?t=35s

추신-수업에 관심이 있다면 (python을 사용한 스마트 디버깅에 대해) http://www.udacity.com/overview/Course/cs259/CourseRev/1 링크를 제공합니다 . 무료입니다!

천만에요! 🙂


답변

HTML 엔티티 (예 &amp;🙂 를 유지해야하는 경우 Eloff의 답변 에 “handle_entityref”메소드를 추가했습니다 .

from HTMLParser import HTMLParser

class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.fed = []
    def handle_data(self, d):
        self.fed.append(d)
    def handle_entityref(self, name):
        self.fed.append('&%s;' % name)
    def get_data(self):
        return ''.join(self.fed)

def html_to_text(html):
    s = MLStripper()
    s.feed(html)
    return s.get_data()