Pythonで文字列からHTMLを取り除く


270
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>'「こんにちは」などの出力のみを行います。これを行うにはどうすればよいでしょうか。


16
重要な考慮事項は、HTMLエンティティ(例:)の処理方法&amp;です。1)タグと一緒に削除する(多くの場合望ましくない、プレーンテキストと同等であるため不要)、2)変更せずに残す(ストリップされたテキストがHTMLコンテキストに戻る場合の適切な解決策)、または3 )それらをプレーンテキストにデコードします(除去されたテキストがデータベースまたは他の非HTMLコンテキストに送られる場合、またはWebフレームワークが自動的にテキストのHTMLエスケープを実行する場合)。
セーレンLøvborg

2
@SørenLøvborg点2)について:stackoverflow.com/questions/753052/...
ロバート

2
2014年3月までDjangoプロジェクトで使用されていたここでのトップの回答は、クロスサイトスクリプティングに対して安全ではないことが判明ています。Bleach.clean()、Markupsafeのstriptags、または最近のDjangoのstrip_tagsを使用することをお勧めします。
rescdsk 2014年

回答:


419

Python stdlibのみが必要であるため、私は常にこの関数を使用してHTMLタグを削除しました。

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

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

3
2年以上後、同じ問題に直面し、これははるかにエレガントな解決策です。私が変更したのは、self.fedを結合するのではなく、リストとして返すことだけだったので、要素のコンテンツをステップスルーできました。
演出:

47
これにより&amp;、タグだけでなくHTMLエンティティ(など)も削除されます。
セーレンLøvborg

30
@suryaきっとこれを見た
tkone

8
すばらしい答えをありがとう。Pythonの新しいバージョン(3.2以降)を使用している場合に注意すべき点の1つは、親クラスの__init__関数を呼び出す必要があることです。ここを参照:stackoverflow.com/questions/11061058/…
2013

10
HTMLエンティティ(ユニコードに変換)を保持するために、2つの行を追加しました:parser = HTMLParser()html = parser.unescape(html)strip_tags関数の先頭に。
James Doepp-pihentagyu

157

私はそれが見逃すケースについてはあまり考えていませんが、簡単な正規表現を行うことができます:

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

正規表現を理解しない人のために、これは文字列を検索します<...>。内部コンテンツは、1つ以上の(+)以外の()文字で構成されています<?それは見つけることができる最小の文字列と一致することを意味します。たとえば、指定された<p>Hello</p>場合は<'p>、と</p>個別に一致し?ます。それがなければ、それは文字列全体と一致します<..Hello..>

非タグ<がhtmlに表示される場合(例:)2 < 3、それは&...とにかくエスケープシーケンスとして記述する必要が^<あります。


10
これはほぼ正確にDjangoのstrip_tagsが行う方法です。
Bluu

10
これ&amp;により、出力のHTMLエンティティ(など)が変更されないことに注意してください。
セーレンLøvborg

36
次のような方法で、このメソッドをだますこともできます:<script <script >> alert( "Hi!")<< / script> / script>

19
このようにしないでください!@フリオ・ガルシアが言うように、それは安全ではありません!
rescdsk 2013

18
HTMLストリッピングとHTMLサニタイズを混同しないでください。はい、壊れたまたは悪意のある入力の場合、この回答はHTMLタグを含む出力を生成する可能性があります。これは、HTMLタグを取り除くための完全に有効なアプローチです。ただし、HTMLタグを取り除くことは、適切なHTMLサニタイズの有効な代用にはなりません。ルールは難しいことではありません。いつでもあなたは、HTML出力にプレーンテキストの文字列を挿入し、あなたがすべき常に HTML(使用して、それを逃れるcgi.escape(s, True)ことはHTMLが含まれていないことをあなたが「知っている」場合でも、)(例えば、あなたがHTMLコンテンツを取り除いているため) 。しかし、これはOPが尋ねたことではありません。
セーレンLøvborg

76

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


32

