小知识,大挑战!本文正在参与“程序员必备小知识”创作活动
一、概述
Beautiful Soup 是一个可以从HTML或XML文件中提取数据的 Python 库,它能够通过你喜欢的转换器实现文档的导航、查找、修改,使用 Beautiful Soup会帮你节省数小时甚至数天的工作时间。
可以使用下面的命令安装 BeautifulSoup:
pip install beautifulsoup4
Beautiful Soup 支持 Python 标准库中的HTML解析器,还支持一些第三方的解析器,其中一个是 lxml ,可以使用下面的命令安装 lxml
:
pip install lxml
另一个可供选择的解析器是纯 Python 实现的 html5lib , html5lib
的解析方式与浏览器相同,可以使用下面的命令来安装 html5lib
:
pip install html5lib
下表列出了主要的解析器,以及它们的优缺点:
解析器 | 使用方法 | 优势 | 劣势 |
---|---|---|---|
Python标准库 | BeautifulSoup(markup, "html.parser") | Python的内置标准库 执行速度适中 文档容错能力强 | Python 2.7.3 or 3.2.2)前 的版本中文档容错能力差 |
lxml HTML 解析器 | BeautifulSoup(markup, "lxml") | 速度快 文档容错能力强 | 需要安装C语言库 |
lxml XML 解析器 | BeautifulSoup(markup, ["lxml-xml"])``BeautifulSoup(markup, "xml") | 速度快 唯一支持XML的解析器 | 需要安装C语言库 |
html5lib | BeautifulSoup(markup, "html5lib") | 最好的容错性 以浏览器的方式解析文档 生成HTML5格式的文档 | 速度慢不依赖外部扩展 |
推荐使用lxml作为解析器,因为效率更高. 在 Python2.7.3 之前的版本和 Python3 中3.2.2之前的版本,必须安装lxml或html5lib, 因为那些Python版本的标准库中内置的HTML解析方法不够稳定.
提示: 如果一段HTML或XML文档格式不正确的话,那么在不同的解析器中返回的结果可能是不一样的,查看 解析器之间的区别 了解更多细节
二、需求描述
前面简单介绍了一下 Beautiful Soup 的安装,关于 Beautiful Soup 的详细使用可以查阅官方文档。接下来介绍一下自己遇到的问题,需求是这样的,我需要获取网页中的文本和图片不包括视频,所以要将获取到的网页中的视频标签去除,有很多种方式都可以实现这个需求,比如可以使用正则表达式匹配并去除,对于不熟悉正则表达式语法的小伙伴来说还是有点麻烦,所以这里我将介绍使用BeautifulSoup 删除指定的 HTML 标签。
三、解决方法
extract()
PageElement.extract()
方法将当前tag移除文档树,并作为方法结果返回:
markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
soup = BeautifulSoup(markup)
a_tag = soup.a
i_tag = soup.i.extract()
a_tag
# <a href="http://example.com/">I linked to</a>
i_tag
# <i>example.com</i>
print(i_tag.parent)
None
这个方法实际上产生了2个文档树: 一个是用来解析原始文档的 BeautifulSoup
对象,另一个是被移除并且返回的tag.被移除并返回的tag可以继续调用 extract
方法:
my_string = i_tag.string.extract()
my_string
# u'example.com'
print(my_string.parent)
# None
i_tag
# <i></i>
案例介绍
这里以爬取央视网新闻客户端的网页数据进行简单的介绍,因为央视网新闻客户端有反爬虫限制,我们无法直接通过 requests 这个python库获取网页的内容,需要使用 selenium 模拟浏览器的行为才能获取,关于 selenium 的安装与使用可以查看 docker 部署 python + selenium + firefox-headless。其中还有一个问题是,网页中的图片不是一次性全部加载的,需要我们拖动滚动条到图片的位置才能加载图片的 url,所以这里还需要模拟浏览器的拖动滚动条的行为,具体的使用可以查看下面的代码。
import time
from lxml import etree
from bs4 import BeautifulSoup
from app.utils.case_class import WebInfo
from app.utils.tools import handle_content
from selenium import webdriver
from selenium.webdriver import DesiredCapabilities
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC # 和下面WebDriverWait一起用的
from selenium.webdriver.support.wait import WebDriverWait
def get_news_content(url):
# 使用远程的 selenuim 模拟浏览器获取html网页源码
html = html_remote_firefox(url)
soup = BeautifulSoup(html, 'lxml')
# 移除所有视频相关标签
for tag in soup.find_all('div', class_='container-video'):
# 执行 extract() 将当前tag 从文档树中移除
tag.extract()
title = soup.find('h1', class_='title').get_text()
pubtime = soup.find('span', class_='media-publish-time').get_text()
source = soup.find('div', class_='media-name').get_text()
content = soup.find('article')
tree = etree.HTML(str(content))
content = handle_content(None, tree)
result = WebInfo(title, content, source, pubtime).to_json()
return result
def html_remote_firefox(url):
driver = webdriver.Remote(
command_executor='http://localhost:4444/wd/hub',
desired_capabilities=DesiredCapabilities.FIREFOX,
)
driver.implicitly_wait(10)
driver.get(url)
# 显式等待:显式地等待某个元素被加载
wait = WebDriverWait(driver, 5)
wait.until(EC.presence_of_element_located((By.TAG_NAME, 'span')))
# 模拟浏览器的拖动滚动条,从 0 开始,350 为步长,可以自行设置,20000 为最大网页内容的高度,可以自行设置
for i in range(0, 20000, 350):
time.sleep(0.05)
driver.execute_script('window.scrollTo(0, %s)' % i)
time.sleep(2)
html = driver.page_source
driver.quit()
return html
if __name__ == "__main__":
url = 'https://content-static.cctvnews.cctv.com/snow-book/index.html?item_id=14268024913723122673&t=1634809629620&toc_style_id=feeds_default'
print(get_news_content(url))
参考文档: