欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

python 提取html文本的方法

程序员文章站 2022-06-19 12:54:34
假设我们需要从各种网页中提取全文,并且要剥离所有html标记。通常,默认解决方案是使用beautifulsoup软件包中的get_text方法,该方法内部使用lxml。这是一个经过充分测试的解决方案,...

假设我们需要从各种网页中提取全文,并且要剥离所有html标记。通常,默认解决方案是使用beautifulsoup软件包中的get_text方法,该方法内部使用lxml。这是一个经过充分测试的解决方案,但是在处理成千上万个html文档时可能会非常慢。
通过用selectolax替换beautifulsoup,您几乎可以免费获得5-30倍的加速!
这是一个简单的基准测试,可分析commoncrawl(`处理nlp问题时,有时您需要获得大量的文本集。互联网是文本的最大来源,但是不幸的是,从任意html页面提取文本是一项艰巨而痛苦的任务。
假设我们需要从各种网页中提取全文,并且要剥离所有html标记。通常,默认解决方案是使用beautifulsoup软件包中的get_text方法,该方法内部使用lxml。这是一个经过充分测试的解决方案,但是在处理成千上万个html文档时可能会非常慢。
通过用selectolax替换beautifulsoup,您几乎可以免费获得5-30倍的加速!这是一个简单的基准测试,可分析commoncrawl(https://commoncrawl.org/)的10,000个html页面:

# coding: utf-8

from time import time

import warc
from bs4 import beautifulsoup
from selectolax.parser import htmlparser


def get_text_bs(html):
    tree = beautifulsoup(html, 'lxml')

    body = tree.body
    if body is none:
        return none

    for tag in body.select('script'):
        tag.decompose()
    for tag in body.select('style'):
        tag.decompose()

    text = body.get_text(separator='\n')
    return text


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='\n')
    return text


def read_doc(record, parser=get_text_selectolax):
    url = record.url
    text = none

    if url:
        payload = record.payload.read()
        header, html = payload.split(b'\r\n\r\n', maxsplit=1)
        html = html.strip()

        if len(html) > 0:
            text = parser(html)

    return url, text


def process_warc(file_name, parser, limit=10000):
    warc_file = warc.open(file_name, 'rb')
    t0 = time()
    n_documents = 0
    for i, record in enumerate(warc_file):
        url, doc = read_doc(record, parser)

        if not doc or not url:
            continue

        n_documents += 1

        if i > limit:
            break

    warc_file.close()
    print('parser: %s' % parser.__name__)
    print('parsing took %s seconds and produced %s documents\n' % (time() - t0, n_documents))
>>> ! wget https://commoncrawl.s3.amazonaws.com/crawl-data/cc-main-2018-05/segments/1516084886237.6/warc/cc-main-20180116070444-20180116090444-00000.warc.gz
>>> file_name = "cc-main-20180116070444-20180116090444-00000.warc.gz"
>>> process_warc(file_name, get_text_selectolax, 10000)
parser: get_text_selectolax
parsing took 16.170367002487183 seconds and produced 3317 documents
>>> process_warc(file_name, get_text_bs, 10000)
parser: get_text_bs
parsing took 432.6902508735657 seconds and produced 3283 documents

显然,这并不是对某些事物进行基准测试的最佳方法,但是它提供了一个想法,即selectolax有时比lxml快30倍。
selectolax最适合将html剥离为纯文本。如果我有10,000多个html片段,需要将它们作为纯文本索引到elasticsearch中。(elasticsearch有一个html_strip文本过滤器,但这不是我想要/不需要在此上下文中使用的过滤器)。事实证明,以这种规模将html剥离为纯文本实际上是非常低效的。那么,最有效的方法是什么?

  • pyquery
from pyquery import pyquery as pq

text = pq(html).text()
  • selectolax
from selectolax.parser import htmlparser

text = htmlparser(html).text()
  • 正则表达式
import re

regex = re.compile(r'<.*?>')
text = clean_regex.sub('', html)

结果

我编写了一个脚本来计算时间,该脚本遍历包含html片段的10,000个文件。注意!这些片段不是完整的<html>文档(带有<head>和<body>等),只是html的一小部分。平均大小为10,314字节(中位数为5138字节)。结果如下:

pyquery
  sum:    18.61 seconds
  mean:   1.8633 ms
  median: 1.0554 ms
selectolax
  sum:    3.08 seconds
  mean:   0.3149 ms
  median: 0.1621 ms
regex
  sum:    1.64 seconds
  mean:   0.1613 ms
  median: 0.0881 ms

我已经运行了很多次,结果非常稳定。重点是:selectolax比pyquery快7倍。

正则表达式好用?真的吗?

对于最基本的html blob,它可能工作得很好。实际上,如果html是<p> foo&amp; bar </ p>,我希望纯文本转换应该是foo&bar,而不是foo&amp; bar。
更重要的一点是,pyquery和selectolax支持非常特定但对我的用例很重要的内容。在继续之前,我需要删除某些标签(及其内容)。例如:

<h4 class="warning">this should get stripped.</h4>
<p>please keep.</p>
<div style="display: none">this should also get stripped.</div>

正则表达式永远无法做到这一点。

2.0 版本

因此,我的要求可能会发生变化,但基本上,我想删除某些标签。例如:<div class =“ warning”>  、 <div class =“ hidden”> 和 <div style =“ display:none”>。因此,让我们实现一下:

  • pyquery
from pyquery import pyquery as pq

_display_none_regex = re.compile(r'display:\s*none')

doc = pq(html)
doc.remove('div.warning, div.hidden')
for div in doc('div[style]').items():
    style_value = div.attr('style')
    if _display_none_regex.search(style_value):
        div.remove()
text = doc.text()
  • selectolax
from selectolax.parser import htmlparser

_display_none_regex = re.compile(r'display:\s*none')

tree = htmlparser(html)
for tag in tree.css('div.warning, div.hidden'):
    tag.decompose()
for tag in tree.css('div[style]'):
    style_value = tag.attributes['style']
    if style_value and _display_none_regex.search(style_value):
        tag.decompose()
text = tree.body.text()

这实际上有效。当我现在为10,000个片段运行相同的基准时,新结果如下:

pyquery
  sum:    21.70 seconds
  mean:   2.1701 ms
  median: 1.3989 ms
selectolax
  sum:    3.59 seconds
  mean:   0.3589 ms
  median: 0.2184 ms
regex
  skip

同样,selectolax击败pyquery约6倍。

结论

正则表达式速度快,但功能弱。selectolax的效率令人印象深刻。

以上就是python 提取html文本的方法的详细内容,更多关于python 提取html文本的资料请关注其它相关文章!