短縮版!

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>せずに、人を物事から遠ざけることは、1つのことiです。しかし、任意の入力を取り、それを完全に無害にすることは別の方法です。このページのほとんどのテクニックでは、閉じられていないコメント(<!--)やタグの一部ではない山括弧(blah <<<><blah)などをそのまま残します。HTMLParserバージョンでは、タグが閉じられていないコメント内にある場合でも、完全なタグを残すことができます。

あなたのテンプレートがどうなったら{{ firstname }} {{ lastname }}firstname = '<a'またlastname = 'href="http://evil.com/">'、このページのすべてのタグストリッパー(@Medeiros!を除く)によって許可されます。これらはそれ自体では完全なタグではないためです。通常のHTMLタグを取り除くだけでは不十分です。

Django strip_tagsは、この質問に対するトップアンサーの改良(次の見出しを参照)バージョンであり、次の警告を出します。

結果の文字列がHTMLセーフであるという保証は一切ありません。したがってstrip_tags、たとえばを使用して、最初にエスケープせずに呼び出しの結果を安全とマークしないでくださいescape()

彼らのアドバイスに従ってください!

HTMLParserでタグを取り除くには、タグを複数回実行する必要があります。

この質問に対するトップ回答を簡単に回避することは簡単です。

この文字列を見てください(ソースとディスカッション):

<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バージョンに高速化されています)。Google App Engineに含まれており、Jinja2(2.7以降)で使用されています、Mako、Pylonsています。Django 1.7のDjangoテンプレートで簡単に動作します。

Djangoのstrip_tagsやその他の最新バージョンのhtmlユーティリティは優れていますが、MarkupSafeほど便利ではありません。それらはかなり自己完結型であり、必要なものをこのファイルからコピーできます

ほとんどすべてのタグを取り除く必要がある場合は、Bleachライブラリが最適です。「ユーザーはイタリックにすることはできますが、iframeを作成することはできません」などのルールを適用することができます。

タグストリッパーの特性を理解しましょう!ファズテストを実行してください! これが、この回答の調査に使用したコードです。

sheepish note-質問自体はコンソールへの印刷に関するものですが、これは「文字列からのpythonストリップHTML」のGoogleの上位の結果です。そのため、この答えはWebに関する99%です。


私の「代替の最終行」のサンプルコードはHTMLエンティティを処理しません-それはどれほど悪いですか?
rescdsk 2015年

私は特別なタグなしでhtmlの小さなチャンクを解析しているだけで、短いバージョンがうまく機能します。共有してくれてありがとう!
tbolender

31

タグ取り除き、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が受け入れることができるUnicode範囲外のコードポイントを指定する数値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エスケープは必要ありません。)

Python 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で改善されていることに注意してください(つまり、コードが少なくなり、エラー処理が改善されます)。


18

これには簡単な方法があります:

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

PS-クラスに興味がある場合(Pythonによるスマートデバッグについて)、リンクを提供します:http : //www.udacity.com/overview/Course/cs259/CourseRev/1。それは無料です!

どういたしまして!:)


2
なぜこの回答は反対投票になったのでしょうか。libなしで問題を解決する簡単な方法です。純粋なpythonであり、リンクに示されているように機能します。
Medeiros

2
おそらく人々は彼らに安全を与えるために図書館を好む。私はあなたのコードをテストして合格しましたが、私は常に、libを使用してバグがポップアップするまで問題ないと想定するよりも、理解できる小さなコードを好みます。私にとってそれは私が探していたものであり、もう一度感謝します。反対票に関しては、その考え方にとらわれないでください。ここの人々は投票ではなく質に気を配るべきです。最近、SOは誰もが知識ではなくポイントを欲する場所になっています。
ジミー・ケイン

2
このソリューションの問題は、エラー処理です。たとえば<b class="o'>x</b>、入力として関数outputを指定するとしますx。しかし、実際にはこの入力は無効です。それが人々が図書館を好む理由だと思います。
laltin 2013

1
その入力でも動作します。ちょうどテストした。これらのライブラリー内に同様のコードがあることを理解してください。それはあまりパイソンではない、私は知っている。CまたはJavaコードのように見えます。私はそれが効率的で簡単に別の言語に移植できると思います。
Medeiros 2013

