Python数据抓取入门:用requests和BeautifulSoup提取静态网页内容

唐可
唐可
发布时间:2026-11-11| 6人浏览过
原创

本文系统讲解使用Python的requests与BeautifulSoup库抓取静态网页内容的完整方法。涵盖HTTP请求构建、HTML文档树解析、find与CSS选择器定位策略、常见异常处理与代码实操,帮助读者扎实掌握静态网页数据提取的核心技术。

在Python中获取静态网页数据的核心路径是两步分工:先使用 requests 库向目标服务器发送HTTP请求,把服务器直接返回的原始HTML文本下载到本地内存;再使用 BeautifulSoup 将无序的HTML字符串解析为有层级结构的文档对象模型(DOM树),通过标签名、CSS类名或层级选择器精确定位目标节点,提取其中的纯文本与标签属性。这种方案不需要启动沉重的浏览器内核,执行速度极快、系统资源开销小,是处理所有服务器端直接渲染页面的标准入门手段。
Python数据抓取入门:用requests和BeautifulSoup提取静态网页内容
全景概览:从目标网页发起HTTP请求到BeautifulSoup结构化数据提取链路

一、静态网页的特征与双库分工机制

在编写数据提取代码之前,需要先明确抓取对象是否为“静态网页”。所谓静态网页,指的是服务器响应的原始HTML正文中已经直接包含了页面呈现的全部文字和链接信息(即服务端渲染SSR)。验证方法很简单:在浏览器中打开目标页面,右键点击“查看网页源代码”(或按下快捷键 Ctrl + U / Cmd + Option + U),在弹出的源码中使用 Ctrl + F 搜索你想要的数据。如果在源码中能直接搜到对应文字,该页面就属于静态内容,完全适用本文的方案;如果在源码中找不到、只在F12“检查元素”的Elements面板中能看到,则说明内容是通过JavaScript在前端异步加载的,需要通过抓包分析接口或借助自动化工具处理。

明确了目标类型后,许多初学者容易产生一个疑问:为什么抓取网页需要同时使用 requests 和 BeautifulSoup 两个独立的库?

这是因为数据抓取过程包含两个完全不同的技术维度:网络通信与文档解析。

  • requests 负责“拿回数据”:它是一个底层的HTTP客户端库,只管向指定的网络URL发送GET或POST请求,处理TCP握手、伪装请求头(Headers)、携带Cookie、获取状态码,并将服务器返回的字节流转化为Python字符串(response.text)。但 requests 并不理解HTML语法,在它眼中,网页源码只是一段毫无结构的长字符串,无法直接按“第2个标题”或“类名为price的标签”进行筛选。
  • BeautifulSoup 负责“解析提取”:它是一个纯粹的HTML/XML文档解析工具,接收上一阶段拿到的HTML字符串,在内存中构建出树状的DOM模型,并提供类似 find()、find_all() 和 CSS 选择器等直观API,帮助开发者轻松遍历树分支、定位特定标签并剔除无关样式代码。但 BeautifulSoup 本身不具备任何网络收发功能,无法脱离字符串输入独立运行。
Python数据抓取入门:用requests和BeautifulSoup提取静态网页内容
图1:requests 与 BeautifulSoup 的职责边界与数据接力关系

二、准备运行环境与依赖安装

在开始编写代码前,需要在Python运行环境中安装这两个第三方库。打开操作系统的终端(Terminal或CMD),执行以下命令:

pip install requests beautifulsoup4

安装完成后,可以通过运行一行简单的Python测试命令来验证环境是否就绪:

python -c "import requests; from bs4 import BeautifulSoup; print('环境安装成功')"

解析器说明:BeautifulSoup 在初始化时需要指定底层解析引擎。Python标准库自带了 html.parser,它无需安装额外的C语言编译依赖,跨平台兼容性好且足够应付大多数常规页面。如果后续需要处理超大规模或极其残破的网页文档,也可以按需安装运行速度更快的 lxml 解析器(pip install lxml),并在代码中将解析器参数切换为 'lxml'。

三、第一步:使用 requests 发起请求并获取网页源码

获取网页内容的第一步,是通过 requests.get() 向目标URL发送HTTP请求。为了写出稳健的抓取代码,有三个关键细节必须显式处理:

  1. 伪装 User-Agent 请求头:许多网站为了防止简陋脚本消耗带宽,会直接拦截带有默认Python标识的请求。如果不传任何自定义请求头,requests 会发送形如 python-requests/x.xx.x 的标头,极易直接触发403 Forbidden错误。因此,应主动传入包含主流浏览器标识的字典。
  2. 显式设置网络超时(timeout):网络请求可能因目标服务器无响应或路由波动而陷入长时间挂起。如果不指定 timeout 参数,程序可能会永远停在发起请求的行上,阻塞整个批处理任务。通常推荐设置合理的秒数(例如 timeout=10)。
  3. 检查响应状态码:通过 response.status_code == 200 或调用 response.raise_for_status() 来断言请求是否真正成功。当服务器返回 404 或 500 等错误时,raise_for_status() 会立即抛出 requests.exceptions.HTTPError 异常,便于及时在异常分支中记录。
