BeautifulSoup 和 Selenium | Jim Zhang's blog
BeautifulSoup 和 Selenium2022-09-19

这是 Jim 在面试清华大学清研学术讲师团时写的稿子,主要围绕 Jim 设计的一套《Python 数据分析》课程其中的《网络数据获取技术》这一讲进行的讲解。好在通过了,还被邀请作为 Python 系列课程的负责人,要是没通过这篇文章就发不出来了。😂

安装

BeautifulSoup 得名于《爱丽丝梦游仙境》第十章:

Beautiful Soup, so rich and green,
Waiting in a hot tureen!
Who for such dainties would not stoop?
Soup of the evening, beautiful Soup!
Soup of the evening, beautiful Soup!
...
Chapter 10, Alice's Adventures in Wonderland, Lewis Carroll.

意为“化腐朽为神奇”。BeautifulSoup 和 Selenium 的安装很方便,只需要一行命令:

$ pip install BeautifulSoup4, selenium

请注意,不是 pip install BeautifulSoup,而是 pip install BeautifulSoup4,因为前者指的是 BeautifulSoup3

安装完成后,我们可以通过 python 交互式控制台来检验是否成功安装,比如:

Python 3.10.7 (tags/v3.10.7:6cc6b13, Sep  5 2022, 14:08:36) [MSC v.1933 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from bs4 import BeautifulSoup, import selenium
>>>

如果没有报错信息,就代表安装成功啦。

BeautifulSoup

本地 html 文件

首先,我们可以先用一个本地 html 文件做测试,这个文件内容如下:

<!--test.html-->
<html>
    <head>
        <title>这是测试。</title>
    </head>
    <body>
        <h1>Test</h1>
        <p>这是一个测试。</p>
        <p>这是第二个测试。</p>
    </body>
</html>

它对应的 DOM(Document Object Model,文档对象模型)树如下:

Document
└── html
    ├── head
    │   └── title
    └── body
        ├── h1
        ├── p
        └── p

这个树形结构为我们讲述了如下信息:

  • Document 是整个文档的根节点,它的子节点是 html
  • html 是文档的根元素,它的子节点是 headbody 等。
  • head 是文档的头部,它的子节点是 title 等。
  • body 是文档的主体,它的子节点是 h1p 等。
  • titleh1p 等都是文档的叶子节点,它们没有子节点。

然后,我们可以通过 BeautifulSoup 来读取这个文件,代码如下:

from bs4 import BeautifulSoup

with open('test.html', 'r', encoding='utf-8') as f:
    soup = BeautifulSoup(f, 'html.parser')

print(soup)

输出结果如下:

<html>
<head>
<title>这是测试。</title>
</head>
<body>
<h1>Test</h1>
<p>这是一个测试。</p>
<p>这是第二个测试。</p>
</body>
</html>

这样,我们就可以通过 soup 来访问这个 html 文件了。进一步地,我们可以通过 soup 来访问它的子节点,比如:

print(soup.html.head.title)

输出结果如下:

<title>这是测试。</title>

我们可以看到,soup.html.head.title 就是 title 节点,对于整个文档唯一的节点(title 就是这样),我们可以通过 soup.title 来直接访问它。但是对于非唯一的节点就比较麻烦,比如第一个 p 节点,我们可以通过 soup.body.p 来访问它,但是如果我们想要访问第二个 p 节点,就没办法了。这时候,我们可以通过 find_all 方法来访问它们,比如:

print(soup.find_all('p'))

输出结果如下:

[<p>这是一个测试。</p>, <p>这是第二个测试。</p>]

获取标签中的内容,对每个 tag 使用 text 属性即可,比如:

print(soup.title.text)

输出结果如下:

这是测试。

网页

接下来,我们来看看如何使用 BeautifulSoup 来爬取网页。首先,为了利用python 帮我们完成发送请求的操作,我们需要导入 requests 库(安装方法我们就不重复介绍了),然后通过 requests 来获取网页的内容。

现在有这样一个网页:https://q-weather.info/weather/54399/history/?date=2022-09-16,其内容大致如下

存储了海淀区的天气信息。现在想获取这个网页数据,我们如下操作:

import requests, pandas as pd
from bs4 import BeautifulSoup

url = 'https://q-weather.info/weather/54399/history/'
params = {'date': '2022-09-16'}

r = requests.get(url, params=params)
soup = BeautifulSoup(r.text, 'html.parser')

这样这个 soup 就存储这个网页的内容了。接下来的任务显而易见:首先获取表格的标题行,然后是数据单元格,最后是把他们存储起来。

table = soup.table
columns = [th.text for th in table.thead.find_all('th')]
data = [[td.text for td in tr.find_all('td')] for tr in table.tbody.find_all('tr')]

df = pd.DataFrame(data, columns=columns)
print(df.head)

输出结果如下:

                        时次  瞬时温度    地面气压 相对湿度     瞬时风向 瞬时风速 1小时极大风速 1小时降水 10分钟平均能见度
0   2022-09-16 01:00 +0800  20.1  1002.5   87           0.0     0.4   0.0       9.3
1   2022-09-16 02:00 +0800  19.5  1002.1   91    45/NE  0.5     0.5   0.0       5.4
2   2022-09-16 03:00 +0800  19.2  1001.9   91  290/WNW  1.4     1.6   0.0       8.1
3   2022-09-16 04:00 +0800  19.6  1001.7   92   25/NNE  0.3     2.1   0.0       4.9
4   2022-09-16 05:00 +0800  18.9  1001.7   93   28/NNE  0.4     1.0   0.0       4.0

这就是 BeautifulSoup 的基本用法了,更多的用法可以参考官方文档:https://www.crummy.com/software/BeautifulSoup/bs4/doc/

Selenium

Selenium 是一个自动化测试工具,它可以模拟浏览器的操作,比如点击、输入、滚动等等。它的主要功能是自动化测试,但是它也可以用来爬取网页。它的优点是可以模拟浏览器的操作,可以模拟人的操作,比如滚动、点击等等,这样就可以爬取一些动态加载的网页了。

安装

前文已经提到了 Selenium 的安装方法,这里就不再重复介绍了。但是除了安装 Selenium 之外,我们还需要安装一个浏览器驱动,这个驱动的作用是让 Selenium 可以控制浏览器。这里我们以 Chrome 浏览器为例,安装 Chrome 浏览器驱动。(Edge 浏览器的话需要额外安装 msedge-selenium-tools

Windows

https://sites.google.com/chromium.org/driver/ 下载对应版本的 Chrome 浏览器驱动,并将其目录添加到 PATH 环境变量中即可。

Mac OS & Linux

在 Mac OS 和 Linux 上安装 Chrome 浏览器驱动比较简单,利用包管理器安装就可以了。

# Mac OS
brew install chromedriver
# Linux (debian-like)
sudo apt-get install chromedriver

使用

Selenium 的使用方法很简单,首先导入 Seleniumwebdriver 模块,然后创建一个 webdriver 对象,这个对象就是浏览器的控制器,然后就可以通过这个对象来控制浏览器了。

from selenium import webdriver

driver = webdriver.Chrome()

注意,这里请确保你的 chromedriver 的版本和你的 Chrome 浏览器的版本是一致的,且 chromedriver 要在 PATH 环境变量中(否则要指定路径)。

然后就是访问网页了,这里我们还是以前面的例子为例,访问 https://q-weather.info/weather/54399/history/?date=2022-09-16

driver.get('https://q-weather.info/weather/54399/history/?date=2022-09-16')

chromedriver 会自动打开一个 Chrome 浏览器窗口,然后访问指定的网页。

Selenium

然后我们就可以通过 Selenium 来获取网页存储的数据了。这里我们可以通过 find_element_by_id 方法来获取指定 id 的元素,然后通过 text 属性来获取元素的文本内容。