1
シンプルでPython的で、議論されている他のどの方法よりもうまく機能しているようです。一部の形式が正しくないHTMLでは機能しない可能性がありますが、それを克服することはできません。
デンソン2017

16

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

13

すべてのHTMLタグを削除したい場合、私が見つけた最も簡単な方法はBeautifulSoupを使用することです。

from bs4 import BeautifulSoup  # Or from BeautifulSoup import BeautifulSoup

def stripHtmlTags(htmlTxt):
    if htmlTxt is None:
            return None
        else:
            return ''.join(BeautifulSoup(htmlTxt).findAll(text=True)) 

受け入れられた回答のコードを試してみましたが、上記のコードブロックでは発生しなかった「RuntimeError:最大再帰深度を超えました」が発生しました。


1
私はあなたの方法を試してみましたが、それは見た目がすっきりしていて、うまくいったので、入力タグを取り除きませんでした!
kustomrtr 2013年

BeautifulSoupの単純なアプリケーションには、空白に問題があることがわかりました''.join(BeautifulSoup('<em>he</em>llo<br>world').find_all(text=True))。ここでは出力は「helloworld」ですが、おそらく「hello world」にしたいでしょう。' '.join(BeautifulSoup('<em>he</em>llo<br>world').find_all(text=True))「彼は世界」になるので、助けにはならない。
FinnÅrupNielsen 2014

@kustomrtr、ご無沙汰して申し訳ありませんが、私は自己主張に何を入れますか?NameError:名前 'self'は定義されていません
Ian_De_Oliveira

@Ian_De_Oliveiraあなたはそれを削除することができます、私はそれがクラスの中にあると仮定しましたが、それは必要ではありません。回答を編集して削除しました
Vasilis

@Ian_De_Oliveiraあなたはそれを削除することができます、私はそれがクラスの中にあると仮定しましたが、それは必要ではありません。回答を編集して削除しました
Vasilis

10

HTMLタグを取り除き、驚くほど高速なlxmlライブラリに基づいてHTMLエンティティをデコードする簡単なソリューションを次に示します。

from lxml import html

def strip_html(s):
    return str(html.fromstring(s).text_content())

strip_html('Ein <a href="">sch&ouml;ner</a> Text.')  # Output: Ein schöner Text.

3
2020年の時点で、これはHTMLのコンテンツをストライプ化するための最速かつ最良の方法でした。さらに、デコード処理のボーナス。言語検出に最適!
dfabiano

text_content()戻ってlxml.etree._ElementUnicodeResultあなたが最初の文字列にキャストする必要がある場合がありますので
Suzana

1
@Suzana良い点。and indexingのstrような文字列操作の場合+、自動キャストされるよう[]です。とにかく良さそうなキャストを追加しました。
ロビンディナー

9

lxml.htmlベースのソリューション(lxmlのは、ネイティブライブラリ、したがって、はるかに高速任意の純粋なPythonのソリューションよりもあります)。

from lxml import html
from lxml.html.clean import clean_html

tree = html.fromstring("""<span class="item-summary">
                            Detailed answers to any questions you might have
                        </span>""")

print(clean_html(tree).strip())

# >>> Detailed answers to any questions you might have

lxml.cleanerが正確に行うことについては、http: //lxml.de/lxmlhtml.html#cleaning-up-html も参照してください

テキストに変換する前に、何をサニタイズするかをより細かく制御する必要がある場合は、コンストラクターで必要なオプションを渡してlxml Cleanerを明示的に使用することができます。例:

cleaner = Cleaner(page_structure=True,
                  meta=True,
                  embedded=True,
                  links=True,
                  style=True,
                  processing_instructions=True,
                  inline_style=True,
                  scripts=True,
                  javascript=True,
                  comments=True,
                  frames=True,
                  forms=True,
                  annoying_tags=True,
                  remove_unknown_tags=True,
                  safe_attrs_only=True,
                  safe_attrs=frozenset(['src','color', 'href', 'title', 'class', 'name', 'id']),
                  remove_tags=('span', 'font', 'div')
                  )