import requests

url = "https://httpbin.org/html"
headers = {
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
}

try:
    response = requests.get(url, headers=headers, timeout=10)
    response.raise_for_status()
    # 乱码预防:若默认推断为 ISO-8859-1,使用 apparent_encoding 重新矫正
    if response.encoding.lower() == "iso-8859-1":
        response.encoding = response.apparent_encoding
    
    html_content = response.text
    print("成功获取网页源码,字符长度:", len(html_content))
except requests.exceptions.RequestException as e:
    print(f"请求失败: {e}")
Python数据抓取入门:用requests和BeautifulSoup提取静态网页内容
图2:静态网页内容抓取的标准4步执行流程

四、第二步:使用 BeautifulSoup 解析 HTML 结构

拿到 response.text 后,我们需要将它交给 BeautifulSoup 处理。创建 BeautifulSoup 对象的语法非常简洁:

from bs4 import BeautifulSoup

soup = BeautifulSoup(html_content, "html.parser")

这一步执行完成后,变量 soup 便成为了一个包含了整张网页全部DOM树节点的对象。此时,HTML中那些可能缺失闭合标签(如缺少 </p> 或 </li>)的不规范语法已经被解析器自动修复,整个文档被整齐地组织成了父节点、子节点、兄弟节点互相连接的多叉树结构。

五、第三步:精确定位目标标签与提取数据

定位HTML中的目标元素通常有两种主流方式:原生查找方法与CSS选择器。掌握这两种方式的适用场景,可以灵活应对不同复杂度的网页排版。

1. 原生方法查找:find() 与 find_all()

这是 BeautifulSoup 最直观的API体系:

  • find(name, attrs, ...):在树结构中查找第一个符合条件的标签。匹配成功则返回一个 bs4.element.Tag 对象;若没找到则返回 None。
  • find_all(name, attrs, limit=None, ...):查找所有符合条件的标签,返回包含多个 Tag 对象的 ResultSet 列表。若没找到则返回空列表 []。

注意 class 属性关键字冲突:因为在 Python 中 class 是用于定义类的保留关键字,所以不能直接写 find('div', class='item')。BeautifulSoup 特别将参数名命名为 class_(带下划线),例如 soup.find('div', class_='article-title');或者使用通用字典参数 attrs={'class': 'article-title'}。

2. CSS选择器定位:select_one() 与 select()

如果你有前端开发经验,或者习惯直接使用浏览器F12开发者工具中的“Copy Selector(复制选择器)”,使用CSS选择器会更加高效快捷:

  • select_one("css_selector"):返回匹配该CSS选择器的首个 Tag 对象,找不到时返回 None。
  • select("css_selector"):返回匹配该CSS选择器的所有 Tag 对象列表。

常见的CSS选择器语法包括:.intro(类名选择器)、#main-content(ID选择器)、div.card > h3(直接子元素选择器)、ul.list li a(后代层级选择器)以及 a[href^="https"](属性筛选)。

Python数据抓取入门:用requests和BeautifulSoup提取静态网页内容
图3:DOM节点定位策略与方法对比(原生方法 vs CSS选择器)

3. 提取纯文本与标签属性

在通过上述方法锁定目标 Tag 对象后,便可以从节点中取出最终所需的数据:

  • 提取内部纯文本:使用 tag.get_text(strip=True)。该方法会自动剥离当前标签及其内部所有子标签的HTML代码,仅提取可读文字;传入 strip=True 可以自动去除文本首尾多余的换行符与空格。
  • 提取标签属性:Tag 对象支持像 Python 字典一样读取属性值。例如提取超链接地址:tag['href'] 或 tag.get('href')。在实际编码中,更推荐使用 tag.get('href'),当标签偶尔遗漏该属性时它会返回 None,而不会直接抛出 KeyError 中断程序。

六、完整实战示例:抓取静态列表并输出结构化数据

为了直观演示全套逻辑的配合方式,下面提供一段包含“模拟静态HTML结构”、“文档树解析”、“多节点遍历”、“健壮性判空提取”以及“输出清洗后列表字典”的完整可运行代码。

import requests
from bs4 import BeautifulSoup

# 此处使用一段典型的静态文章列表HTML字符串作为演示目标(实际开发中替换为 requests.get(url).text)
html_sample = """
<div class="container">
    <div class="post-card">
        <h2 class="post-title"><a href="/posts/101.html">Python数据分析初探</a></h2>
        <span class="publish-date">2026-10-15</span>
        <p class="summary">介绍Pandas和NumPy的核心操作与数据过滤方法。</p>
    </div>
    <div class="post-card">
        <h2 class="post-title"><a href="/posts/102.html">Linux常用基础命令备忘</a></h2>
        <span class="publish-date">2026-10-20</span>
        <p class="summary">系统维护、文件权限管理及常用网络诊断命令汇编。</p>
    </div>
    <div class="post-card">
        <h2 class="post-title"><a href="/posts/103.html">Git分支管理与协同工作流</a></h2>
        <span class="publish-date">2026-10-28</span>
        <!-- 模拟部分标签缺少summary摘要的情况 -->
    </div>
</div>
"""

