Ответ 1
html2text - это программа на Python, которая хорошо справляется с этой задачей.
Я хочу извлечь текст из файла HTML с помощью Python. Я хочу, по сути, тот же вывод, который я получил бы, если бы скопировал текст из браузера и вставил его в блокнот.
Мне нужно что-то более надежное, чем использование регулярных выражений, которые могут выйти из строя на плохо сформированном HTML. Я видел, как многие рекомендуют Beautiful Soup, но у меня было несколько проблем с этим. Во-первых, он взял нежелательный текст, например, источник JavaScript. Кроме того, он не интерпретировал объекты HTML. Например, я бы ожидал & # 39; в 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
Я потратил 4-5 часов на исправление проблем с помощью html2text. К счастью, я мог встретить 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 __name__ == '__main__':
main()
Вот версия ответа xperroni, которая немного более полная. Он пропускает script и разделы стиля и переводит charrefs (например, & # 39;) и объекты HTML (например, &).
Он также включает тривиальный обратный конвертер plain-text-to-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)
Я знаю, что ответов уже много, но наиболее изящное и питонное решение, которое я нашел, частично описано здесь.
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)
Вы также можете использовать метод html2text в библиотеке лентограмм.
from stripogram import html2text
text = html2text(your_html_string)
Для установки stripogram выполните 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 была убита, поэтому здесь есть еще одно место, где есть примеры использования PyParsing (пример ссылки). Одна из причин, по которой стоит потратить немного времени на pyparsing, заключается в том, что он также написал очень краткое, очень хорошо организованное руководство по сокращенному использованию O'Reilly, которое также недорогое.
Сказав это, я часто использую BeautifulSoup, и не так уж сложно разобраться с проблемами сущностей, вы можете конвертировать их перед запуском BeautifulSoup.
Удачи
http://pypi.python.org/pypi/webstemmer/0.5.0
http://atropine.sourceforge.net/documentation.html
В качестве альтернативы, я думаю, вы можете управлять lynx из python, искать на нем
Вместо модуля HTMLParser проверьте htmllib. Он имеет аналогичный интерфейс, но делает больше работы для вас. (Он довольно древний, поэтому он не очень помогает с точки зрения избавления от javascript и css. Вы можете создать производный класс, но и добавлять методы с именами, такими как start_script и end_style (см. Документы python для подробностей), но это сложно чтобы сделать это надежно для malformed 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 play it safe
except HTMLParseError: print ':(' #the html is badly malformed (or you found a bug)
Это не совсем решение Python, но оно преобразует текст. Javascript будет генерировать текст, который, как мне кажется, важен (E.G. google.com). В браузере Links (not 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()
если вам нужна более высокая скорость и меньше точности, вы можете использовать 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()
установить 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!
Красивый суп конвертирует 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 ""
Другой вариант - запустить html через текстовый веб-браузер и выгрузить его. Например (используя Lynx):
lynx -dump html_to_convert.html > converted_html.txt
Это можно сделать в python script следующим образом:
import subprocess
with open('converted_html.txt', 'w') as outputFile:
subprocess.call(['lynx', '-dump', 'html_to_convert.html'], stdout=testFile)
Это не даст вам точно текст из HTML файла, но в зависимости от вашего варианта использования может быть предпочтительнее вывод html2text.
Я рекомендую пакет Python, называемый goose-extractor Гусь попытается извлечь следующую информацию:
Основной текст статьи Главное изображение статьи Любые фильмы Youtube/Vimeo, встроенные в статью Описание Meta Мета-теги
Другое решение, отличное от python: Libre Office:
soffice --headless --invisible --convert-to txt input1.html
Причина, по которой я предпочитаю эту альтернативу по сравнению с другими альтернативами, заключается в том, что каждый абзац HTML преобразуется в одну текстовую строку (без разрывов строк), что я и искал. Другие методы требуют последующей обработки. Lynx действительно производит хороший результат, но не совсем то, что я искал. Кроме того, Libre Office может использоваться для преобразования из всех видов форматов...
Кто-нибудь пробовал bleach.clean(html,tags=[],strip=True)
с bleach? он работает для меня.
Я знаю, что здесь уже есть множество ответов, но я думаю, что 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
У меня были хорошие результаты с 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
Теперь вы можете напечатать переменную body, и она будет в формате открытого текста. Если вам это достаточно хорошо, было бы неплохо выбрать ее в качестве принятого ответа.
простым способом
import re
html_text = open('html_file.html').read()
text_filtered = re.sub(r'<(.*?)>', '', html_text)
этот код обнаруживает, что все части html_text начинаются с '<' и заканчивается на ' > ' и заменяет все найденное пустой строкой
@PeYoTIL ответ с использованием BeautifulSoup и устранение стиля и script контент не работал у меня. Я попробовал его с помощью 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
Лучшая работа для меня это надписи.
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)
Результаты действительно хороши
Вы можете извлечь только текст из HTML с BeautifulSoup
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
Здесь код, который я использую на регулярной основе.
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
Я надеюсь, что это поможет.
Комментарий автора LibreOffice имеет свои достоинства, поскольку приложение может использовать макросы Python. Похоже, что он предлагает множество преимуществ как для ответа на этот вопрос, так и для расширения базы макросов LibreOffice. Если это разрешение является одноразовой реализацией, а не для использования в качестве части большой производственной программы, открытие HTML в Writer и сохранение страницы в виде текста, похоже, решит проблемы, обсуждаемые здесь.
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