【爬虫】使用 BeautifulSoup 删除指定的 HTML 标签

3,116 阅读4分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动

一、概述

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语言库
html5libBeautifulSoup(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))

参考文档:

www.crummy.com/software/Be…