Python爬虫入门:用requests和BeautifulSoup抓取静态网页数据

陈默
陈默
发布时间:2026-11-09| 8人浏览过
原创

本教程针对初学者,深入浅出地讲解如何使用Python的requests库与BeautifulSoup4库抓取并解析静态网页数据。内容涵盖HTTP通信原理、User-Agent伪装、DOM节点定位、异常防御及完整实战代码示例。

技术教程 主题:网络数据抓取与DOM解析 环境:Python 3.x / requests / BeautifulSoup4
Python爬虫入门:用requests和BeautifulSoup抓取静态网页数据
图 0:requests 与 BeautifulSoup 在静态网页抓取中的协作分工
静态网页数据抓取的核心逻辑可以高度概括为两步:使用 requests 模拟浏览器向服务器发起请求并取回 HTML 源码,接着使用 BeautifulSoup 将源码转换为树状对象并筛选提取所需数据。只要目标页面的数据直接存在于服务端返回的 HTML 文本中,这套经典组合就是最简单、最稳健的基础方案。本文将从抓取原理、环境准备、逐步解析到完整可运行案例,完整拆解这一操作过程。

一、静态网页抓取的核心运作原理

在开始写代码前,需要明确什么是“静态网页”。所谓静态网页,是指当客户端向服务器发出 URL 请求后,服务器直接返回已经渲染完整的 HTML 文档。用户在网页上看到的所有文字、列表和链接,已经全部以纯文本形式存在于这段 HTML 源码中。这与依靠前端 JavaScript 脚本异步拉取 JSON 数据再在浏览器本地渲染的“动态网页”有着本质区别。

正因为数据已经就绪,我们不需要在本地启动庞大沉重的完整浏览器内核(如 Selenium 或 Playwright),只需要通过网络通信库拿到 HTML 字符串,再交给解析库进行标签筛选即可。

Python爬虫入门:用requests和BeautifulSoup抓取静态网页数据
图 1:静态网页数据抓取的完整体系架构与数据流转路径

整个处理过程分为四个紧密衔接的环节:

  • 请求发起(requests):组装目标网址与必要的 HTTP 请求头(重点是 User-Agent 标识),向服务端发起标准的 HTTP GET 请求。
  • 网络传输与响应:目标服务器接收请求并返回状态码(如 200 表示成功),同时回传 HTML 字节流。
  • 语法建模(BeautifulSoup4):解析器(内置的 html.parser 或第三方 lxml)读取 HTML 字符串,自动修补缺失的闭合标签,在内存中构建起树状的 DOM(文档对象模型)节点结构。
  • 节点定位与提取:通过标签名、CSS 类名、ID 或层级选择器,遍历定位到目标节点,读取其中的文本或属性值,最终存入列表或保存为文件。

二、环境准备与依赖安装

完成本项目需要 Python 3.6 及以上环境。核心依赖为两个第三方库:用于网络请求的 requests 和用于解析 HTML 的 beautifulsoup4。同时,推荐安装高性能解析器 lxml 作为底层支持(如果不安装,也可以使用 Python 自带的 html.parser)。

打开终端或命令行工具,执行以下安装命令:

# 建议使用 pip 安装核心依赖
pip install requests beautifulsoup4 lxml

安装完成后,可以在 Python 交互环境中导入模块以验证是否就绪:

import requests
from bs4 import BeautifulSoup

print(requests.__version__)
print(BeautifulSoup)

如果没有抛出 ModuleNotFoundError 错误,说明运行环境已经准备完毕。

三、分步实战:从发送请求到定位解析

为了让抓取过程条理清晰,我们按照标准的开发顺序拆解为五个关键步骤,并逐一分析其中的代码写法与注意事项。

Python爬虫入门:用requests和BeautifulSoup抓取静态网页数据
图 2:静态网页爬取全流程规范操作步骤指南

第 1 步:用 requests 发起 HTTP GET 请求

发起请求时,最容易被初学者忽视的是 User-Agent(用户代理)。很多网站的服务器如果检测到请求头中包含 Python 默认的 python-requests/x.x.x 标识,会直接返回 403 Forbidden 拦截访问。因此,必须在请求头中附带常规浏览器的标识信息。

import requests

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"
}

# 设置 10 秒超时,防止网络卡顿造成程序永久挂起
response = requests.get(url, headers=headers, timeout=10)

# 检查 HTTP 状态码
if response.status_code == 200:
    print("请求成功,网页字节长度:", len(response.text))
else:
    print(f"请求失败,状态码:{response.status_code}")
关于中文乱码的提醒

如果抓取中文网站时发现控制台输出乱码,通常是因为 requests 根据 HTTP 头猜错了编码(例如误判为 ISO-8859-1)。此时只需在读取 response.text 前加入一行代码:response.encoding = response.apparent_encoding,即可自动根据网页实际字节内容矫正编码。

第 2 步:构建 BeautifulSoup 解析对象

拿到 HTML 字符串后,需要将其交给 BeautifulSoup 实例化为 DOM 对象。构造函数接收两个参数:HTML 文本内容,以及所选用的解析引擎。

from bs4 import BeautifulSoup

# 优先使用速度更快的 lxml;若未安装可换用 "html.parser"
soup = BeautifulSoup(response.text, "html.parser")

# 测试读取页面标题标签
print("页面标题:", soup.title.string)

第 3 步:熟练运用核心定位器定位元素

BeautifulSoup 提供了两套非常便捷的定位风格:基于方法签名的 find() / find_all(),以及基于前端样式的 select() / select_one() CSS 选择器。下表对比了两者的差异与使用习惯:

