第 14 课 | 进阶爬虫:API 分析 + Selenium 动态页面

第 13 课的 requests 爬虫只能处理静态 HTML。真实世界更复杂:有些网站数据来自 API 接口,有些页面由 JavaScript 动态渲染。这节课,我们攻克这两种场景。


一、业务价值:为什么需要进阶爬虫

1.1 第 13 课的局限性

第 13 课的爬虫很好,但遇到了两个现实问题:

场景第 13 课方案问题
虎嗅网新闻列表requests + BeautifulSoupHTML 里只有骨架,数据是 JS 异步加载的
澎湃新闻滚动加载requests + BeautifulSoup只能拿到首屏,滚动加载的内容拿不到
36氪 API 数据requests + BeautifulSoup明明有 JSON 接口,解析 HTML 费力不讨好

进阶爬虫要解决的核心问题:拿到第 13 课拿不到的数据。

1.2 两种进阶方案

数据来自 API
(JSON 接口)

JS 动态渲染
(滚动加载等)

目标网页

页面类型?

方案 A:API 分析
直接请求 JSON 接口

方案 B:Selenium
模拟浏览器行为

解析 JSON 数据
干净、高效、省带宽

等待页面渲染完成
再提取 HTML 内容


二、方案 A:API 接口分析(以虎嗅为例)

2.1 核心思路

很多现代网站采用前后端分离架构:前端只是壳,数据通过 API 接口以 JSON 格式返回。与其解析复杂的 HTML,不如直接请求 API——数据更干净、更高效。

2.2 如何找到 API 接口

打开 Chrome 开发者工具(F12),切换到 Network(网络) 面板,刷新页面:

服务器 浏览器 服务器 浏览器 打开 F12 → Network 面板 在 Network 中找到 JSON 接口 请求页面 HTML HTML(骨架,无数据) XHR/Fetch 请求数据 JSON 数据

关键步骤:

  1. 打开 F12 → Network 面板
  2. 点击 XHR 或 Fetch 过滤器(只看数据请求)
  3. 刷新页面,观察请求列表
  4. 找到返回 JSON 数据的请求(Preview 面板可预览)
  5. 右键 → Copy → Copy as cURL,分析请求参数

2.3 实战:分析新闻列表 API

以常见的新闻列表 API 为例,典型请求格式:

GET /api/v1/article/list?page=1&page_size=20&category=tech
Headers:
  User-Agent: Mozilla/5.0...
  Referer: https://www.example.com/
  X-Requested-With: XMLHttpRequest

API 分析要点:

要素说明如何获取
URL 路径/api/v1/article/listNetwork 面板中查看
请求方法GET / POSTNetwork 面板中查看
查询参数page, page_size, category改变页码/分类,观察参数变化
请求头User-Agent, Referer, Token有些 API 需要特殊 Header
分页方式页码 or 游标(cursor)翻页时观察 URL 参数变化

2.4 代码实现

import requests

def fetch_articles_api(page=1, page_size=20):
    """通过 API 接口获取新闻列表"""
    url = "https://api.example.com/v1/article/list"
    params = {
        "page": page,
        "page_size": page_size,
        "category": "tech",
    }
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
        "Referer": "https://www.example.com/",
        "X-Requested-With": "XMLHttpRequest",
    }
    
    response = requests.get(url, params=params, headers=headers)
    data = response.json()  # 直接拿到 JSON,无需 BeautifulSoup
    
    articles = []
    for item in data.get("data", {}).get("items", []):
        articles.append({
            "title": item.get("title"),
            "summary": item.get("summary"),
            "url": item.get("url"),
            "pub_time": item.get("publish_time"),
            "author": item.get("author", {}).get("name"),
        })
    
    return articles

API 方式的优势:

对比维度HTML 解析API 接口
数据格式混乱的 HTML干净的 JSON
代码量大量 CSS 选择器几行字典取值
稳定性网站改版就失效API 相对稳定
数据量只返回显示的可控制分页参数
网络开销需下载完整 HTML只传输数据

三、方案 B:Selenium 处理动态页面

3.1 什么时候需要 Selenium

有些页面无法用 API 方式获取,必须用 Selenium:

  • 滚动加载:滚动到底部才加载更多内容(如澎湃新闻)
  • 点击加载:点击"加载更多"按钮才出现新内容
  • 登录后可见:需要登录才能查看的内容
  • 复杂交互:需要填写表单、点击按钮等操作

3.2 Selenium 工作原理

WebDriver 协议

渲染页面

返回 HTML

Python 脚本

Chrome 浏览器
(自动化控制)

完整网页
(含 JS 执行结果)

Selenium 启动一个真实的 Chrome 浏览器,执行 JavaScript 渲染页面,然后把渲染后的 HTML 返回给 Python。因为执行了 JS,所以滚动加载、动态内容都会被渲染出来。

3.3 安装配置

# 安装 Selenium
uv pip install selenium

# 安装 ChromeDriver(Selenium 4.6+ 自动管理,无需手动下载)
# 如果自动下载失败,手动安装:
# 1. 查看 Chrome 版本:chrome://version
# 2. 下载对应版本:https://chromedriver.chromium.org/

