使用Beautiful Soup库解析网页

支淮晨
2023-12-01

使用Beautiful Soup库解析网页

Beautiful Soup是一个可以从HTML或XML文件中提取数据的Python库。它能够实现惯用的文档导航,查找,修改文档的方式等功能。Beautiful Soup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,其中一个是 lxml,另一个可供选择的解析器是纯Python实现的 html5lib,html5lib的解析方式与浏览器相同。下表介绍了Beautiful Soup主要的解析器,以及它们的优缺点:

解析器使用方法优势劣势
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格式的文档速度慢、不依赖外部扩展

1.安装

Beautiful Soup可使用pip安装,如代码所示:

pip install bs4

2.创建BeautifulSoup对象

要使用BeautifulSoup库解析网页,首先需要创建BeautifulSoup对象,将一段文档传入BeautifulSoup的构造方法,就能得到一个文档的对象,可以传入一段字符串或一个文件句柄,如代码所示:

from bs4 import BeautifulSoup

soup = BeautifulSoup(open("index.html"))
soup = BeautifulSoup("<html>data</html>")

Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种: Tag,NavigableString,BeautifulSoup,Comment。

(1)、Tag对象

Tag对象与XML或HTML原生文档中的tag相同,包括HTML标签还有其中包含的文本内容。例如"<title>北京二手房房源_北京二手房出售|买卖|交易信息</title>“或”<div class=“mainInfo”>4室2厅</div>"都是Beautiful Soup中的Tag对象。

通过Tag名称可以很方便的在文档树中获取需要的Tag对象,使用Tag名称查找的方法只能获得文档树中的第一个同名Tag对象,示例代码如下:

>>> # 导入BeautifulSoup
>>> from bs4 import BeautifulSoup

>>> # 创建BeautifulSoup对象
>>> soup = BeautifulSoup('<b class="boldest">Extremely bold</b>', 'lxml')
>>> print(soup.b)
<b class="boldest">Extremely bold</b>

Tag有很多方法和属性,其中最重要的属性: name和attributes。

每个tag都有自己的名字,通过 .name来获取,示例代码如下:

>>> # 创建Tag对象
>>> tag = soup.b
>>> print(tag.name)
b

一个tag可能有很多个属性。<b class=“boldest”>有一个 “class” 的属性,值为 “boldest”。tag的属性的操作方法与字典相同:

>>> print(tag['class'])
['boldest']

也可以直接"点"取属性,返回的是一个地点,属性为键,属性值为值,示例代码如下:

>>> print(tag.attrs)
{'class': ['boldest']}

(2)、NavigableString对象

NavigableString对象为包含在Tag对象中的文本字符串内容,可使用string方法获取,示例代码如下:

>>> # 导入BeautifulSoup
>>> from bs4 import BeautifulSoup

>>> html = '''
<div class="communityName"><i></i><span class="label">小区名称</span><a href="/xiaoqu/1111027379370/" target="_blank" class="info ">世纪星城</a><a href="#around" class="map">地图</a></div>
'''
>>> # 创建BeautifulSoup对象
>>> soup = BeautifulSoup(html, 'lxml')
>>> print(soup.a.string)
世纪星城

string只能获取该Tag对象中的直系文本内容,可以使用text属性和get_text()方法获取Tag对象中后代节点的所有文本内容,返回值为字符串,示例代码如下:

>>> # 导入BeautifulSoup
>>> from bs4 import BeautifulSoup

>>> html = '''
    <div class="room"><div class="mainInfo">4室2厅</div><div class="subInfo">高楼层/共10层</div></div>
'''
>>> # 创建BeautifulSoup对象
>>> soup = BeautifulSoup(html, 'lxml')
# 使用text属性获取div节点下的所有文本内容
>>> print(soup.div.text)
4室2厅高楼层/共10层
# 使用get_text()方法获取div节点下的所有文本内容
>>> print(soup.div.get_text())
4室2厅高楼层/共10层

(3)、BeautifulSoup对象

BeautifulSoup对象表示的是一个文档的全部内容,大部分时候,可以把它当作 Tag对象。BeautifulSoup对象并不是真正的HTML或XML的tag,所以它没有name和attribute属性。但BeautifulSoup对象包含了一个值为 “[document]” 的特殊属性 .name属性,示例代码如下:

>>> # 导入BeautifulSoup
>>> from bs4 import BeautifulSoup

>>> html = '''
    <div class="room"><div class="mainInfo">4室2厅</div><div class="subInfo">高楼层/共10层</div></div>
'''
>>> # 创建BeautifulSoup对象
>>> soup = BeautifulSoup(html, 'lxml')
>>> print(soup.name)
[document]

(4)、Comment对象

Tag,NavigableString,BeautifulSoup几乎覆盖了html和xml中的所有内容,但是还有一些特殊对象就文档的注释部分。Comment 对象是一个特殊类型的 NavigableString对象,但是当它出现在HTML文档中时,会使用特殊的格式输出,示例代码如下:

