Извлечение текста из файла HTML с помощью Python

Я хочу извлечь текст из файла HTML с помощью Python. Я хочу, по сути, тот же вывод, который я получил бы, если бы скопировал текст из браузера и вставил его в блокнот.

Мне нужно что-то более надежное, чем использование регулярных выражений, которые могут выйти из строя на плохо сформированном HTML. Я видел, как многие рекомендуют Beautiful Soup, но у меня было несколько проблем с этим. Во-первых, он взял нежелательный текст, например, источник JavaScript. Кроме того, он не интерпретировал объекты HTML. Например, я бы ожидал & # 39; в HTML-источнике, который будет преобразован в апостроф в тексте, точно так же, как если бы я вставлял содержимое браузера в блокнот.

Обновление html2text выглядит многообещающим. Он правильно обрабатывает объекты HTML и игнорирует JavaScript. Однако он не дает ровного текста; он производит уценку, которая затем должна быть превращена в простой текст. Он не содержит примеров или документации, но код выглядит чистым.


Похожие вопросы:

Ответ 1

html2text - это программа на Python, которая хорошо справляется с этой задачей.

Ответ 2

Лучший фрагмент кода, который я нашел для извлечения текста, не получая 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

Ответ 3

ПРИМЕЧАНИЕ. 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)

Ответ 4

Сегодня я столкнулся с одной и той же проблемой. Я написал очень простой синтаксический анализатор 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()

Ответ 5

Вот версия ответа 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 {'&':'&amp;', "'":'&#39;', '"':'&quot;', '<':'&lt;', '>':'&gt;'}.get(t)
        return '<a href="%s">%s</a>' % (t, t)
    return re.sub(r'https?://[^] ()"\';]+|[&\'"<>]', f, text)

Ответ 6

Я знаю, что ответов уже много, но наиболее изящное и питонное решение, которое я нашел, частично описано здесь.

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)

Ответ 7

Вы также можете использовать метод html2text в библиотеке лентограмм.

from stripogram import html2text
text = html2text(your_html_string)

Для установки stripogram выполните sudo easy_install stripogram

Ответ 8

Существует библиотека шаблонов для интеллектуального анализа данных.

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

Ответ 9

PyParsing делает отличную работу. Вики PyParsing была убита, поэтому здесь есть еще одно место, где есть примеры использования PyParsing (пример ссылки). Одна из причин, по которой стоит потратить немного времени на pyparsing, заключается в том, что он также написал очень краткое, очень хорошо организованное руководство по сокращенному использованию O'Reilly, которое также недорогое.

Сказав это, я часто использую BeautifulSoup, и не так уж сложно разобраться с проблемами сущностей, вы можете конвертировать их перед запуском BeautifulSoup.

Удачи

Ответ 11

Вместо модуля 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)

Ответ 12

Это не совсем решение 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()

Ответ 13

если вам нужна более высокая скорость и меньше точности, вы можете использовать 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()

Ответ 14

установить 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!

Ответ 15

Красивый суп конвертирует html-объекты. Вероятно, ваш лучший выбор, учитывая, что HTML часто ошибочен и заполнен проблемами кодирования unicode и html. Это код, который я использую для преобразования html в исходный текст:

import BeautifulSoup
def getsoup(data, to_unicode=False):
    data = data.replace("&nbsp;", " ")
    # 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 ""

Ответ 16

Другой вариант - запустить 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.

Ответ 17

Я рекомендую пакет Python, называемый goose-extractor Гусь попытается извлечь следующую информацию:

Основной текст статьи Главное изображение статьи Любые фильмы Youtube/Vimeo, встроенные в статью Описание Meta Мета-теги

Подробнее: https://pypi.python.org/pypi/goose-extractor/

Ответ 18

Другое решение, отличное от python: Libre Office:

soffice --headless --invisible --convert-to txt input1.html

Причина, по которой я предпочитаю эту альтернативу по сравнению с другими альтернативами, заключается в том, что каждый абзац HTML преобразуется в одну текстовую строку (без разрывов строк), что я и искал. Другие методы требуют последующей обработки. Lynx действительно производит хороший результат, но не совсем то, что я искал. Кроме того, Libre Office может использоваться для преобразования из всех видов форматов...

Ответ 19

Кто-нибудь пробовал bleach.clean(html,tags=[],strip=True) с bleach? он работает для меня.

Ответ 20

Я знаю, что здесь уже есть множество ответов, но я думаю, что 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

Ответ 21

У меня были хорошие результаты с Apache Tika. Его целью является извлечение метаданных и текста из содержимого, поэтому соответствующий синтаксический анализатор настраивается соответствующим образом из коробки.

Tika может быть запущена как сервер, тривиально запустить/развернуть в контейнере Docker, и оттуда можно получить доступ через привязки Python.

Ответ 22

В 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, и она будет в формате открытого текста. Если вам это достаточно хорошо, было бы неплохо выбрать ее в качестве принятого ответа.

Ответ 23

простым способом

import re

html_text = open('html_file.html').read()
text_filtered = re.sub(r'<(.*?)>', '', html_text)

этот код обнаруживает, что все части html_text начинаются с '<' и заканчивается на ' > ' и заменяет все найденное пустой строкой

Ответ 24

@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

Ответ 25

Лучшая работа для меня это надписи.

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)

Результаты действительно хороши

Ответ 26

Вы можете извлечь только текст из 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)

Ответ 27

Хотя многие люди упоминали об использовании регулярных выражений для удаления HTML-тегов, есть много минусов.

например:

<p>hello&nbsp;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

Ответ 28

Здесь код, который я использую на регулярной основе.

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

Я надеюсь, что это поможет.

Ответ 29

Комментарий автора LibreOffice имеет свои достоинства, поскольку приложение может использовать макросы Python. Похоже, что он предлагает множество преимуществ как для ответа на этот вопрос, так и для расширения базы макросов LibreOffice. Если это разрешение является одноразовой реализацией, а не для использования в качестве части большой производственной программы, открытие HTML в Writer и сохранение страницы в виде текста, похоже, решит проблемы, обсуждаемые здесь.

Ответ 30

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