在 Python 中提取和清理 HTML 正文的最快、最无错误的方法是什么?
What is the fastest, most error-free method of extracting and cleaning the HTML body text in Python?
我目前有两个函数可以从 Python 和 return 中提取 HTML <body>
文本作为词袋。他们提供等效的输出。我还清理了各种标签,否则这些标签会给我垃圾文本(例如 <script>
代码)。
def html_to_bow_bs(text):
if text is None or len(text)==0:
return []
soup = BeautifulSoup(text, "lxml",parse_only=SoupStrainer('body'))
# Remove all irrelevant tags
for elem in soup.findAll(['script','style','a']):
elem.extract()
body_text = soup.findAll("body")
if len(body_text) == 0:
return []
# Encoding. Remove extra whitespace and unprintable characters
the_text = body_text[0].get_text().encode('utf-8')
the_text = str(the_text)
the_text = the_text.strip()
the_text = re.sub(r'[^\x00-\x7F]+',' ',the_text)
return [w.lower() for w in the_text.split()]
def html_to_bow_bs_lxml(text):
if text is None or len(text)==0:
return []
body_re = re.findall('<body(.*?)</body>', text, flags=re.DOTALL)
if len(body_re) == 0:
return []
fragment = body_re[0]
# Remove irrelevant tags
fragment = re.sub(r'<script.*?</script>', ' ', fragment, flags=re.DOTALL)
fragment = re.sub(r'<style.*?</style>', ' ', fragment, flags=re.DOTALL)
text = "<body" + fragment + "</body>"
soup = BeautifulSoup(text, "lxml")
if soup is None:
return []
# Remote more irrelevant tags
for elem in soup.findAll(['a']):
elem.extract()
# Encoding. Remove extra whitespace and unprintable characters
the_text = body_text[0].get_text().encode('utf-8')
the_text = str(the_text)
the_text = the_text.strip()
the_text = re.sub(r'[^\x00-\x7F]+',' ',the_text)
return [w.lower() for w in the_text.split()]
我的主要要求是匹配输出:来自 html_to_bow_bs_lxml(text)
的词组匹配 html_to_bow_bs(text)
。目前,两者在 运行ning 时间上相当;对于 330 页,他们 运行 大约 20 秒(慢!)。如果我用正则表达式删除并替换第二个函数中的最后一个 soup.findAll(['a'])...extract()
,我可以节省 6 秒的时间。将 BeautifulSoup
完全替换为 lxml.etree
可以再节省 10 秒,使总的 运行 时间约为 3-4 秒。但是,当用正则表达式替换时,
- 输出并不总是匹配。替换
BeautifulSoup
时,输出不匹配或
- 由于格式不正确 HTML,我的程序在处理过程中崩溃了。如何在保持正确性的同时提高速度?
我在 Whosebug 上看到了关于使用 Python 提取 HTML 的各种建议,但这些建议可以追溯到几年前(例如 2012 年)。可以理解的是,从那时起,库进行了许多更新。
(我也尝试过 pyquery,但它并不总能正确提取正文。)
您已经做了很多工作来让它变得更快 - 在使用 BeautifulSoup
.
优化解析时,滤汤器和 lxml
解析器通常是首先要尝试的事情
下面是对这个特定代码的一些改进。
删除主体存在检查:
body_text = soup.findAll("body")
if len(body_text) == 0:
return []
并使用 find()
代替。
将 if text is None or len(text)==0:
替换为 if not text:
。
通过 get_text(strip=True)
.
剥离
改进代码:
def html_to_bow_bs(text):
if not text:
return []
soup = BeautifulSoup(text, "lxml", parse_only=SoupStrainer('body'))
# Remove all irrelevant tags
for elem in soup.find_all(['script','style','a']):
elem.extract()
body = soup.find("body")
if not body:
return []
the_text = body.get_text(strip=True).encode('utf-8')
the_text = re.sub(r'[^\x00-\x7F]+', ' ', the_text)
return [w.lower() for w in the_text.split()]
这些只是微小的改进,我认为它们不会改变整体性能。我还要研究的内容:
- 运行 通过
pypy
的脚本(beautifulsoup4
是 compatible,但您将无法使用 lxml
解析器 - 尝试使用 html.parser
或 html5lib
)。甚至根本不修改代码,您可能会赢得很多。
使用请求模块和 bs4
这是打印正文的最简单方法。
import requests
from bs4 import BeautifulSoup
url = "yourUrl"
r = requests.get(url)
soup = BeautifulSoup(r.content, 'lxml')
items = soup.find_all('body')
for item in items:
print item.text
注意:如果打印所有正文,它还会打印 jquery 和 javascript 函数,以防其中有。
我目前有两个函数可以从 Python 和 return 中提取 HTML <body>
文本作为词袋。他们提供等效的输出。我还清理了各种标签,否则这些标签会给我垃圾文本(例如 <script>
代码)。
def html_to_bow_bs(text):
if text is None or len(text)==0:
return []
soup = BeautifulSoup(text, "lxml",parse_only=SoupStrainer('body'))
# Remove all irrelevant tags
for elem in soup.findAll(['script','style','a']):
elem.extract()
body_text = soup.findAll("body")
if len(body_text) == 0:
return []
# Encoding. Remove extra whitespace and unprintable characters
the_text = body_text[0].get_text().encode('utf-8')
the_text = str(the_text)
the_text = the_text.strip()
the_text = re.sub(r'[^\x00-\x7F]+',' ',the_text)
return [w.lower() for w in the_text.split()]
def html_to_bow_bs_lxml(text):
if text is None or len(text)==0:
return []
body_re = re.findall('<body(.*?)</body>', text, flags=re.DOTALL)
if len(body_re) == 0:
return []
fragment = body_re[0]
# Remove irrelevant tags
fragment = re.sub(r'<script.*?</script>', ' ', fragment, flags=re.DOTALL)
fragment = re.sub(r'<style.*?</style>', ' ', fragment, flags=re.DOTALL)
text = "<body" + fragment + "</body>"
soup = BeautifulSoup(text, "lxml")
if soup is None:
return []
# Remote more irrelevant tags
for elem in soup.findAll(['a']):
elem.extract()
# Encoding. Remove extra whitespace and unprintable characters
the_text = body_text[0].get_text().encode('utf-8')
the_text = str(the_text)
the_text = the_text.strip()
the_text = re.sub(r'[^\x00-\x7F]+',' ',the_text)
return [w.lower() for w in the_text.split()]
我的主要要求是匹配输出:来自 html_to_bow_bs_lxml(text)
的词组匹配 html_to_bow_bs(text)
。目前,两者在 运行ning 时间上相当;对于 330 页,他们 运行 大约 20 秒(慢!)。如果我用正则表达式删除并替换第二个函数中的最后一个 soup.findAll(['a'])...extract()
,我可以节省 6 秒的时间。将 BeautifulSoup
完全替换为 lxml.etree
可以再节省 10 秒,使总的 运行 时间约为 3-4 秒。但是,当用正则表达式替换时,
- 输出并不总是匹配。替换
BeautifulSoup
时,输出不匹配或 - 由于格式不正确 HTML,我的程序在处理过程中崩溃了。如何在保持正确性的同时提高速度?
我在 Whosebug 上看到了关于使用 Python 提取 HTML 的各种建议,但这些建议可以追溯到几年前(例如 2012 年)。可以理解的是,从那时起,库进行了许多更新。
(我也尝试过 pyquery,但它并不总能正确提取正文。)
您已经做了很多工作来让它变得更快 - 在使用 BeautifulSoup
.
lxml
解析器通常是首先要尝试的事情
下面是对这个特定代码的一些改进。
删除主体存在检查:
body_text = soup.findAll("body")
if len(body_text) == 0:
return []
并使用 find()
代替。
将 if text is None or len(text)==0:
替换为 if not text:
。
通过 get_text(strip=True)
.
改进代码:
def html_to_bow_bs(text):
if not text:
return []
soup = BeautifulSoup(text, "lxml", parse_only=SoupStrainer('body'))
# Remove all irrelevant tags
for elem in soup.find_all(['script','style','a']):
elem.extract()
body = soup.find("body")
if not body:
return []
the_text = body.get_text(strip=True).encode('utf-8')
the_text = re.sub(r'[^\x00-\x7F]+', ' ', the_text)
return [w.lower() for w in the_text.split()]
这些只是微小的改进,我认为它们不会改变整体性能。我还要研究的内容:
- 运行 通过
pypy
的脚本(beautifulsoup4
是 compatible,但您将无法使用lxml
解析器 - 尝试使用html.parser
或html5lib
)。甚至根本不修改代码,您可能会赢得很多。
使用请求模块和 bs4
这是打印正文的最简单方法。
import requests
from bs4 import BeautifulSoup
url = "yourUrl"
r = requests.get(url)
soup = BeautifulSoup(r.content, 'lxml')
items = soup.find_all('body')
for item in items:
print item.text
注意:如果打印所有正文,它还会打印 jquery 和 javascript 函数,以防其中有。