这是 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
是文档的根元素,它的子节点是head
、body
等。head
是文档的头部,它的子节点是title
等。body
是文档的主体,它的子节点是h1
、p
等。title
、h1
、p
等都是文档的叶子节点,它们没有子节点。
然后,我们可以通过 BeautifulSoup
来读取这个文件,代码如下:
from bs4 import BeautifulSoup
with open('test.html', 'r', encoding='utf-8') as f:
soup = BeautifulSoup(f, 'html.parser')
print(soup.prettify())
输出结果如下:
<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
的使用方法很简单,首先导入 Selenium
的 webdriver
模块,然后创建一个 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
来获取网页存储的数据了。这里我们可以通过 find_element_by_id
方法来获取指定 id
的元素,然后通过 text
属性来获取元素的文本内容。