Извлечение текста из файла HTML с использованием Python
Я хотел бы извлечь текст из файла HTML, используя Python. По сути, я хочу получить такой же вывод, как если бы я скопировал текст из браузера и вставил его в блокнот.
Мне бы хотелось что-то более надежное, чем использование регулярных выражений, которые могут не работать на плохо сформированном HTML. Я видел, как многие люди рекомендуют Beautiful Soup, но у меня было несколько проблем с его использованием. С одной стороны, он поднял нежелательный текст, такой как источник JavaScript. Кроме того, он не интерпретирует HTML-сущности. Например, я ожидал бы & # 39; в исходном тексте HTML для преобразования в апостроф в тексте, как если бы я вставил содержимое браузера в блокнот.
Обновить html2text
выглядит многообещающе Он обрабатывает HTML-объекты правильно и игнорирует JavaScript. Тем не менее, он точно не дает простой текст; он производит уценку, которая затем должна быть превращена в простой текст. Он не содержит примеров или документации, но код выглядит чистым.
Смежные вопросы:
36 ответов
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
Я потратил 4-5 часов на решение проблем с html2text. К счастью, я мог столкнуться с НЛТК.
Это работает волшебно.
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 __name__ == '__main__':
main()
Я знаю, что ответов уже много, но наиболее изящное и питонное решение, которое я нашел, частично описано здесь.
from bs4 import BeautifulSoup
text = ''.join(BeautifulSoup(some_html_string, "html.parser").findAll(text=True))
Обновить
Основываясь на комментарии Фрейзера, вот более элегантное решение:
from bs4 import BeautifulSoup
clean_text = ''.join(BeautifulSoup(some_html_string, "html.parser").stripped_strings)
Вот вариант ответа xperroni, который является более полным. Он пропускает разделы сценариев и стилей и переводит charrefs (например, & # 39;) и объекты 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)
Я знаю, что здесь уже есть множество ответов, но я думаю, что magazine3k также заслуживает упоминания. Недавно мне нужно было выполнить аналогичную задачу по извлечению текста из статей в Интернете, и эта библиотека отлично справилась с этой задачей в своих тестах. Он игнорирует текст, найденный в пунктах меню и боковых панелях, а также любой JavaScript, который появляется на странице по запросу OP.
from newspaper import Article
article = Article(url)
article.download()
article.parse()
article.text
Если у вас уже есть загруженные файлы HTML, вы можете сделать что-то вроде этого:
article = Article('')
article.set_html(html)
article.parse()
article.text
Он даже имеет несколько функций НЛП для обобщения тем статей:
article.nlp()
article.summary
Вы также можете использовать метод 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
Если вам нужна большая скорость и меньшая точность, вы можете использовать raw 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()
PyParsing делает отличную работу. Вики PyParsing была убита, поэтому здесь есть еще одно место, где есть примеры использования PyParsing ( пример ссылки). Одна из причин, по которой стоит потратить немного времени на pyparsing, заключается в том, что он также написал очень краткое, очень хорошо организованное руководство по сокращенному использованию O'Reilly, которое также является недорогим.
Сказав это, я часто использую BeautifulSoup, и не так уж сложно разобраться с проблемами сущностей, вы можете конвертировать их перед запуском BeautifulSoup.
Удачи
Это не совсем решение Python, но оно преобразует текст, сгенерированный Javascript, в текст, что я считаю важным (например, google.com). Ссылки браузера (не 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()
Кто-нибудь пробовал bleach.clean(html,tags=[],strip=True)
с отбеливателем? это работает для меня.
Лучшая работа для меня это надписи.
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)
Результаты действительно хороши
Если вы хотите автоматически извлекать текстовые фрагменты с веб-страницы, есть несколько доступных пакетов Python, таких как Trafilatura. В рамках бенчмаркинга были сравнены несколько пакетов Python:
https://github.com/adbar/trafilatura#evaluation-and-alternatives
- html_текст https://github.com/TeamHG-Memex/html-текст
- inscriptis https://github.com/weblyzard/inscriptis
- газета3k
- простотекст
- бойлерпи3 https://github.com/jmriebold/BoilerPy3
- исходный уровень
- гусь3 https://github.com/goose3/goose3
- читабельность-lxml https://github.com/predatell/python-readability-lxml
- новости-пожалуйста https://github.com/fhamborg/news-пожалуйста
- читаемость https://github.com/alan-turing-institute/ReadabiliPy
- трафилатура
Вместо модуля HTMLParser, проверьте htmllib. У него похожий интерфейс, но он делает больше за вас. (Он довольно древний, поэтому он не очень помогает с точки зрения избавления от javascript и css. Вы можете создать производный класс, но и добавить методы с именами, такими как start_script и end_style (подробности см. В документации по python), но это сложно чтобы сделать это надежно для искаженного HTML.) В любом случае, вот что-то простое, что выводит на консоль простой текст
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)
Установить html2text используя
pip install 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!
Я рекомендую пакет Python с именем goose-extractor Goose попытается извлечь следующую информацию:
Основной текст статьи Главное изображение статьи Любые фильмы 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 ""
У меня был аналогичный вопрос, и я использовал один из ответов с BeautifulSoup. Проблема была в том, что это было очень медленно. В итоге я использовал библиотеку под названием selectolax. Он довольно ограничен, но подходит для этой задачи. Единственная проблема заключалась в том, что мне приходилось вручную удалять ненужные пробелы. Но похоже, что решение BeautifulSoup работает намного быстрее.
from selectolax.parser import HTMLParser
def get_text_selectolax(html):
tree = HTMLParser(html)
if tree.body is None:
return None
for tag in tree.css('script'):
tag.decompose()
for tag in tree.css('style'):
tag.decompose()
text = tree.body.text(separator='')
text = " ".join(text.split()) # this will remove all the whitespaces
return text
Другой вариант - запустить 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.
Еще одно решение, отличное от Python: Libre Office:
soffice --headless --invisible --convert-to txt input1.html
Причина, по которой я предпочитаю этот вариант другим, заключается в том, что каждый абзац HTML преобразуется в одну текстовую строку (без разрывов строк), что я и искал. Другие методы требуют последующей обработки. Рысь дает хороший результат, но не совсем то, что я искал. Кроме того, Libre Office можно использовать для конвертирования из любых форматов...
Хотя многие люди упоминали об использовании регулярных выражений для удаления тегов 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
Ответ @PeYoTIL, использующий BeautifulSoup и исключающий стиль и содержание скриптов, не сработал для меня. Я попробовал это с помощью decompose
вместо extract
но это все еще не сработало. Так что я создал свой собственный, который также форматирует текст, используя <p>
теги и замены <a>
теги со ссылкой href. Также справляется со ссылками внутри текста. Доступный в этой сущности со встроенным тестовым документом.
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
У меня были хорошие результаты с Apache Tika. Его целью является извлечение метаданных и текста из содержимого, поэтому соответствующий синтаксический анализатор настраивается соответствующим образом из коробки.
Tika может быть запущена как сервер, тривиально запустить / развернуть в контейнере Docker, и оттуда можно получить доступ через привязки Python.
В 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
Теперь вы можете напечатать переменную тела, и она будет в текстовом формате:) Если она достаточно хороша, было бы неплохо выбрать ее в качестве принятого ответа.
Другой пример использования BeautifulSoup4 в Python 2.7.9+
включает:
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(). Разбейте на строки и удалите начальные и конечные пробелы на каждой, затем разбейте несколько заголовков на строку каждый chunks = (фраза.strip() для строки в строках для фразы в line.split(" ")). Затем, используя text = '\n'.join, отбросьте пустые строки и, наконец, вернитесь как санкционированный utf-8.
Заметки:
Некоторые системы, на которых это работает, не работают с подключениями https:// из-за проблемы с SSL, вы можете отключить проверку, чтобы исправить эту проблему. Пример исправления: http://blog.pengyifan.com/how-to-fix-python-ssl-certificate_verify_failed/
Python <2.7.9 может иметь некоторые проблемы с запуском этого
text.encode('utf-8') может оставлять странную кодировку, возможно, вместо этого может потребоваться просто вернуть str(text).
Ответьте, используя Pandas, чтобы получить данные таблицы из HTML.
Если вы хотите быстро извлечь данные таблицы из HTML. Вы можете использовать функцию read_HTML, документы . Прежде чем использовать эту функцию, вы должны прочитать ошибки /проблемы, связанные с библиотеками разбора HTML парсеров BeautifulSoup4/html5lib/lxml.
import pandas as pd
http = r'https://www.ibm.com/docs/en/cmofz/10.1.0?topic=SSQHWE_10.1.0/com.ibm.ondemand.mp.doc/arsa0257.htm'
table = pd.read_html(http)
df = table[0]
df
Есть несколько вариантов, с которыми можно поиграться, см. здесь и здесьздесь .
Perl way (извини мама, я никогда не буду делать это в производстве).
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
Простым способом
import re
html_text = open('html_file.html').read()
text_filtered = re.sub(r'<(.*?)>', '', html_text)
этот код находит все части html_text, начинающиеся с '<' и заканчивающиеся '>', и заменяет все найденные пустой строкой