python 提取html文本的方法
在解决自然语言处理问题时,有时你需要获得大量的文本集。互联网是文本的最大来源,但是从任意HTML页面提取文本是一项艰巨而痛苦的任务。本文将讲述python高效提取html文本的方法
假设我们需要从各种网页中提取全文,并且要剥离所有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的一小部分。平均大小为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是
Foo&amp; Bar
,我希望纯文本转换应该是Foo&Bar,而不是Foo&amp; bar。更重要的一点是,PyQuery和selectolax支持非常特定但对我的用例很重要的内容。在继续之前,我需要删除某些标签(及其内容)。例如:
This should get stripped.
Please keep.
This should also get stripped.
正则表达式永远无法做到这一点。
2.0 版本
- 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轻松管理与操作文件的技巧分享
在日常开发中,我们经常会遇到需要对文件进行操作的场景,如读写文件、文件夹操作等。本文将为大家介绍一些 Python 中处理文件的实用技巧,让你的工作更高效
pytorch实现梯度下降和反向传播图文详细讲解
这篇文章主要介绍了pytorch实现梯度下降和反向传播,反向传播的目的是计算成本函数C对网络中任意w或b的偏导数。一旦我们有了这些偏导数,我们将通过一些常数α的乘积和该数量相对于成本函数的偏导数来更新网络中的权重和偏差
深入理解pytorch库的dockerfile
这篇文章主要介绍了pytorch库的dockerfile,主要包括dockerfile命令,使用指令的注意点及存在的一些问题,本文给大家介绍的非常详细,需要的朋友可以参考下
Selenium多窗口切换解决方案
本文主要介绍了Selenium多窗口切换解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
Python自动化办公之定时发送邮件的实现
python中的schedule模块可以使我们方便简单的使用定时任务,即在特定的时间自动的执行一些任务的功能,本文将用这一模块实现邮件自动发送,需要的可以参考一下