>>> # 导入BeautifulSoup
>>> from bs4 import BeautifulSoup

>>> markup = "<b><!--Hey, buddy. Want to buy a used parser?--></b>"
>>> soup = BeautifulSoup(markup, 'lxml')
>>> comment = soup.b.string
>>> print(comment)
Hey, buddy. Want to buy a used parser?

3.搜索特定节点并获取其中的链接及文本

BeautifulSoup库中定义了很多搜索方法,常用的有find方法、find_all方法,还可以通过CSS选择器进行搜索。find()方法返回直接的结果,find_all()方法返回一个包含结果的列表,基本语法如下:

find(name, attrs, recursive, string, **kwargs)
find_all(name, attrs, recursive, string, **kwargs)

参数说明:

  • name:需要查找的Tag名字
  • attrs:可以定义一个字典参数来搜索包含特殊属性的tag,按照CSS类名搜索tag,但标识CSS类名的关键字 class 在Python中是保留字,使用 class 做参数会导致语法错误,可以通过 class_参数搜索有指定CSS类名的tag
  • recursive:调用find_all()方法时,Beautiful Soup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数 recursive=False
  • string:可以搜索文档中的字符串内容。string参数接受字符串, 正则表达式,列表, True
  • **kwargs:若一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字的Tag对象的属性来搜索。

使用BeautifulSoup库解析某网站二手房详情页中的信息,如代码3-7所示:

# 导包
import requests
from bs4 import BeautifulSoup


# 定义LianJiaSpider类
class LianJiaSpider:

    # 定义初始化函数
    def __init__(self):
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/'
                          '537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36'}

    # 定义发送请求函数
    def get_page(self, url):
        response = requests.get(url=url, headers=self.headers).content.decode('utf-8')
        soup = BeautifulSoup(response, 'lxml')
        return soup

    # 定义解析函数,解析详情页的url地址
    def parse_url(self, soup):
        li_list = soup.find_all('li', 'clear LOGVIEWDATA LOGCLICKDATA')
        for li in li_list:
            # 解析每套房屋标题中的链接
            detail_url = li.find('a', 'noresultRecommend img LOGCLICKDATA').get('href')
            # 向详情页链接发送请求
            detail_soup = self.get_page(detail_url)
            # 调用parse()函数解析详情页信息
            self.parse(detail_soup)

    # 定义详情页解析函数
    def parse(self, detail_soup):
        # 创建空子典保存解析出的房屋信息
        item = {}
        # 解析详情页房屋标题
        item['title'] = detail_soup.find('h1', 'main').string
        # 解析详情页房屋所在小区
        item['community'] = detail_soup.find('a', 'info').string
        # 解析详情页属性值为"mainInfo"的div标签
        main_list = detail_soup.find_all('div', 'mainInfo')
        # 解析详情页房屋户型
        item['house_type'] = main_list[0].string if main_list[0] else None
        # 解析详情页房屋面积
        item['area'] = main_list[2].string if main_list[2] else None
        # 解析详情页房屋单价
        item['unit_price'] = detail_soup.select('.unitPrice>span')[0].text
        # 解析详情页房屋总价
        item['total_price'] = detail_soup.find('span', 'total').string
        print(item)

    # 定义运行函数
    def run(self, url):
        tree = self.get_page(url)
        self.parse_url(tree)


if __name__ == '__main__':
    # 调用类创建实例对象
    spider = LianJiaSpider()
    url = 'https://xx.xxxxxx.com/ershoufang/'
    # 调用run函数运行程序
    spider.run(url)

BeautifulSoup支持大部分的CSS选择器在Tag或BeautifulSoup对象的select()方法中传入字符串参数,即可使用CSS选择器的语法找到tag,示例如下:

>>> from bs4 import BeautifulSoup

>>> html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""
>>> # 创建BeautifulSoup对象
>>> soup = BeautifulSoup(html_doc, 'html.parser')

>>> # 通过tag查找:
>>> soup.select("title")
[<title>The Dormouse's story</title>]

>>> soup.select("p nth-of-type(3)")
[<p class="story">...</p>]

>>> # 通过tag标签逐层查找:
>>> soup.select("body a")
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
<a class="sister" href="http://example.com/lacie"  id="link2">Lacie</a>,
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

>>> soup.select("html head title")
[<title>The Dormouse's story</title>]

>>> # 找到某个tag标签下的直接子标签:
>>> soup.select("head > title")
[<title>The Dormouse's story</title>]

>>> soup.select("p > a")
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
<a class="sister" href="http://example.com/lacie"  id="link2">Lacie</a>,
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

>>> soup.select("p > a:nth-of-type(2)")
[<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

>>> soup.select("p > #link1")
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]

>>> soup.select("body > a")
[]
 类似资料: