Python爬虫入门:用Requests和BeautifulSoup抓取静态网页
假设你正在跟进一个图书排行榜页面,每天需要手动打开浏览器、复制书名与价格整理到表格里。面对这类结构固定且内容直接写在 HTML 源码中的静态网页,使用 Python 编写自动化脚本是最直接的解决方案。抓取静态网页的核心路径非常清晰:先使用 Requests 库下载服务器返回的完整 HTML 文档,再交给 BeautifulSoup 库解析 DOM 树结构并提取目标数据。
1. 什么是静态网页与基础分工
所谓“静态网页”,指的是浏览器发起 HTTP GET 请求后,服务端返回的 HTML 文本中已经包含了所有需要展示的文本、链接和排版信息,无需在浏览器端执行复杂的 JavaScript 脚本二次渲染。验证一个网页是否属于静态网页的最简单方法,是在浏览器页面空白处右键点击“查看网页源代码”(快捷键 Ctrl+U 或 Cmd+Option+U),并在源码中搜索页面上可见的文字。如果源码中能直接搜到,就属于可以直接抓取的静态内容。
在这个任务中,两个核心库各司其职:Requests 充当浏览器的“网络通信通道”,负责封装 HTTP 请求头、发送网络请求并接收原始数据;BeautifulSoup 则充当“数据提取放大镜”,负责将冰冷的纯文本解析成具有层级结构的节点树,方便我们定位与提取标签内的属性和文本。
2. 准备运行环境
在编写代码前,需要确保系统中已安装 Python(建议 Python 3.8 或更高版本),并在终端或命令提示符中通过 pip 工具安装所需的第三方库。我们同时安装用于解析 HTML 的 lxml 解析器,它的底层是 C 语言实现,相较于 Python 自带的 html.parser 速度更快且容错率更高:
pip install requests beautifulsoup4 lxml
安装完成后,可以在交互式终端中执行以下语句,验证库是否能正常导入:
python -c "import requests; from bs4 import BeautifulSoup; print('环境准备就绪')"
如果控制台输出“环境准备就绪”且没有任何报错,说明基础开发依赖已经正确配置。
3. 静态爬虫操作步骤
抓取并提取静态网页数据遵循一套标准的流水线作业,从发起请求直到拿到最终纯净数据,整个过程由四个连续动作构成。
第一步:使用 Requests 获取源码
直接通过 requests.get() 发送请求时,默认的 User-Agent 会标明自身是 Python 脚本,这往往会被许多网站的防火墙直接拦截返回 403 状态码。因此,规范的做法是附带浏览器请求头:
import requests
target_url = "https://quotes.toscrape.com/"
custom_headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
}
# 设置 timeout=10,避免因网络无响应导致程序无限挂起
response = requests.get(target_url, headers=custom_headers, timeout=10)
# 确保请求成功;若状态码非 200 会主动抛出异常
response.raise_for_status()
# 页面 HTML 纯文本
html_content = response.text
第二步:使用 BeautifulSoup 转换与定位节点
拿到 HTML 纯文本后,纯字符串操作(如正则查找)容易受到标签嵌套、换行和多余属性的干扰。BeautifulSoup 将这段文本解析成由节点构成的 DOM 树:
from bs4 import BeautifulSoup
soup = BeautifulSoup(html_content, "lxml")
在解析对象生成后,主要通过两种选择方式提取节点:
- 标准查找方法(find / find_all):
soup.find("div", class_="quote"):返回页面中匹配到的第一个<div class="quote">标签对象。soup.find_all("div", class_="quote"):返回一个包含全部匹配标签的列表,可直接使用for循环遍历。注意由于class是 Python 保留关键字,BeautifulSoup 统一使用class_参数来指定类名。
- CSS 选择器(select / select_one):
soup.select_one("div.quote"):按 CSS 选择器匹配首个元素。soup.select("div.quote span.text"):支持类选择器、后代选择器与属性选择器,对于熟悉前端 CSS 语法的读者更为直观。
4. 完整实战代码
以专门用于爬虫演练的公开测试网站 quotes.toscrape.com 为例,下面的脚本将完整演示从请求网页、循环解析每一个名言区块,到提取“名言内容”、“作者”及“详情链接”的全过程。
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin
def scrape_quotes():
base_url = "https://quotes.toscrape.com/"
headers = {
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36"
)
}
try:
response = requests.get(base_url, headers=headers, timeout=10)
response.raise_for_status()
except requests.exceptions.RequestException as error:
print(f"网络请求失败: {error}")
return
# 解析 HTML 内容
soup = BeautifulSoup(response.text, "lxml")
# 查找所有名言卡片节点
quote_blocks = soup.find_all("div", class_="quote")
print(f"成功定位到 {len(quote_blocks)} 条记录,开始解析:\n")
results = []
for item in quote_blocks:
# 1. 提取名言文字
text_element = item.find("span", class_="text")
quote_text = text_element.get_text(strip=True) if text_element else "无内容"
# 2. 提取作者姓名
author_element = item.find("small", class_="author")
author_name = author_element.get_text(strip=True) if author_element else "未知"
# 3. 提取作者主页链接(属性提取)
link_element = item.find("a", string="(about)")
if link_element and "href" in link_element.attrs:
relative_link = link_element["href"]
# 将相对路径拼接为完整绝对 URL
full_link = urljoin(base_url, relative_link)
else:
full_link = "无链接"
results.append({
"quote": quote_text,
"author": author_name,
"link": full_link
})
# 输出抓取结果
for idx, data in enumerate(results, start=1):
print(f"[{idx}] {data['author']}")
print(f" 名言: {data['quote']}")
print(f" 主页: {data['link']}")
if __name__ == "__main__":
scrape_quotes()
将上述代码保存为 scraper_demo.py 并运行。终端会按顺序输出格式化后的数据列表:
示例输出:
成功定位到 10 条记录,开始解析:
[1] Albert Einstein
名言: “The world as we have created it is a process of our thinking...”
主页: https://quotes.toscrape.com/author/Albert-Einstein
[2] J.K. Rowling
名言: “It is our choices, Harry, that show what we truly are...”
主页: https://quotes.toscrape.com/author/J-K-Rowling
5. 常见问题与排查思路
| 常见现象 | 产生原因 | 针对性解决方案 |
|---|---|---|
AttributeError: 'NoneType' object has no attribute 'get_text' |
find() 没有找到匹配的标签,返回了 None,直接调用方法导致报错。 |
在提取属性或文本前做非空判断(if element: ...),或先检查选择器是否因网页改版失效。 |
| 抓取中文内容出现乱码(如 Ã¥Â...) | Requests 推测的网页编码与服务器实际编码不一致。 | 手动指定解码方式:在调用 response.text 前设置 response.encoding = response.apparent_encoding。 |
| 返回状态码 403 Forbidden | 服务器反爬机制识别出请求未携带合法浏览器标识。 | 完善请求头中的 User-Agent、Referer 与 Accept-Language 等字段。 |
| 在浏览器控制台能看到数据,但代码抓取为空 | 该部分数据是前端通过 Ajax/Fetch 异步加载生成的,并非静态 HTML。 | 静态爬虫无法直接拿到动态数据。需通过浏览器的“网络 (Network)”面板抓取后端 API,或换用无头浏览器。 |
6. 流程总结
使用 Requests 和 BeautifulSoup 抓取静态网页是一套规律明确的工作流:先通过网络层获取准确的 HTML 文本,再通过解析层定位目标节点并清洗数据。在实际开发中,务必养成随时添加超时约束、异常防御和编码校验的习惯,以保障爬虫程序稳定运行。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