sanitized_html = cleaner.clean_html(unsafe_html)

1
「AttributeError: 'HtmlElement' object has no attribute 'strip'
aris

7

Beautiful Soupパッケージはこれをすぐに行います。

from bs4 import BeautifulSoup

soup = BeautifulSoup(html)
text = soup.get_text()
print(text)

3
レビューキューから:回答の前後にコンテキストを追加してください。コードのみの回答は理解が困難です。投稿に情報を追加できれば、質問者と将来の読者の両方に役立ちます。
help-info.de 2017年

2

これがPython 3の私の解決策です。

import html
import re

def html_to_txt(html_text):
    ## unescape html
    txt = html.unescape(html_text)
    tags = re.findall("<[^>]+>",txt)
    print("found tags: ")
    print(tags)
    for tag in tags:
        txt=txt.replace(tag,'')
    return txt

それが完璧かどうかはわかりませんが、私のユースケースを解決し、簡単に思えます。


2

別のHTMLパーサー(lxmlBeautiful Soupなど)を使用できます。これは、テキストのみを抽出する機能を提供するものです。または、タグを取り除くラインストリングで正規表現を実行できます。詳細については、Pythonのドキュメントをご覧ください。


1
amkリンクが停止しています。代替物を手に入れましたか?

2
PythonのWebサイトには今、良いハウツーがあります。これが正規表現のハウツーです:docs.python.org/howto/regex
Jason Coon

5
lxmlの場合:lxml.html.fromstring(s).text_content()
Bluu

1
lxmlを使用したBluuの例は、HTMLエンティティ(例:)&amp;をテキストにデコードします。
セーレンLøvborg

1

Eloffの答えをPython 3.1でうまく利用しました[どうもありがとう!]

Python 3.2.3にアップグレードしたところ、エラーが発生しました。

レスポンダThomas Kのおかげでここに提供された解決策はsuper().__init__()、次のコードに挿入することです:

def __init__(self):
    self.reset()
    self.fed = []

...次のようにするために:

def __init__(self):
    super().__init__()
    self.reset()
    self.fed = []

...そしてそれはPython 3.2.3で動作します。

繰り返しになりますが、修正とEloffの元のコードを提供してくれたThomas Kに感謝します!


1

独自の関数を書くことができます:

def StripTags(text):
     finished = 0
     while not finished:
         finished = 1
         start = text.find("<")
         if start >= 0:
             stop = text[start:].find(">")
             if stop >= 0:
                 text = text[:start] + text[start+stop+1:]
                 finished = 0
     return text

1
文字列に追加すると、文字列の新しいコピーが作成されますか?
ジェレミーL

1
@Nerdling-はい、あります。これは、頻繁に使用される関数(または、さらに言えば、テキストの大きな塊に作用する使用頻度の低い関数)にかなりの非効率性をもたらす可能性があります。詳細については、このページを参照しください。:D
ジェレミーサンデル

引用された文字列に対してテストしますか?いいえ
ジミー・ケイン

1

HTML-Parserを使用したソリューションは、一度しか実行されない場合、すべて破壊可能です。

