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>'
が見つかった場合、「some text」のみを出力し、'<b>hello</b>'
は「hello」などを出力します。これを行うにはどうすればよいでしょうか?
Python stdlibのみを必要とするため、私は常にこの関数を使用してHTMLタグを除去しました。
Python 2で
from HTMLParser import HTMLParser
class MLStripper(HTMLParser):
def __init__(self):
self.reset()
self.fed = []
def handle_data(self, d):
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()
Python 3の場合
from html.parser import HTMLParser
class MLStripper(HTMLParser):
def __init__(self):
self.reset()
self.strict = False
self.convert_charrefs= True
self.fed = []
def handle_data(self, d):
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()
注:これは3.1のみで機能します。 3.2以降では、親クラスのinit関数を呼び出す必要があります。 Python 3.2でのHTMLParserの使用 を参照してください
見落としがちなケースについてはあまり考えていませんが、簡単な正規表現を実行できます。
re.sub('<[^<]+?>', '', text)
正規表現を理解していない人のために、これは文字列<...>
を検索します。ここで、内部コンテンツは+
ではない1つ以上の(<
)文字で構成されます。 ?
は、見つけることができる最小の文字列と一致することを意味します。たとえば、<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)
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
sを浮かせたままにせずに、人々を<i>italicizing</i>
から守ることは一つのことです。しかし、任意の入力を取得して完全に無害にすることは別です。このページのテクニックのほとんどは、閉じられていないコメント(<!--
)やタグの一部ではない山括弧(blah <<<><blah
)などをそのまま残します。 HTMLParserバージョンは、閉じられていないコメント内にある場合、完全なタグを残すことさえできます。
テンプレートが{{ firstname }} {{ lastname }}
の場合はどうなりますか? firstname = '<a'
とlastname = 'href="http://evil.com/">'
は、このページのすべてのタグストリッパー(@Medeirosを除く!)によって許可されます。これらは、独自の完全なタグではないためです。通常のHTMLタグを取り除くだけでは十分ではありません。
Djangoのstrip_tags
は、この質問に対するトップアンサーの改良版(次の見出しを参照)であり、次の警告を提供します。
結果の文字列がHTMLセーフであるという保証はまったくありません。したがって、たとえば
escape()
を使用して、最初にエスケープせずにstrip_tags
呼び出しの結果を安全にマークしないでください。
彼らのアドバイスに従ってください!
この質問に対するトップアンサーを回避するのは簡単です。
この文字列を見てください( ソースとディスカッション ):
<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 ライブラリから取得されています。便利で高速です(ネイティブPythonバージョンへのCの高速化により)。 Google App Engine に含まれており、 Jinja2(2.7以降) 、Mako、Pylonsなどで使用されています。 Django 1.7のDjangoテンプレートで簡単に動作します。
Djangoのstrip_tagsと他のhtmlユーティリティの最新バージョンは優れていますが、MarkupSafeほど便利ではありません。これらはかなり自己完結型で、必要なものを このファイル からコピーできます。
すべてのタグをalmost除去する必要がある場合は、 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>(¬ \u0394ημώ)</em></a>'
print repr(html_to_text(html))
結果:
u'Demo (\xac \u0394\u03b7\u03bc\u03ce)'
エラー処理:
&#apos;
など、XMLおよびXHTMLで有効ですが、プレーンHTMLではない)は、ValueError
例外を発生させます。ValueError
例外を発生させます。セキュリティ上の注意:HTML除去(HTMLをプレーンテキストに変換)とHTMLサニタイズ(プレーンテキストをHTMLに変換)を混同しないでください。この答えは、HTMLを削除し、エンティティをプレーンテキストにデコードします。これにより、結果をHTMLコンテキストで安全に使用できなくなります。
例:<script>alert("Hello");</script>
は<script>alert("Hello");</script>
に変換されます。これは100%正しい動作ですが、結果のプレーンテキストがそのままHTMLページに挿入される場合は明らかに不十分です。
ルールは難しくありません:いつでもプレーンテキスト文字列をHTML出力に挿入する場合、alwaysHTML escape( cgi.escape(s, True)
を使用して、HTMLが含まれていないことを「知っている」場合でも(たとえば、HTMLコンテンツを削除したため)。
(ただし、OPは結果をコンソールに出力するように要求しました。この場合、HTMLエスケープは必要ありません。)
Python 3.4以降のバージョン:(with 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>(¬ \u0394ημώ)</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('<script>alert("Hello");</script>')
'<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 < z <!--b')
'x < y < z '
Unrecognized named entities are included as-is. ''' is recognized,
despite being XML only.
>>> html_to_text('&nosuchentity; ' ')
"&nosuchentity; ' "
"""
s = HTMLTextExtractor()
s.feed(html)
return s.get_text()
Python 3ではHTMLParserが改善されていることに注意してください(つまり、コードが少なくなり、エラー処理が改善されます)。
これには簡単な方法があります:
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 。それは無料です!
どういたしまして! :)
HTMLエンティティ(つまり_&
)を保持する必要がある場合、「handle_entityref」メソッドを Eloffの答え に追加しました。
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()
すべての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:maximum recursion depth exceeded」を取得していましたが、上記のコードブロックでは発生しませんでした。
lxml.html -basedソリューション(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
http://lxml.de/lxmlhtml.html#cleaning-up-html を参照して、lxml.cleanerの正確な動作を確認してください。
テキストに変換する前に何をサニタイズするかを厳密に制御する必要がある場合は、コンストラクタで 希望するオプション を渡すことで 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)
Beautiful Soupパッケージはこれをすぐに行います。
from bs4 import BeautifulSoup
soup = BeautifulSoup(html)
text = soup.get_text()
print(text)
別のHTMLパーサー( lxml 、または Beautiful Soup )を使用できます。これは、テキストのみを抽出する機能を提供するものです。または、タグを取り除くライン文字列で正規表現を実行できます。詳細については、 http://www.amk.ca/python/howto/regex/ を参照してください。
あるプロジェクトでは、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()
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()
HTML-Parserを使用したソリューションは、実行が1回のみの場合、すべて壊れやすくなります。
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
これは簡単な修正であり、さらに最適化できますが、問題なく動作します。このコードは、空ではないすべてのタグを「」で置き換え、指定された入力テキストからすべてのhtmlタグを取り除きます。/file.py input 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("<","<")
lineTemp = lineTemp.replace(">",">")
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"
Python 3.1でEloffの答えをうまく使いました[本当にありがとう!]。
Python 3.2.3にアップグレードすると、エラーが発生しました。
here 応答者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に感謝します!
これは、現在受け入れられている回答( https://stackoverflow.com/a/925630/95989 )に似たソリューションです。ただし、内部HTMLParser
クラスを直接使用する(つまり、サブクラス化しない)ことを除きます。はるかに簡潔:
def strip_html(text): parts = [] parser = HTMLParser() parser.handle_data = parts.append parser.feed (テキスト) return '' .join(parts)
独自の関数を作成できます。
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
これが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
それが完璧かどうかはわかりませんが、私のユースケースを解決し、シンプルに見えます。
シンプルなコード!これにより、その中のすべての種類のタグとコンテンツが削除されます。
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(' ', ' ')
return s
ただし、テキストに<>記号が含まれている場合、完全な結果は得られません。
私は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を正しく削除します。
hext
は、とりわけHTML strip が可能なパッケージです。 beautifulsoup
の代替です。以下はhext==0.2.3
でテストされました。
これをユーティリティモジュールに保存します。 util/hext.py
:
import hext
_HTML_TEXT_RULE = hext.Rule('<html @text:text />')
def html_to_text(text: str) -> str:
# Ref: https://stackoverflow.com/a/56894409/
return _HTML_TEXT_RULE.extract(hext.Html(f'<html>{text}</html>'))[0]['text']
使用例:
>>> from .util.hext import html_to_text
>>> html_to_text('<b>Hello world!</b>')
'Hello world!'
>>> html_to_text('<a href="google.com">some text</a>')
'some text'
>>> html_to_text('<span class="small-caps">l</span>-arginine minimizes immunosuppression and prothrombin time and enhances the genotoxicity of 5-fluorouracil in rats')
'l-arginine minimizes immunosuppression and prothrombin time and enhances the genotoxicity of 5-fluorouracil in rats'
>>> html_to_text('Attenuation of diabetic nephropathy by dietary fenugreek (<em>Trigonella foenum-graecum</em>) seeds and onion (<em>Allium cepa</em>) <em>via</em> suppression of glucose transporters and renin-angiotensin system')
'Attenuation of diabetic nephropathy by dietary fenugreek (Trigonella foenum-graecum) seeds and onion (Allium cepa) via suppression of glucose transporters and renin-angiotensin system'
不正なHTMLの使用例:
>>> html_to_text('<b>Hello <i>world!')
'Hello world!'
>>> html_to_text('<a href="google.com">some <faketag>text')
'some text'
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
それは私にはうまくいきますが、もちろん強化することができます...