第 14 课 | 进阶爬虫:API 分析 + Selenium 动态页面
第 13 课的 requests 爬虫只能处理静态 HTML。真实世界更复杂:有些网站数据来自 API 接口,有些页面由 JavaScript 动态渲染。这节课,我们攻克这两种场景。
一、业务价值:为什么需要进阶爬虫
1.1 第 13 课的局限性
第 13 课的爬虫很好,但遇到了两个现实问题:
| 场景 | 第 13 课方案 | 问题 |
|---|---|---|
| 虎嗅网新闻列表 | requests + BeautifulSoup | HTML 里只有骨架,数据是 JS 异步加载的 |
| 澎湃新闻滚动加载 | requests + BeautifulSoup | 只能拿到首屏,滚动加载的内容拿不到 |
| 36氪 API 数据 | requests + BeautifulSoup | 明明有 JSON 接口,解析 HTML 费力不讨好 |
进阶爬虫要解决的核心问题:拿到第 13 课拿不到的数据。
1.2 两种进阶方案
二、方案 A:API 接口分析(以虎嗅为例)
2.1 核心思路
很多现代网站采用前后端分离架构:前端只是壳,数据通过 API 接口以 JSON 格式返回。与其解析复杂的 HTML,不如直接请求 API——数据更干净、更高效。
2.2 如何找到 API 接口
打开 Chrome 开发者工具(F12),切换到Network(网络)面板,刷新页面:
关键步骤:
- 打开 F12 → Network 面板
- 点击XHR或Fetch过滤器(只看数据请求)
- 刷新页面,观察请求列表
- 找到返回 JSON 数据的请求(Preview 面板可预览)
- 右键 → 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: XMLHttpRequestAPI 分析要点:
| 要素 | 说明 | 如何获取 |
|---|---|---|
| URL 路径 | /api/v1/article/list | Network 面板中查看 |
| 请求方法 | GET / POST | Network 面板中查看 |
| 查询参数 | page,page_size,category | 改变页码/分类,观察参数变化 |
| 请求头 | User-Agent, Referer, Token | 有些 API 需要特殊 Header |
| 分页方式 | 页码 or 游标(cursor) | 翻页时观察 URL 参数变化 |
2.4 代码实现
importrequestsdeffetch_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,无需 BeautifulSouparticles=[]foritemindata.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"),})returnarticlesAPI 方式的优势:
| 对比维度 | HTML 解析 | API 接口 |
|---|---|---|
| 数据格式 | 混乱的 HTML | 干净的 JSON |
| 代码量 | 大量 CSS 选择器 | 几行字典取值 |
| 稳定性 | 网站改版就失效 | API 相对稳定 |
| 数据量 | 只返回显示的 | 可控制分页参数 |
| 网络开销 | 需下载完整 HTML | 只传输数据 |
三、方案 B:Selenium 处理动态页面
3.1 什么时候需要 Selenium
有些页面无法用 API 方式获取,必须用 Selenium:
- 滚动加载:滚动到底部才加载更多内容(如澎湃新闻)
- 点击加载:点击"加载更多"按钮才出现新内容
- 登录后可见:需要登录才能查看的内容
- 复杂交互:需要填写表单、点击按钮等操作
3.2 Selenium 工作原理
Selenium 启动一个真实的 Chrome 浏览器,执行 JavaScript 渲染页面,然后把渲染后的 HTML 返回给 Python。因为执行了 JS,所以滚动加载、动态内容都会被渲染出来。
3.3 安装配置
# 安装 Seleniumuv 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 最关键的技巧。页面加载需要时间,如果不等待,拿到的就是空白或半成品页面。
fromseleniumimportwebdriverfromselenium.webdriver.common.byimportByfromselenium.webdriver.support.uiimportWebDriverWaitfromselenium.webdriver.supportimportexpected_conditionsasEC driver=webdriver.Chrome()# ❌ 错误做法:固定 sleep,浪费时间且不可靠importtime;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 滚动加载实战
defscroll_to_load_all(driver,max_scrolls=10):"""滚动页面加载全部内容"""last_height=driver.execute_script("return document.body.scrollHeight")scroll_count=0whilescroll_count<max_scrolls:# 滚动到底部driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")time.sleep(2)# 等待新内容加载# 检查是否还有新内容new_height=driver.execute_script("return document.body.scrollHeight")ifnew_height==last_height:break# 高度没变,说明到底了last_height=new_height scroll_count+=1print(f" 滚动{scroll_count}/{max_scrolls}...")3.6 完整 Selenium 爬虫示例
fromseleniumimportwebdriverfromselenium.webdriver.chrome.optionsimportOptionsfromselenium.webdriver.common.byimportByfromselenium.webdriver.support.uiimportWebDriverWaitfromselenium.webdriver.supportimportexpected_conditionsasECimporttime# 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")foriteminnews_items:title=item.find_element(By.CSS_SELECTOR,"h2").textprint(f"{title}")driver.quit()四、API vs Selenium:如何选择
选择优先级:API 接口 > requests + BeautifulSoup > Selenium
- API 最优:数据干净、速度快、省资源
- requests 次之:轻量、不需要浏览器
- Selenium 最后:重量级、需要浏览器环境,但能处理最复杂的场景
五、实战代码
本课配套代码包含两个实战脚本:
5.1 API 爬虫:huxiu_api.py
cdcode/lesson-14-advanced-crawler uv run huxiu_api.py通过 API 接口获取新闻列表,存为 JSON 文件。演示了:
- 如何构造 API 请求参数
- 如何处理分页
- 如何解析 JSON 响应
- 数据保存到 SQLite
5.2 Selenium 爬虫:pengpai_selenium.py
cdcode/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 → 复制请求 |
| Selenium | JS 动态渲染页面 | 显式等待 + 滚动加载 + 无头模式 |
加上第 13 课的 requests + BeautifulSoup,你现在拥有了完整的爬虫工具箱——能应对 90% 的数据采集需求。
下一课,我们将解决另一个关键问题:数据清洗与结构化——把爬下来的"脏数据"变成 Agent 能用的高质量知识。
本课代码:code/lesson-14-advanced-crawler/