html_to_text('<<b>script>alert("hacked")<</b>/script>

結果は:

<script>alert("hacked")</script>

あなたが防ぐつもりのもの。HTMLパーサーを使用する場合は、ゼロが置き換えられるまでタグを数えます。

from HTMLParser import HTMLParser

class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.fed = []
        self.containstags = False

    def handle_starttag(self, tag, attrs):
       self.containstags = True

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

    def has_tags(self):
        return self.containstags

    def get_data(self):
        return ''.join(self.fed)

def strip_tags(html):
    must_filtered = True
    while ( must_filtered ):
        s = MLStripper()
        s.feed(html)
        html = s.get_data()
        must_filtered = s.has_tags()
    return html

1
呼び出された関数を呼び出し、html_to_textその関数から出力されたテキストをエスケープせずにhtml内に埋め込む場合、それはエスケープの欠如であり、これはhtml_to_text関数ではなくセキュリティの脆弱性です。html_to_textこの関数は、出力がテキストだろうあなたを約束したことはありません。また、エスケープせずにHTMLにテキストを挿入することは、テキストをhtml_to_text 他のソースから取得したかどうかに関係なく、潜在的なセキュリティの脆弱性です。
kasperd 14

あなたはエスケープの欠如の場合に正しいですが、問題は、与えられた文字列をエスケープしないように与えられた文字列からhtmlを取り除くことでした。以前の回答がいくつかのhtmlを削除した結果としてソリューションを使用して新しいhtmlを構築する場合、このソリューションの使用は危険です。
フォークNisius

1

これは簡単な修正であり、さらに最適化できますが、正常に動作します。このコードは、空でないすべてのタグを ""で置き換え、指定された入力テキストからすべてのhtmlタグを取り除きます。./file.pyinput outputを使用して実行できます。

    #!/usr/bin/python
import sys

def replace(strng,replaceText):
    rpl = 0
    while rpl > -1:
        rpl = strng.find(replaceText)
        if rpl != -1:
            strng = strng[0:rpl] + strng[rpl + len(replaceText):]
    return strng


lessThanPos = -1
count = 0
listOf = []

try:
    #write File
    writeto = open(sys.argv[2],'w')

    #read file and store it in list
    f = open(sys.argv[1],'r')
    for readLine in f.readlines():
        listOf.append(readLine)         
    f.close()

    #remove all tags  
    for line in listOf:
        count = 0;  
        lessThanPos = -1  
        lineTemp =  line

            for char in lineTemp:

            if char == "<":
                lessThanPos = count
            if char == ">":
                if lessThanPos > -1:
                    if line[lessThanPos:count + 1] != '<>':
                        lineTemp = replace(lineTemp,line[lessThanPos:count + 1])
                        lessThanPos = -1
            count = count + 1
        lineTemp = lineTemp.replace("&lt","<")
        lineTemp = lineTemp.replace("&gt",">")                  
        writeto.write(lineTemp)  
    writeto.close() 
    print "Write To --- >" , sys.argv[2]
except:
    print "Help: invalid arguments or exception"
    print "Usage : ",sys.argv[0]," inputfile outputfile"

1

søren-løvborgの答えのpython 3への適合

from html.parser import HTMLParser
from html.entities import html5

class HTMLTextExtractor(HTMLParser):
    """ Adaption of http://stackoverflow.com/a/7778368/196732 """
    def __init__(self):
        super().__init__()
        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):
        if name in html5:
            self.result.append(unichr(html5[name]))

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

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

1

1つのプロジェクトでは、HTMLだけでなくcssおよびjsも必要でした。したがって、私はEloffsの回答のバリエーションを作りました:

class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.strict = False
        self.convert_charrefs= True
        self.fed = []
        self.css = False
    def handle_starttag(self, tag, attrs):
        if tag == "style" or tag=="script":
            self.css = True
    def handle_endtag(self, tag):
        if tag=="style" or tag=="script":
            self.css=False
    def handle_data(self, d):
        if not self.css:
            self.fed.append(d)
    def get_data(self):
        return ''.join(self.fed)

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

1

