Python 进阶爬虫教程:动态页面、会话与受控并发

Chen Xi
Chen Xi

在掌握 Requests 与 Beautiful Soup 基础 后,可能会遇到需要 Cookie 的会话、由 JavaScript 渲染的内容或大量独立 URL。本教程提供更稳健的实现骨架,但不用于绕过登录、验证码、付费墙或反自动化控制。

示例于 2026-07-29 复核。浏览器、驱动和 Python 库的兼容要求可能变化,请以各项目官方文档为准。

1. 复用 Requests 会话

Session 可以复用连接,并在同一会话中保存 Cookie:

1
2
3
4
5
6
7
8
9
10
11
12
import requests

with requests.Session() as session:
session.headers.update(
{"User-Agent": "ExampleResearchBot/1.0 (+https://example.com/bot)"}
)
response = session.get(
"https://example.com/",
timeout=(5, 15),
)
response.raise_for_status()
print(response.url)

认证信息不应写入源码或日志。只对你有权使用的账户和接口自动化,并遵守站点条款。

2. Selenium 与显式等待

静态 HTTP 客户端看不到浏览器执行 JavaScript 后生成的 DOM。对于获准自动访问的动态页面,可以使用 Selenium,并等待明确条件:

1
2
3
4
5
6
7
8
9
10
11
12
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

with webdriver.Chrome() as driver:
driver.get("https://example.com/dynamic-page")

element = WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((By.CSS_SELECTOR, "main article"))
)
print(element.text)

固定 time.sleep() 要么浪费时间,要么在页面较慢时仍然失败。显式等待能把“等什么”和最长等待时间写清楚。若站点提供正式 API,通常应优先使用 API。

3. aiohttp 受控并发

异步并发不是越高越好。下面用一个共享 ClientSession、总超时和信号量限制并发:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import asyncio
import aiohttp

URLS = [
"https://example.com/a",
"https://example.com/b",
]

async def fetch(session, semaphore, url):
async with semaphore:
async with session.get(url) as response:
response.raise_for_status()
return url, await response.text()

async def main():
timeout = aiohttp.ClientTimeout(total=20)
connector = aiohttp.TCPConnector(limit=4)
semaphore = asyncio.Semaphore(2)
headers = {
"User-Agent": "ExampleResearchBot/1.0 (+https://example.com/bot)"
}

async with aiohttp.ClientSession(
timeout=timeout,
connector=connector,
headers=headers,
) as session:
results = await asyncio.gather(
*(fetch(session, semaphore, url) for url in URLS),
return_exceptions=True,
)
for result in results:
print(result if isinstance(result, Exception) else result[0])

asyncio.run(main())

实际项目还应实现指数退避、Retry-After 处理、去重、断点续跑和持久化队列。重试只适合暂时性错误,并且必须设置次数上限;不要对所有 4xx 响应盲目重试。

4. 选择工具

  • 页面 HTML 已含目标数据:Requests + Beautiful Soup。
  • 官方提供 API:优先 API。
  • 内容必须执行 JavaScript 才出现:在许可范围内使用 Selenium。
  • 大量独立、轻量 HTTP 请求:采用有限并发的 aiohttp。

先在少量 URL 上验证解析规则和数据质量,再逐步扩大规模。每次运行应记录成功率、响应状态、限速事件和解析失败,方便及时停止异常任务。

参考资料