我想使用Python从HTML文件中提取文本。我想从本质上得到相同的输出,如果我从浏览器复制文本,并将其粘贴到记事本。

我想要一些更健壮的东西,而不是使用正则表达式,正则表达式可能会在格式不佳的HTML上失败。我见过很多人推荐Beautiful Soup,但我在使用它时遇到了一些问题。首先,它会抓取不需要的文本,比如JavaScript源代码。此外,它也不解释HTML实体。例如,我会期望'在HTML源代码中转换为文本中的撇号,就像我将浏览器内容粘贴到记事本一样。

更新html2text看起来很有希望。它正确地处理HTML实体,而忽略JavaScript。然而,它并不完全生成纯文本;它产生的降价,然后必须转换成纯文本。它没有示例或文档,但代码看起来很干净。


相关问题:

在python中过滤HTML标签并解析实体 在Python中将XML/HTML实体转换为Unicode字符串


当前回答

今天我发现自己面临着同样的问题。我编写了一个非常简单的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()

其他回答

而不是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)

下面是xperroni回答的一个版本,它更完整一些。它跳过脚本和样式部分,并翻译charref(例如,&#39;)和HTML实体(例如,&)。

它还包括一个简单的纯文本到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)

我知道已经有很多答案了,但是我找到的最优雅、最python化的解决方案在这里进行了部分描述。

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)

如果您需要更高的速度和更低的准确性,那么您可以使用原始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()

我使用Apache Tika取得了很好的结果。它的目的是从内容中提取元数据和文本,因此底层解析器要进行相应的开箱即用调优。

Tika可以作为服务器运行,在Docker容器中运行/部署很简单,并且可以通过Python绑定访问。