python 提取html文本的方法
作者:Python中文社区 发布时间:2021-05-27 03:43:46
假设我们需要从各种网页中提取全文,并且要剥离所有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的效率令人印象深刻。
来源:https://mp.weixin.qq.com/s/kn-6fxdSU1W9EbqnyiB_HA


猜你喜欢
- PSUtil是一个跨平台的Python库,用于检索有关正在运行的进程和系统利用率(CPU,内存,磁盘,网络,传感器)的信息。它可以跨平台使用
- 本文分析了让ThinkPHP的模板引擎达到最佳效率的方法。分享给大家供大家参考,具体如下:默认情况下ThinkPHP框架系统默认使用的模板引
- 准备写一个操作Excel脚本却在导入包的时候出现了一个小问题导入包from Tkinter import Tkfrom time impor
- 本系列教程我们将使用python实现一些简单的测试工具,为了尽可能的简单,我们的工具以命令行工具为主。本系列教程使用的python版本是3.
- 使用自带的Tkinter模块,简单的弹输入框示例,返回输入值from Tkinter import *import tkMessageBox
- MySQL:Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEM1:错误日志大量错误150602 14:40:02 [Warning] Unsafe statement written to the bi
- 开发工具Python版本:3.6.4相关模块:pygame模块;以及一些python自带的模块。环境搭建安装Python并添加到环境变量,p
- 问题你想从一个简单的XML文档中提取数据。解决方案可以使用 xml.etree.ElementTree 模块从简单的XML文档中提取数据。为
- 我们通常会使用 apt-get或者是pip来安装包,但是这两者的安装路径是不一样的,区别如下:如果使用pip或者pip3安装,则第三方包在:
- javascript中声明函数的方法有两种:函数声明式和函数表达式.区别如下:1).以函数声明的方法定义的函数,函数名是必须的,而函数表达式
- hmac模块:比较两个函数的密钥是否一致:import hmacimport osmsg = os.urandom(32) #
- 提高性能有如下方法1、Cython,用于合并python和c语言静态编译泛型2、IPython.parallel,用于在本地或者集群上并行执
- 图片版PDF无法复制,转化成文字版的PDF后使用更方便. 我们需要用到python3.6,pypdf2,ghostscript,Python
- 用一句话概括冷备,就是把数据库服务,比如MySQL,O
- 以下测试用于去除任何字符串中连线的分隔符 --去除字符串中连续的分隔符 declare @str nvarchar(200) declare
- 多行正则表达式是对象的只读布尔属性。它指定是否一个特定的正则表达式进行多行匹配,即,不管是否使用“m”属性创建。语法RegExp
- 1、jsp前端<%-- Created by IntelliJ IDEA. User: Lenovo Date: 2020/6/19
- 什么是钩子之前有转一篇关于回调函数的文章钩子函数、注册函数、回调函数,他们的概念其实是一样的。 钩子函数,顾名思义,就是把我们自己实现的ho
- 一、存储引擎上节我们最后说到,SQL 的执行计划是执行器组件调用存储引擎的接口来完成的。那我们可以理解为:MySQL 这个数据库管理系统是依
- 本文实例为大家分享了js实现放大镜效果的具体代码,供大家参考,具体内容如下该放大区域用背景图片放大<!DOCTYPE html>