操作目标 find / find_all 风格 select (CSS 选择器) 风格 返回值类型
查找符合条件的第一个节点 soup.find('div', class_='item') soup.select_one('div.item') Tag 对象(未找到返回 None)
查找所有符合条件的节点 soup.find_all('div', class_='item') soup.select('div.item') Tag 对象构成的列表(未找到返回空列表)
提取标签内部文字 tag.get_text(strip=True) tag.get_text(strip=True) 字符串(去除首尾空白)
提取标签属性(如 href、src) tag.get('href') 或 tag['href'] tag.get('href') 字符串属性值
Python爬虫入门:用requests和BeautifulSoup抓取静态网页数据
图 3:HTML 节点层级与 BeautifulSoup 定位代码对照图

在实际抓取列表中,通常采用“两级查找法”:先定位到包裹每一条数据的最外层容器节点(例如循环 soup.find_all('div', class_='quote')),再在每个子节点内部查找该条目专属的标题、作者和标签。这样做能确保每个字段严格对应所属条目,绝不会发生错位。

四、完整可运行的实战案例

下面是一个完整的可执行 Python 脚本,以公开的名言练习网站(quotes.toscrape.com)为例,演示从网络请求、DOM 树提取、异常捕获到清洗输出的完整流程:

import requests
from bs4 import BeautifulSoup

def scrape_quotes(page_url):
    # 1. 配置请求头
    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:
        # 2. 发起网络请求
        response = requests.get(page_url, headers=headers, timeout=10)
        response.raise_for_status()  # 若响应状态码不是 200 则主动触发 HTTPError
        
        # 自动纠正网页编码
        response.encoding = response.apparent_encoding
    except requests.exceptions.RequestException as err:
        print(f"网络请求出现异常: {err}")
        return []

    # 3. 构造解析对象
    soup = BeautifulSoup(response.text, "html.parser")
    
    # 4. 寻找所有名言卡片节点
    quote_elements = soup.find_all("div", class_="quote")
    results = []

    # 5. 遍历每个卡片并防御性提取字段
    for item in quote_elements:
        # 提取名言正文
        text_node = item.find("span", class_="text")
        text = text_node.get_text(strip=True) if text_node else ""

        # 提取作者
        author_node = item.find("small", class_="author")
        author = author_node.get_text(strip=True) if author_node else "未知"

        # 提取标签列表 (同一个卡片下有多个 a.tag)
        tag_nodes = item.find_all("a", class_="tag")
        tags = [t.get_text(strip=True) for t in tag_nodes]

        results.append({
            "quote": text,
            "author": author,
            "tags": tags
        })

    return results


if __name__ == "__main__":
    target_url = "https://quotes.toscrape.com/"
    data = scrape_quotes(target_url)
    
    print(f"成功抓取到 {len(data)} 条名言数据:
")
    for idx, entry in enumerate(data[:3], start=1):
        print(f"[条目 {idx}]")
        print(f"  内容: {entry['quote']}")
        print(f"  作者: {entry['author']}")
        print(f"  标签: {', '.join(entry['tags'])}")
        print("-" * 50)

运行结果示例(非实际网络输出,仅展示数据格式):

成功抓取到 10 条名言数据:

[条目 1]
  内容: “The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”
  作者: Albert Einstein
  标签: change, deep-thoughts, thinking, world
--------------------------------------------------
[条目 2]
  内容: “It is our choices, Harry, that show what we truly are, far more than our abilities.”
  作者: J.K. Rowling
  标签: abilities, choices
--------------------------------------------------

五、开发中的常见问题排查与避坑准则

在编写爬虫的过程中,初学者最常遭遇四类挫折:网络请求超时、中文字符乱码、NoneType 报错以及找不到节点。理解它们的成因与应对决策,是写出健壮爬虫的关键。

Python爬虫入门:用requests和BeautifulSoup抓取静态网页数据
图 4:静态网页爬虫核心问题排查矩阵与防御性编程指南
  1. 严防 AttributeError: 'NoneType' object has no attribute 'get_text':
    这是使用 BeautifulSoup 最频繁出现的报错。原因在于调用 find() 未找到指定标签,返回了 None,紧接着直接调用 .get_text() 或 .text 就会直接崩溃。因此提取前务必做非空校验(如三元表达式 node.text if node else "")。
  2. 排查“浏览器里有,抓下来却没有”的假象:
    如果在浏览器控制台审查元素时明明看得到某个 <div>,但用 Python 打印 response.text 却搜索不到,通常是因为该部分内容是网页加载后通过 Ajax / JavaScript 异步渲染的。对于这种内容,仅靠 requests 无法执行 JS 脚本,需要使用浏览器开发者工具中的 Network(网络)选项卡排查真实的后端 JSON 接口,或者使用 Selenium / Playwright 等无头浏览器进行渲染抓取。
  3. 合理控制请求频率(频控与礼貌爬取):
    循环抓取多页数据时,切忌在 for 循环中无间隔高频并发请求。应导入 time 模块,在每次请求后调用 time.sleep(1) 暂停片刻。这既是对目标服务器资源的尊重,也能有效避免触发服务器的防御性封锁。
总结:requests 负责“拿到网页”,BeautifulSoup 负责“拆解提取”。掌握这套流水线后,面对任何传统的静态内容站点,只需右键“查看网页源代码”,找准对应标签的 class 或 id,即可快速完成数据抽取。
相关专题:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。