Pythonを使用してHTMLファイルからテキストを抽出したいのですが。ブラウザからテキストをコピーしてメモ帳に貼り付けた場合と基本的に同じ出力が必要です。
整形式ではないHTMLでは失敗する可能性がある正規表現を使用するよりも、もっと堅牢なものが欲しいのです。私は多くの人がBeautiful Soupを推薦するのを見ましたが、私はそれを使うことにいくつかの問題を抱えています。一つには、JavaScriptのソースのような不要なテキストを拾いました。また、HTMLエンティティを解釈しませんでした。たとえば、HTMLソースでは、ブラウザのコンテンツをメモ帳に貼り付けた場合と同様に、テキスト内のアポストロフィに変換されることを期待します。
更新html2text
は有望に見えます。 HTMLエンティティを正しく処理し、JavaScriptを無視します。ただし、プレーンテキストは正確には生成されません。それはそれから平文に変えられなければならないであろう値下げを生み出す。例やドキュメントは付属していませんが、コードはきれいに見えます。
関連する質問
html2text はこれでかなり良い仕事をするPythonプログラムです。
JavaScriptを使わずに、あるいは欲しくないものを使わずにテキストを抽出するために見つけた最高のコードです。
import urllib
from bs4 import BeautifulSoup
url = "http://news.bbc.co.uk/2/hi/health/2284783.stm"
html = urllib.urlopen(url).read()
soup = BeautifulSoup(html)
# kill all script and style elements
for script in soup(["script", "style"]):
script.extract() # rip it out
# get text
text = soup.get_text()
# break into lines and remove leading and trailing space on each
lines = (line.strip() for line in text.splitlines())
# break multi-headlines into a line each
chunks = (phrase.strip() for line in lines for phrase in line.split(" "))
# drop blank lines
text = '\n'.join(chunk for chunk in chunks if chunk)
print(text)
あなただけの前にBeautifulSoupをインストールする必要があります。
pip install beautifulsoup4
注:NTLKはclean_html
関数をサポートしなくなりました
以下の元の答え、およびコメントセクションの代替案。
NLTK を使う
私はhtml2textで問題を解決するために私の4-5時間を無駄にしました。幸いなことに、私はNLTKに遭遇する可能性があります。
それは魔法のように機能します。
import nltk
from urllib import urlopen
url = "http://news.bbc.co.uk/2/hi/health/2284783.stm"
html = urlopen(url).read()
raw = nltk.clean_html(html)
print(raw)
今日も同じ問題に直面していました。私はすべてのマークアップの入ってくるコンテンツを取り除き、残りのテキストを最小限のフォーマットで返すための非常に単純なHTMLパーサーを書きました。
from HTMLParser import HTMLParser
from re import sub
from sys import stderr
from traceback import print_exc
class _DeHTMLParser(HTMLParser):
def __init__(self):
HTMLParser.__init__(self)
self.__text = []
def handle_data(self, data):
text = data.strip()
if len(text) > 0:
text = sub('[ \t\r\n]+', ' ', text)
self.__text.append(text + ' ')
def handle_starttag(self, tag, attrs):
if tag == 'p':
self.__text.append('\n\n')
Elif tag == 'br':
self.__text.append('\n')
def handle_startendtag(self, tag, attrs):
if tag == 'br':
self.__text.append('\n\n')
def text(self):
return ''.join(self.__text).strip()
def dehtml(text):
try:
parser = _DeHTMLParser()
parser.feed(text)
parser.close()
return parser.text()
except:
print_exc(file=stderr)
return text
def main():
text = r'''
<html>
<body>
<b>Project:</b> DeHTML<br>
<b>Description</b>:<br>
This small script is intended to allow conversion from HTML markup to
plain text.
</body>
</html>
'''
print(dehtml(text))
if __== '__main__':
main()
これはxperroniの答えのバージョンで、もう少し完全です。スクリプトやスタイルのセクションをスキップし、文字(例: ')とHTMLエンティティ(例&amp;)を翻訳します。
それはまた平凡なプレーンテキストからhtmlへの逆変換も含みます。
"""
HTML <-> text conversions.
"""
from HTMLParser import HTMLParser, HTMLParseError
from htmlentitydefs import name2codepoint
import re
class _HTMLToText(HTMLParser):
def __init__(self):
HTMLParser.__init__(self)
self._buf = []
self.hide_output = False
def handle_starttag(self, tag, attrs):
if tag in ('p', 'br') and not self.hide_output:
self._buf.append('\n')
Elif tag in ('script', 'style'):
self.hide_output = True
def handle_startendtag(self, tag, attrs):
if tag == 'br':
self._buf.append('\n')
def handle_endtag(self, tag):
if tag == 'p':
self._buf.append('\n')
Elif tag in ('script', 'style'):
self.hide_output = False
def handle_data(self, text):
if text and not self.hide_output:
self._buf.append(re.sub(r'\s+', ' ', text))
def handle_entityref(self, name):
if name in name2codepoint and not self.hide_output:
c = unichr(name2codepoint[name])
self._buf.append(c)
def handle_charref(self, name):
if not self.hide_output:
n = int(name[1:], 16) if name.startswith('x') else int(name)
self._buf.append(unichr(n))
def get_text(self):
return re.sub(r' +', ' ', ''.join(self._buf))
def html_to_text(html):
"""
Given a piece of HTML, return the plain text it contains.
This handles entities and char refs, but not javascript and stylesheets.
"""
parser = _HTMLToText()
try:
parser.feed(html)
parser.close()
except HTMLParseError:
pass
return parser.get_text()
def text_to_html(text):
"""
Convert the given text to html, wrapping what looks like URLs with <a> tags,
converting newlines to <br> tags and converting confusing chars into html
entities.
"""
def f(mo):
t = mo.group()
if len(t) == 1:
return {'&':'&', "'":''', '"':'"', '<':'<', '>':'>'}.get(t)
return '<a href="%s">%s</a>' % (t, t)
return re.sub(r'https?://[^] ()"\';]+|[&\'"<>]', f, text)
私はすでにたくさんの答えがあることを知っています、しかし最も優しいとPythonic私が見つけた解決策は、部分的に説明されています ここ 。
from bs4 import BeautifulSoup
text = ''.join(BeautifulSoup(some_html_string, "html.parser").findAll(text=True))
Fraserのコメントに基づいて、これはもっとエレガントな解決策です。
from bs4 import BeautifulSoup
clean_text = ''.join(BeautifulSoup(some_html_string, "html.parser").stripped_strings)
ストリップグラムライブラリでもhtml2textメソッドを使用できます。
from stripogram import html2text
text = html2text(your_html_string)
ストリップグラムをインストールするには、Sudo easy_install stripogramを実行します。
データマイニング用のパターンライブラリがあります。
http://www.clips.ua.ac.be/pages/pattern-web
どのタグを保持するかを決めることもできます。
s = URL('http://www.clips.ua.ac.be').download()
s = plaintext(s, keep={'h1':[], 'h2':[], 'strong':[], 'a':['href']})
print s
PyParsingは素晴らしい仕事をします。 PyParsing wikiが殺されたので、ここにPyParsingの使用例がある別の場所があります( example link )。構文解析に少し時間を費やす理由の1つは、非常によく整理されたO'Reilly Short Cutのマニュアルも安価であることを書いていることです。
とは言っても、私はBeautifulSoupを多用しており、エンティティの問題に対処するのはそれほど難しくありません。BeautifulSoupを実行する前にそれらを変換することができます。
がんばろう
あなたがより速い速度とより低い正確さを必要とするなら、あなたは生のlxmlを使うことができます。
import lxml.html as lh
from lxml.html.clean import clean_html
def lxml_to_text(html):
doc = lh.fromstring(html)
doc = clean_html(doc)
return doc.text_content()
install html2text using
ピップインストールhtml2text
それから、
>>> import html2text
>>>
>>> h = html2text.HTML2Text()
>>> # Ignore converting links from HTML
>>> h.ignore_links = True
>>> print h.handle("<p>Hello, <a href='http://earth.google.com/'>world</a>!")
Hello, world!
HTMLParserモジュールの代わりに、htmllibを調べてください。それは同様のインターフェースを持っていますが、あなたのためにもっと仕事をします。 (それはかなり古く、javascriptやcssを取り除くという意味ではあまり役に立ちません。派生クラスを作成することはできますが、start_scriptやend_styleのような名前のメソッドを追加することもできます(詳細はpythonのドキュメントを参照))とにかく、これはプレーンテキストをコンソールに出力する簡単なものです。
from htmllib import HTMLParser, HTMLParseError
from formatter import AbstractFormatter, DumbWriter
p = HTMLParser(AbstractFormatter(DumbWriter()))
try: p.feed('hello<br>there'); p.close() #calling close is not usually needed, but let's play it safe
except HTMLParseError: print ':(' #the html is badly malformed (or you found a bug)
これはまさにPythonのソリューションではありませんが、Javascriptが生成するテキストをテキストに変換します。ブラウザのLinks(Lynxではない)はJavascriptエンジンを持っており、-dumpオプションでソースをテキストに変換します。
それで、あなたは以下のようなことをすることができました:
fname = os.tmpnam()
fname.write(html_source)
proc = subprocess.Popen(['links', '-dump', fname],
stdout=subprocess.PIPE,
stderr=open('/dev/null','w'))
text = proc.stdout.read()
Goose-extractor GooseというPythonパッケージでは、次の情報を抽出することをお勧めします。
記事のメインテキスト記事のメイン画像記事に埋め込まれたすべてのYoutube/Vimeoムービーメタ説明メタタグ
美しいスープはHTMLエンティティを変換します。 HTMLにはバグが多く、UnicodeやHTMLのエンコーディングの問題でいっぱいであることを考えると、おそらくこれが最善の策です。これは私がHTMLを生のテキストに変換するのに使用するコードです:
import BeautifulSoup
def getsoup(data, to_unicode=False):
data = data.replace(" ", " ")
# Fixes for bad markup I've seen in the wild. Remove if not applicable.
masssage_bad_comments = [
(re.compile('<!-([^-])'), lambda match: '<!--' + match.group(1)),
(re.compile('<!WWWAnswer T[=\w\d\s]*>'), lambda match: '<!--' + match.group(0) + '-->'),
]
myNewMassage = copy.copy(BeautifulSoup.BeautifulSoup.MARKUP_MASSAGE)
myNewMassage.extend(masssage_bad_comments)
return BeautifulSoup.BeautifulSoup(data, markupMassage=myNewMassage,
convertEntities=BeautifulSoup.BeautifulSoup.ALL_ENTITIES
if to_unicode else None)
remove_html = lambda c: getsoup(c, to_unicode=True).getText(separator=u' ') if c else ""
私はすでにたくさんの答えがここにあることを知っています、しかし私は newspaper3k も言及に値すると思います。私は最近、Web上の記事からテキストを抽出するという同様のタスクを完了する必要がありました。このライブラリーは、これまでのところ私のテストでこれを達成するための優れた仕事をしました。メニュー項目やサイドバーにあるテキスト、およびOPが要求したときにページに表示されるJavaScriptは無視されます。
from newspaper import Article
article = Article(url)
article.download()
article.parse()
article.text
HTMLファイルが既にダウンロードされている場合は、次のようにすることができます。
article = Article('')
article.set_html(html)
article.parse()
article.text
記事のトピックを要約するためのNLP機能もいくつかあります。
article.nlp()
article.summary
その他の非Pythonソリューション:Libre Office:
soffice --headless --invisible --convert-to txt input1.html
私がこれを他の選択肢よりも好む理由は、すべてのHTML段落が1行のテキストに変換されるため(改行なし)、これが私が探していたものです。他の方法は後処理を必要とする。 LynxはNice出力を生成しますが、私が探していたものとは正確には異なりません。その上、Libre Officeはあらゆる種類のフォーマットから変換するために使用することができます...
Apache Tika で良い結果が得られました。その目的は、コンテンツからメタデータとテキストを抽出することです。そのため、基礎となるパーサーはそのまま使用できるように調整されています。
Tikaは server として実行することができ、Dockerコンテナで実行/デプロイするのは簡単です。そこから Pythonバインディング を介してアクセスできます。
別の選択肢は、テキストベースのWebブラウザを通してHTMLを実行してそれをダンプすることです。例えば(Lynxを使って):
lynx -dump html_to_convert.html > converted_html.txt
これは、Pythonスクリプト内で次のようにして実行できます。
import subprocess
with open('converted_html.txt', 'w') as outputFile:
subprocess.call(['lynx', '-dump', 'html_to_convert.html'], stdout=testFile)
HTMLファイルのテキストだけでは表示されませんが、ユースケースによってはhtml2textの出力よりも望ましい場合があります。
誰でもbleach.clean(html,tags=[],strip=True)
を bleach で試したことがありますか?それは私のために働いています。
BeautifulSoupを使用してスタイルとスクリプトの内容を削除した@ PeYoTILの答えは私にはうまくいきませんでした。 decompose
の代わりにextract
を使って試しましたが、まだうまくいきませんでした。それで、私は自分自身で<p>
タグを使ってテキストをフォーマットし、<a>
タグをhrefリンクに置き換える独自のものを作りました。テキスト内のリンクにも対応しています。テストドキュメントが埋め込まれた this Gist で入手可能です。
from bs4 import BeautifulSoup, NavigableString
def html_to_text(html):
"Creates a formatted text email message as a string from a rendered html template (page)"
soup = BeautifulSoup(html, 'html.parser')
# Ignore anything in head
body, text = soup.body, []
for element in body.descendants:
# We use type and not isinstance since comments, cdata, etc are subclasses that we don't want
if type(element) == NavigableString:
# We use the assumption that other tags can't be inside a script or style
if element.parent.name in ('script', 'style'):
continue
# remove any multiple and leading/trailing whitespace
string = ' '.join(element.string.split())
if string:
if element.parent.name == 'a':
a_tag = element.parent
# replace link text with the link
string = a_tag['href']
# concatenate with any non-empty immediately previous string
if ( type(a_tag.previous_sibling) == NavigableString and
a_tag.previous_sibling.string.strip() ):
text[-1] = text[-1] + ' ' + string
continue
Elif element.previous_sibling and element.previous_sibling.name == 'a':
text[-1] = text[-1] + ' ' + string
continue
Elif element.parent.name == 'p':
# Add extra paragraph formatting newline
string = '\n' + string
text += [string]
doc = '\n'.join(text)
return doc
Python 3.xでは、 'imaplib'および 'email'パッケージをインポートすることによって非常に簡単な方法でそれを行うことができます。これは古い記事ですが、私の答えがこの記事に関する新しい人に役立つことがあります。
status, data = self.imap.fetch(num, '(RFC822)')
email_msg = email.message_from_bytes(data[0][1])
#email.message_from_string(data[0][1])
#If message is multi part we only want the text version of the body, this walks the message and gets the body.
if email_msg.is_multipart():
for part in email_msg.walk():
if part.get_content_type() == "text/plain":
body = part.get_payload(decode=True) #to control automatic email-style MIME decoding (e.g., Base64, uuencode, quoted-printable)
body = body.decode()
Elif part.get_content_type() == "text/html":
continue
今、あなたはボディ変数を印刷することができます、そしてそれは平文フォーマットになるでしょう:)それがあなたにとって十分であるならばそれは受け入れられた答えとしてそれを選択するのはいいでしょう。
簡単な方法で
import re
html_text = open('html_file.html').read()
text_filtered = re.sub(r'<(.*?)>', '', html_text)
このコードは、 '<'で始まり '>'で終わるhtml_textのすべての部分を検索し、見つかったすべてを空の文字列で置き換えます。
私にとって一番うまくいったのは碑文です。
https://github.com/weblyzard/inscriptis
import urllib.request
from inscriptis import get_text
url = "http://www.informationscience.ch"
html = urllib.request.urlopen(url).read().decode('utf-8')
text = get_text(html)
print(text)
結果は本当に良いです
beautifulSoupを使用してHTMLからテキストのみを抽出できます
url = "https://www.geeksforgeeks.org/extracting-email-addresses-using-regular-expressions-python/"
con = urlopen(url).read()
soup = BeautifulSoup(con,'html.parser')
texts = soup.get_text()
print(texts)
多くの人が正規表現を使用してHTMLタグを削除することを述べていますが、多くの欠点があります。
例えば:
<p>hello world</p>I love you
解析する必要があります:
Hello world
I love you
これは私が思い付いたスニペットです、あなたはあなたの特定のニーズにそれをカスタマイズすることができます、そしてそれは魅力のように働きます
import re
import html
def html2text(htm):
ret = html.unescape(htm)
ret = ret.translate({
8209: ord('-'),
8220: ord('"'),
8221: ord('"'),
160: ord(' '),
})
ret = re.sub(r"\s", " ", ret, flags = re.MULTILINE)
ret = re.sub("<br>|<br />|</p>|</div>|</h\d>", "\n", ret, flags = re.IGNORECASE)
ret = re.sub('<.*?>', ' ', ret, flags=re.DOTALL)
ret = re.sub(r" +", " ", ret)
return ret
Python 2.7.9+でBeautifulSoup4を使用する別の例
includes:
import urllib2
from bs4 import BeautifulSoup
コード:
def read_website_to_text(url):
page = urllib2.urlopen(url)
soup = BeautifulSoup(page, 'html.parser')
for script in soup(["script", "style"]):
script.extract()
text = soup.get_text()
lines = (line.strip() for line in text.splitlines())
chunks = (phrase.strip() for line in lines for phrase in line.split(" "))
text = '\n'.join(chunk for chunk in chunks if chunk)
return str(text.encode('utf-8'))
説明:
Urlデータをhtmlとして(BeautifulSoupを使用して)読み込み、すべてのスクリプトおよびスタイル要素を削除し、.get_text()を使用してテキストのみを取得します。行に分割し、それぞれの先頭と末尾のスペースを削除してから、複数のヘッドラインを各チャンク=(phrase.strip()の行の行に対して、line.split( "")のフレーズの行に分割します。次に、text = '\ n'.joinを使用して空白行をドロップし、最終的に承認済みのutf-8として戻ります。
注:
これが実行される一部のシステムは、SSLの問題によりhttps://接続で失敗します。検証をオフにしてその問題を修正できます。修正例: http://blog.pengyifan.com/how-to-fix-python-ssl-certificate_verify_failed/
Python <2.7.9では、これを実行する際に問題が発生する可能性があります
text.encode( 'utf-8')は奇妙なエンコーディングを残すことができますが、代わりにstr(text)を返したいだけかもしれません。
Perlのやり方(すみません、ママ、私は実運用では決してやらないでしょう)。
import re
def html2text(html):
res = re.sub('<.*?>', ' ', html, flags=re.DOTALL | re.MULTILINE)
res = re.sub('\n+', '\n', res)
res = re.sub('\r+', '', res)
res = re.sub('[\t ]+', ' ', res)
res = re.sub('\t+', '\t', res)
res = re.sub('(\n )+', '\n ', res)
return res
これが私が定期的に使うコードです。
from bs4 import BeautifulSoup
import urllib.request
def processText(webpage):
# EMPTY LIST TO STORE PROCESSED TEXT
proc_text = []
try:
news_open = urllib.request.urlopen(webpage.group())
news_soup = BeautifulSoup(news_open, "lxml")
news_para = news_soup.find_all("p", text = True)
for item in news_para:
# SPLIT WORDS, JOIN WORDS TO REMOVE EXTRA SPACES
para_text = (' ').join((item.text).split())
# COMBINE LINES/PARAGRAPHS INTO A LIST
proc_text.append(para_text)
except urllib.error.HTTPError:
pass
return proc_text
それが役立つことを願っています。
アプリケーションはpythonマクロを使用できるため、LibreOfficeの作家のコメントにはメリットがあります。この質問に答えることと、LibreOfficeのマクロ基盤をさらに進めることの両方に、複数の利点があるようです。この解像度が、より大規模なプロダクションプログラムの一部として使用されるのではなく、1回限りの実装である場合は、HTMLをwriterで開き、ページをテキストとして保存すると、ここで説明した問題は解決します。