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

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

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


相关问题:

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


当前回答

这里的所有方法在一些网站上都不能很好地工作。由JS代码生成的段落可以抵抗上述所有问题。这是我最终得到的启发,受到这个和这个的启发。

这个想法是在webdriver中加载页面,并滚动到页面的末尾,让JS做它的事情来生成/加载页面的其余部分。然后插入键击命令选择全部复制/粘贴整个页面:

import selenium
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import pyperclip
import time

driver = webdriver.Chrome()
driver.get("https://www.lazada.com.ph/products/nike-womens-revolution-5-running-shoes-black-i1262506154-s4552606107.html?spm=a2o4l.seller.list.3.6f5d7b6cHO8G2Y&mp=1&freeshipping=1")

# Scroll down to end of the page to let all javascript code load its content
lenOfPage = driver.execute_script("window.scrollTo(0, document.body.scrollHeight);var lenOfPage=document.body.scrollHeight;return lenOfPage;")
match=False
while(match==False):
        lastCount = lenOfPage
        time.sleep(1)
        lenOfPage = driver.execute_script("window.scrollTo(0, document.body.scrollHeight);var lenOfPage=document.body.scrollHeight;return lenOfPage;")
        if lastCount==lenOfPage:
            match=True

# copy from the webpage
element = driver.find_element_by_tag_name('body')
element.send_keys(Keys.CONTROL,'a')
element.send_keys(Keys.CONTROL,'c')
alltext = pyperclip.paste()
alltext = alltext.replace("\n", " ").replace("\r", " ")  # cleaning the copied text
print(alltext )

它很慢。但其他的都不奏效。

更新:一个更好的方法是在滚动到页面末尾后使用inscriptis库加载页面的源代码:

from inscriptis import get_text
text = get_text(driver.page_source)

仍然不能与无头驱动程序一起工作(页面检测到它不是由real显示,滚动到末尾不会使JS代码加载它的东西),但至少我们不需要疯狂的复制/粘贴,这阻碍了我们在共享剪贴板的机器上运行多个脚本。

其他回答

另一个在Python 2.7.9+中使用BeautifulSoup4的例子

包括:

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()仅获取文本。分割成行,删除每个标题的开头和结尾空格,然后将多个标题分割成一行,each chunks = (phrase.strip() for line in line for phrase in line。(" "))。然后使用text = '\n'。加入,删除空行,最后返回为批准的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)即可。

在Python 3中。X,你可以通过导入“imaplib”和“电子邮件”包,以非常简单的方式做到这一点。虽然这是一个老帖子,但也许我的答案可以帮助到这个帖子的新人。

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

现在你可以打印主体变量,它将是明文格式:)如果它对你来说足够好,那么它将很好地选择它作为接受的答案。

html2text是一个Python程序,它在这方面做得很好。

Beautiful soup可以转换html实体。考虑到HTML经常有bug并且充满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 ""

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