Selenium 4.6+ 的重大改进:内置了 ChromeDriver 自动管理(Selenium Manager),不需要手动下载和配置 PATH。

3.4 核心:等待策略

这是 Selenium 最关键的技巧。页面加载需要时间,如果不等待,拿到的就是空白或半成品页面。

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Chrome()

# ❌ 错误做法:固定 sleep,浪费时间且不可靠
import time; time.sleep(3)

# ✅ 正确做法:显式等待,条件满足立即继续
wait = WebDriverWait(driver, 10)  # 最多等 10 秒
element = wait.until(
    EC.presence_of_element_located((By.CSS_SELECTOR, ".news-list"))
)

三种等待策略:

策略用法场景
presence_of_element_located等待元素出现在 DOM 中列表容器加载完成
visibility_of_element_located等待元素可见弹窗、模态框
element_to_be_clickable等待元素可点击按钮、链接

3.5 滚动加载实战

def scroll_to_load_all(driver, max_scrolls=10):
    """滚动页面加载全部内容"""
    last_height = driver.execute_script("return document.body.scrollHeight")
    scroll_count = 0
    
    while scroll_count < max_scrolls:
        # 滚动到底部
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
        time.sleep(2)  # 等待新内容加载
        
        # 检查是否还有新内容
        new_height = driver.execute_script("return document.body.scrollHeight")
        if new_height == last_height:
            break  # 高度没变,说明到底了
        
        last_height = new_height
        scroll_count += 1
        print(f"  滚动 {scroll_count}/{max_scrolls}...")

3.6 完整 Selenium 爬虫示例

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time

# Chrome 无头模式(后台运行,不弹出窗口)
options = Options()
options.add_argument("--headless=new")  # 无头模式
options.add_argument("--no-sandbox")
options.add_argument("--disable-gpu")
options.add_argument("--window-size=1920,1080")
options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64)")

driver = webdriver.Chrome(options=options)
driver.get("https://www.thepaper.cn/")

# 等待新闻列表加载
wait = WebDriverWait(driver, 10)
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".news-list")))

# 滚动加载更多
scroll_to_load_all(driver, max_scrolls=5)

# 提取新闻
news_items = driver.find_elements(By.CSS_SELECTOR, ".news-item")
for item in news_items:
    title = item.find_element(By.CSS_SELECTOR, "h2").text
    print(f"  {title}")

driver.quit()

四、API vs Selenium:如何选择

能

需要登录

是

否,JS 渲染

能

不能

需要爬取数据

能直接访问吗?

数据在 HTML 里?

Selenium
模拟登录后爬取

requests + BeautifulSoup
(第13课方案)

能找到 API 接口?

直接请求 API
(方案 A)

选择优先级:API 接口 > requests + BeautifulSoup > Selenium

  • API 最优:数据干净、速度快、省资源
  • requests 次之:轻量、不需要浏览器
  • Selenium 最后:重量级、需要浏览器环境,但能处理最复杂的场景

五、实战代码

本课配套代码包含两个实战脚本:

5.1 API 爬虫:huxiu_api.py

cd code/lesson-14-advanced-crawler
uv run huxiu_api.py

通过 API 接口获取新闻列表,存为 JSON 文件。演示了:

  • 如何构造 API 请求参数
  • 如何处理分页
  • 如何解析 JSON 响应
  • 数据保存到 SQLite

5.2 Selenium 爬虫:pengpai_selenium.py

cd code/lesson-14-advanced-crawler
uv run pengpai_selenium.py

用 Selenium 爬取动态加载页面,演示了:

  • Chrome 无头模式配置
  • 显式等待策略
  • 滚动加载全部内容
  • 提取后存入 SQLite

六、常见问题

6.1 ChromeDriver 版本不匹配

selenium.common.exceptions.SessionNotCreatedException:
This version of ChromeDriver only supports Chrome version XXX

解决:Selenium 4.6+ 会自动下载匹配的 ChromeDriver。如果失败,手动从 ChromeDriver 官网 下载对应版本。

6.2 Selenium 被检测为机器人

一些网站会检测 Selenium 的特征(如 navigator.webdriver 属性)。对抗方法:

options.add_argument("--disable-blink-features=AutomationControlled")
options.add_experimental_option("excludeSwitches", ["enable-automation"])

6.3 API 返回 401/403

说明需要认证。检查:

  • 是否需要登录 token(在 Network 面板找 Authorization header)
  • 是否需要 Referer 头
  • 是否需要 Cookie(从浏览器复制)

七、总结

这节课我们掌握了两种进阶爬虫核心技术:

技术适用场景核心技巧
API 分析前后端分离网站Chrome DevTools → Network → XHR → 复制请求
SeleniumJS 动态渲染页面显式等待 + 滚动加载 + 无头模式

加上第 13 课的 requests + BeautifulSoup,你现在拥有了完整的爬虫工具箱——能应对 90% 的数据采集需求。

下一课,我们将解决另一个关键问题:数据清洗与结构化——把爬下来的"脏数据"变成 Agent 能用的高质量知识。


本课代码:code/lesson-14-advanced-crawler/

Logo

聚焦RDK与具身智能,汇集机器人开发教程、开源案例和技术交流

更多推荐