python 提取html文本的方法
发布时间:2021-06-22 15:26:45
在解决自然语言处理问题时,有时你需要获得大量的文本集。互联网是文本的最大来源,但是从任意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页面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | 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))
|
1 2 3 4 5 6 7 8 | >>> ! 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剥离为纯文本实际上是非常低效的。那么,最有效的方法是什么?
1 2 3 | from pyquery import PyQuery as pq text = pq(html).text()
|
1 2 3 | from selectolax.parser import HTMLParser text = HTMLParser(html).text()
|
1 2 3 4 | import re regex = re. compile (r '<.*?>' )
text = clean_regex.sub('', html)
|
结果
我编写了一个脚本来计算时间,该脚本遍历包含HTML片段的10,000个文件。注意!这些片段不是完整的<html>文档(带有<head>和<body>等),只是HTML的一小部分。平均大小为10,314字节(中位数为5138字节)。结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 | 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支持非常特定但对我的用例很重要的内容。在继续之前,我需要删除某些标签(及其内容)。例如:
1 2 3 | < 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”>。因此,让我们实现一下:
1 2 3 4 5 6 7 8 9 10 11 | 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()
|
1 2 3 4 5 6 7 8 9 10 11 12 | 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个片段运行相同的基准时,新结果如下:
1 2 3 4 5 6 7 8 9 10 | 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文本的方法的详细内容