これは、現在受け入れられている回答(https://stackoverflow.com/a/925630/95989)に似たソリューションですが、内部のHTMLParserクラスを直接する(つまり、サブクラス化しない)ため、非常に簡潔になります。

def strip_html(text):
    パーツ= []                                                                      
    パーサー= HTMLParser()                                                           
    parser.handle_data = parts.append                                               
    parser.feed(text)                                                               
    戻り値 '' .join(parts)

0

私はGithubのreadmeを解析していますが、以下が本当にうまくいくことがわかりました。

import re
import lxml.html

def strip_markdown(x):
    links_sub = re.sub(r'\[(.+)\]\([^\)]+\)', r'\1', x)
    bold_sub = re.sub(r'\*\*([^*]+)\*\*', r'\1', links_sub)
    emph_sub = re.sub(r'\*([^*]+)\*', r'\1', bold_sub)
    return emph_sub

def strip_html(x):
    return lxml.html.fromstring(x).text_content() if x else ''

その後

readme = """<img src="https://raw.githubusercontent.com/kootenpv/sky/master/resources/skylogo.png" />

            sky is a web scraping framework, implemented with the latest python versions in mind (3.4+). 
            It uses the asynchronous `asyncio` framework, as well as many popular modules 
            and extensions.

            Most importantly, it aims for **next generation** web crawling where machine intelligence 
            is used to speed up the development/maintainance/reliability of crawling.

            It mainly does this by considering the user to be interested in content 
            from *domains*, not just a collection of *single pages*
            ([templating approach](#templating-approach))."""

strip_markdown(strip_html(readme))

すべてのマークダウンとHTMLを正しく削除します。


0

BeautifulSoup、html2text、または@Eloffのコードを使用すると、ほとんどの場合、いくつかのHTML要素、JavaScriptコードが残ります...

したがって、これらのライブラリの組み合わせを使用して、マークダウンフォーマットを削除できます(Python 3)。

import re
import html2text
from bs4 import BeautifulSoup
def html2Text(html):
    def removeMarkdown(text):
        for current in ["^[ #*]{2,30}", "^[ ]{0,30}\d\\\.", "^[ ]{0,30}\d\."]:
            markdown = re.compile(current, flags=re.MULTILINE)
            text = markdown.sub(" ", text)
        return text
    def removeAngular(text):
        angular = re.compile("[{][|].{2,40}[|][}]|[{][*].{2,40}[*][}]|[{][{].{2,40}[}][}]|\[\[.{2,40}\]\]")
        text = angular.sub(" ", text)
        return text
    h = html2text.HTML2Text()
    h.images_to_alt = True
    h.ignore_links = True
    h.ignore_emphasis = False
    h.skip_internal_links = True
    text = h.handle(html)
    soup = BeautifulSoup(text, "html.parser")
    text = soup.text
    text = removeAngular(text)
    text = removeMarkdown(text)
    return text

それは私にとってはうまくいきますが、もちろん強化することができます...


0

シンプルなコード!。これにより、すべての種類のタグとその中のコンテンツが削除されます。

def rm(s):
    start=False
    end=False
    s=' '+s
    for i in range(len(s)-1):
        if i<len(s):
            if start!=False:
                if s[i]=='>':
                    end=i
                    s=s[:start]+s[end+1:]
                    start=end=False
            else:
                if s[i]=='<':
                    start=i
    if s.count('<')>0:
        self.rm(s)
    else:
        s=s.replace('&nbsp;', ' ')
        return s

ただし、テキスト内に<>記号が含まれている場合、完全な結果は得られません。


0
# This is a regex solution.
import re
def removeHtml(html):
  if not html: return html
  # Remove comments first
  innerText = re.compile('<!--[\s\S]*?-->').sub('',html)
  while innerText.find('>')>=0: # Loop through nested Tags
    text = re.compile('<[^<>]+?>').sub('',innerText)
    if text == innerText:
      break
    innerText = text

  return innerText.strip()

-2

この方法は私にとって問題なく機能し、追加のインストールは必要ありません。

import re
import htmlentitydefs

def convertentity(m):
    if m.group(1)=='#':
        try:
            return unichr(int(m.group(2)))
        except ValueError:
            return '&#%s;' % m.group(2)
        try:
            return htmlentitydefs.entitydefs[m.group(2)]
        except KeyError:
            return '&%s;' % m.group(2)

def converthtml(s):
    return re.sub(r'&(#?)(.+?);',convertentity,s)

html =  converthtml(html)
html.replace("&nbsp;", " ") ## Get rid of the remnants of certain formatting(subscript,superscript,etc).

3
これはHTMLエンティティをプレーンテキストにデコードしますが、元の質問であるタグを実際には取り除きません。(また、2番目のtry-exceptブロックのインデントを解除して、コードがさらに多くのことを実行できるようにする必要もあります)。
セーレンLøvborg
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.