def extract_posts_data(html_text):
    soup = BeautifulSoup(html_text, "html.parser")
    # 步骤1:定位所有的文章卡片父容器
    cards = soup.select("div.container > div.post-card")
    results = []

    # 步骤2:遍历每一张卡片,逐一提取内部字段
    for card in cards:
        # 定位标题链接元素
        title_tag = card.select_one("h2.post-title > a")
        date_tag = card.find("span", class_="publish-date")
        summary_tag = card.find("p", class_="summary")

        # 步骤3:安全提取属性与文本(包含空值保护)
        title = title_tag.get_text(strip=True) if title_tag else "无标题"
        link = title_tag.get("href", "") if title_tag else ""
        date = date_tag.get_text(strip=True) if date_tag else "未知日期"
        summary = summary_tag.get_text(strip=True) if summary_tag else "暂无摘要"

        results.append({
            "title": title,
            "link": link,
            "date": date,
            "summary": summary
        })
    return results

if __name__ == "__main__":
    extracted_data = extract_posts_data(html_sample)
    print(f"成功解析出 {len(extracted_data)} 篇文章数据:")
    for item in extracted_data:
        print(item)

运行上述代码,控制台将输出清晰规整的结构化字典列表(示例输出):

成功解析出 3 篇文章数据:
{'title': 'Python数据分析初探', 'link': '/posts/101.html', 'date': '2026-10-15', 'summary': '介绍Pandas和NumPy的核心操作与数据过滤方法。'}
{'title': 'Linux常用基础命令备忘', 'link': '/posts/102.html', 'date': '2026-10-20', 'summary': '系统维护、文件权限管理及常用网络诊断命令汇编。'}
{'title': 'Git分支管理与协同工作流', 'link': '/posts/103.html', 'date': '2026-10-28', 'summary': '暂无摘要'}

七、抓取过程中的常见问题与防护处理

在真实的抓取场景中,网络环境的不确定性与网页结构的多变性往往会导致脚本报错。在编写生产或批处理任务时,需重点防范以下四类常见隐患:

1. AttributeError: 'NoneType' object has no attribute 'get_text'

这是初学者遇到频率最高的问题。产生原因是调用了 find() 或 select_one() 但没有匹配到任何元素,返回了 None,紧接着直接调用了 .get_text()。解决方法是永远不要假设页面中的每个标签都必然存在,在提取文本或属性前进行判空,例如使用条件表达式:

target = soup.find('h1')
content = target.get_text(strip=True) if target is not None else ""

2. 中文页面乱码问题

当抓取部分国内网站时,可能会出现文字全变成 å容 或类似符号的情况。这是因为 HTTP 标头中未标明 charset 或误标为 ISO-8859-1。解决方式是在读取 response.text 前加入编码重置:

response.encoding = response.apparent_encoding

apparent_encoding 会根据网页内容的实际字节特征自动推算编码(通常为 UTF-8 或 GB18030/GBK),能够有效解决绝大多数中文乱码。

3. 网络异常与超时保护

必须使用 try...except requests.exceptions.RequestException 包裹网络请求段落,捕获包括连接超时(Timeout)、主机名无法解析(ConnectionError)在内的各类网络错误,避免一个页面的瞬时网络波动直接导致整个爬虫进程崩溃退出。

4. 遵守抓取合规与礼貌原则

在批量抓取静态页面时,每次请求之间应当加入合理的延时(如 time.sleep(1)),避免因并发过高对目标服务器造成负荷冲击;同时应优先查阅目标站点的 /robots.txt 协议文件,明确允许爬取的路径范围,只抓取公开且合法授权的数据。

Python数据抓取入门:用requests和BeautifulSoup提取静态网页内容
图4:抓取全生命周期容错与生产级健壮性闭环

八、全流程总结与方案适用边界

回顾静态网页内容提取的完整过程,本质是一套严密的模块化数据管道:由 requests 负责安全可靠地建立网络连接并取回原始HTML,再由 BeautifulSoup 负责将文本映射为DOM树并执行节点提取与规整。

需要牢记的是该方案的适用边界:它只适用于服务器端直接渲染出完整HTML的静态网页。当遇到需要用户交互后才加载的数据、由前端框架(如Vue、React)通过异步接口动态渲染的内容,或者需要滑动滚轮触发懒加载的列表时,requests.get() 拿到的源码中将不会包含这些动态数据。面对动态场景,应当优先打开浏览器的 Network 面板寻找真正传输数据的 Fetch/XHR 接口,或者转而使用 Playwright、Selenium 等能够驱动完整浏览器渲染环境的自动化工具。

相关专题:

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

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 算法库。