浏览器上下文 HTTP 请求
发起自动继承浏览器会话状态、Cookie 和身份验证的 HTTP 请求。非常适合结合 UI 导航和 API 效率的混合自动化。
混合自动化的游戏规则改变者
曾经希望您可以发起自动获取所有浏览器 Cookie 和身份验证的 HTTP 请求吗?现在您可以了!tab.request 属性为您提供了一个漂亮的类似 requests 的接口,可以直接在浏览器的 JavaScript 上下文中执行 HTTP 调用。
为什么使用浏览器上下文请求?
传统自动化通常需要您手动提取 Cookie 和标头以进行 API 调用。浏览器上下文请求消除了这种麻烦:
| 传统方法 | 浏览器上下文请求 |
|---|---|
| 手动提取 Cookie | Cookie 自动继承 |
| 管理会话令牌 | 会话状态保留 |
| 单独处理 CORS | 遵守 CORS 策略 |
| 同时使用两个 HTTP 客户端 | 一个统一的接口 |
| 同步身份验证状态 | 始终已认证 |
非常适合:
- 通过 UI 登录后抓取已认证的 API
- 混合工作流,混合浏览器交互和 API 调用
- 测试已认证的端点而无需管理令牌
- 绕过复杂的身份验证流程
- 使用单页应用程序(SPA)
快速入门
最简单的示例:通过 UI 登录,然后进行已认证的 API 调用:
import asyncio
from pydoll.browser.chromium import Chrome
async def hybrid_automation():
async with Chrome() as browser:
tab = await browser.start()
# 1. 通过 UI 正常登录
await tab.go_to('https://example.com/login')
await (await tab.find(id='username')).type_text('user@example.com')
await (await tab.find(id='password')).type_text('password123')
await (await tab.find(id='login-btn')).click()
# 登录后等待重定向
await asyncio.sleep(2)
# 2. 现在使用已认证的会话进行 API 调用!
response = await tab.request.get('https://example.com/api/user/profile')
user_data = response.json()
print(f"登录为: {user_data['name']}")
print(f"邮箱: {user_data['email']}")
asyncio.run(hybrid_automation())
无需 Cookie 管理
注意我们没有提取或传递任何 Cookie?请求自动继承了浏览器的已认证会话!
常见用例
1. 抓取已认证的 API
使用 UI 登录,然后使用 API 提取数据:
import asyncio
from pydoll.browser.chromium import Chrome
async def scrape_user_data():
async with Chrome() as browser:
tab = await browser.start()
# 通过 UI 登录(处理复杂的认证流程)
await tab.go_to('https://app.example.com/login')
await (await tab.find(id='email')).type_text('user@example.com')
await (await tab.find(id='password')).type_text('password')
await (await tab.find(type='submit')).click()
await asyncio.sleep(2)
# 现在通过 API 提取数据(比抓取 UI 快得多)
all_users = []
for page in range(1, 6):
response = await tab.request.get(
f'https://app.example.com/api/users',
params={'page': str(page), 'limit': '100'}
)
users = response.json()['users']
all_users.extend(users)
print(f"第 {page} 页: 获取了 {len(users)} 个用户")
print(f"抓取的总用户数: {len(all_users)}")
asyncio.run(scrape_user_data())
2. 测试受保护的端点
测试 API 端点而无需管理身份验证令牌:
import asyncio
from pydoll.browser.chromium import Chrome
async def test_api_endpoints():
async with Chrome() as browser:
tab = await browser.start()
# 一次性认证
await tab.go_to('https://api.example.com/login')
# ... 执行登录 ...
await asyncio.sleep(2)
# 测试多个端点
endpoints = [
'/api/users/me',
'/api/settings',
'/api/notifications',
'/api/dashboard/stats'
]
for endpoint in endpoints:
response = await tab.request.get(f'https://api.example.com{endpoint}')
if response.ok:
print(f"成功 {endpoint}: {response.status_code}")
else:
print(f"失败 {endpoint}: {response.status_code}")
print(f" 错误: {response.text[:100]}")
asyncio.run(test_api_endpoints())
3. 通过 API 提交表单
通过直接向 API 发送来更快地填充表单:
import asyncio
from pydoll.browser.chromium import Chrome
async def bulk_form_submission():
async with Chrome() as browser:
tab = await browser.start()
# 首先登录
await tab.go_to('https://crm.example.com/login')
# ... 登录逻辑 ...
await asyncio.sleep(2)
# 通过 API 提交多个条目(比填写表单快得多)
contacts = [
{'name': 'John Doe', 'email': 'john@example.com', 'company': 'Acme Inc'},
{'name': 'Jane Smith', 'email': 'jane@example.com', 'company': 'Tech Corp'},
{'name': 'Bob Wilson', 'email': 'bob@example.com', 'company': 'StartupXYZ'},
]
for contact in contacts:
response = await tab.request.post(
'https://crm.example.com/api/contacts',
json=contact
)
if response.ok:
print(f"已添加: {contact['name']}")
else:
print(f"失败: {contact['name']} - {response.status_code}")
asyncio.run(bulk_form_submission())
4. 使用会话下载文件
下载需要身份验证的文件:
import asyncio
from pathlib import Path
from pydoll.browser.chromium import Chrome
async def download_authenticated_file():
async with Chrome() as browser:
tab = await browser.start()
# 认证
await tab.go_to('https://portal.example.com/login')
# ... 登录逻辑 ...
await asyncio.sleep(2)
# 下载需要身份验证的文件
response = await tab.request.get(
'https://portal.example.com/api/reports/monthly.pdf'
)
if response.ok:
# 保存文件
output_path = Path('/tmp/monthly_report.pdf')
output_path.write_bytes(response.content)
print(f"已下载: {output_path} ({len(response.content)} 字节)")
else:
print(f"下载失败: {response.status_code}")
asyncio.run(download_authenticated_file())
5. 使用自定义标头
向您的请求添加自定义标头:
import asyncio
from pydoll.browser.chromium import Chrome
from pydoll.protocol.fetch.types import HeaderEntry
async def custom_headers_example():
async with Chrome() as browser:
tab = await browser.start()
# 首先登录
await tab.go_to('https://api.example.com/login')
# ... 登录逻辑 ...
# 使用自定义标头发起请求
headers: list[HeaderEntry] = [
{'name': 'X-API-Version', 'value': '2.0'},
{'name': 'X-Request-ID', 'value': 'unique-id-123'},
{'name': 'Accept-Language', 'value': 'pt-BR,pt;q=0.9'},
]
response = await tab.request.get(
'https://api.example.com/data',
headers=headers
)
print(f"状态: {response.status_code}")
print(f"数据: {response.json()}")
asyncio.run(custom_headers_example())
6. 处理不同的响应类型
以多种格式访问响应数据:
import asyncio
from pydoll.browser.chromium import Chrome
async def response_formats():
async with Chrome() as browser:
tab = await browser.start()
await tab.go_to('https://api.example.com')
# JSON 响应
json_response = await tab.request.get('/api/users/1')
user = json_response.json()
print(f"JSON: {user}")
# 文本响应
text_response = await tab.request.get('/api/status')
status_text = text_response.text
print(f"文本: {status_text}")
# 二进制响应(例如,图像)
image_response = await tab.request.get('/api/avatar/1')
image_bytes = image_response.content
print(f"二进制: {len(image_bytes)} 字节")
# 检查响应状态
if json_response.ok:
print("请求成功!")
# 访问响应 URL(在重定向后很有用)
print(f"最终 URL: {json_response.url}")
asyncio.run(response_formats())
HTTP 方法
支持所有标准的 HTTP 方法:
GET - 检索数据
# 简单的 GET
response = await tab.request.get('https://api.example.com/users')
# 带查询参数的 GET
response = await tab.request.get(
'https://api.example.com/search',
params={'q': 'python', 'limit': '10'}
)
POST - 创建资源
# 使用 JSON 数据的 POST
response = await tab.request.post(
'https://api.example.com/users',
json={'name': 'John Doe', 'email': 'john@example.com'}
)
# 使用表单数据的 POST
response = await tab.request.post(
'https://api.example.com/login',
data={'username': 'john', 'password': 'secret'}
)
PUT - 更新资源
# 更新整个资源
response = await tab.request.put(
'https://api.example.com/users/123',
json={'name': 'Jane Doe', 'email': 'jane@example.com', 'role': 'admin'}
)
PATCH - 部分更新
# 更新特定字段
response = await tab.request.patch(
'https://api.example.com/users/123',
json={'email': 'newemail@example.com'}
)
DELETE - 删除资源
HEAD - 仅获取标头
# 检查资源是否存在而不下载它
response = await tab.request.head('https://example.com/large-file.zip')
print(f"Content-Length: {response.headers}")
OPTIONS - 检查功能
# 检查允许的方法
response = await tab.request.options('https://api.example.com/users')
print(f"允许的方法: {response.headers}")
这是如何工作的?
浏览器上下文请求使用 Fetch API 直接在浏览器的 JavaScript 上下文中执行 HTTP 调用,同时监控 CDP 网络事件以捕获全面的元数据(标头、Cookie、时序)。
有关内部架构、事件监控和实现详细信息的详细说明,请参阅浏览器请求架构。
响应对象
Response 对象提供了类似于 requests.Response 的熟悉接口:
response = await tab.request.get('https://api.example.com/users')
# 状态码
print(response.status_code) # 200, 404, 500 等
# 检查是否成功(2xx 或 3xx)
if response.ok:
print("成功!")
# 响应体
text_data = response.text # 作为字符串
byte_data = response.content # 作为字节
json_data = response.json() # 解析的 JSON
# 标头
for header in response.headers:
print(f"{header['name']}: {header['value']}")
# 请求标头(实际发送的内容)
for header in response.request_headers:
print(f"{header['name']}: {header['value']}")
# 响应设置的 Cookie
for cookie in response.cookies:
print(f"{cookie['name']} = {cookie['value']}")
# 最终 URL(在重定向后)
print(response.url)
# 为错误状态码引发异常
response.raise_for_status() # 如果是 4xx 或 5xx 则引发 HTTPError
重定向和 URL 跟踪
response.url 属性仅包含所有重定向后的最终 URL。如果您需要跟踪完整的重定向链(中间 URL、状态码、时序),请使用网络监控详细观察所有请求。
标头和 Cookie
使用标头
标头表示为 HeaderEntry 对象:
import asyncio
from pydoll.browser.chromium import Chrome
from pydoll.protocol.fetch.types import HeaderEntry
async def header_example():
async with Chrome() as browser:
tab = await browser.start()
# 使用 HeaderEntry 类型以获得 IDE 自动完成和类型检查
headers: list[HeaderEntry] = [
{'name': 'Authorization', 'value': 'Bearer token-123'},
{'name': 'X-Custom-Header', 'value': 'custom-value'},
]
response = await tab.request.get(
'https://api.example.com/protected',
headers=headers
)
# 检查响应标头(也是 HeaderEntry 类型的字典)
for header in response.headers:
if header['name'] == 'Content-Type':
print(f"Content-Type: {header['value']}")
asyncio.run(header_example())
标头的类型提示
HeaderEntry 是来自 pydoll.protocol.fetch.types 的 TypedDict。将其用作类型提示可为您提供:
- 自动完成:IDE 建议
name和value键 - 类型安全:在运行前捕获拼写错误和缺失的键
- 文档:清晰的标头结构
虽然您可以传递普通字典,但使用类型提示可以提高代码质量和 IDE 支持。
自定义标头行为
自定义标头与浏览器的自动标头(如 User-Agent、Accept、Referer 等)一起发送。
如果您尝试设置标准浏览器标头(例如 User-Agent),行为取决于特定标头;有些可能会被覆盖,其他可能被忽略,有些可能会导致冲突。对于大多数用例,坚持使用自定义标头(例如 X-API-Key、Authorization)以避免意外行为。
理解 Cookie
Cookie 由浏览器自动管理:
import asyncio
from pydoll.browser.chromium import Chrome
async def cookie_example():
async with Chrome() as browser:
tab = await browser.start()
# 第一个请求设置 Cookie
login_response = await tab.request.post(
'https://api.example.com/login',
json={'username': 'user', 'password': 'pass'}
)
# 检查服务器设置的 Cookie
print("服务器设置的 Cookie:")
for cookie in login_response.cookies:
print(f" {cookie['name']} = {cookie['value']}")
# 后续请求自动包含 Cookie
profile_response = await tab.request.get(
'https://api.example.com/profile'
)
# 无需传递 Cookie - 浏览器会处理!
print(f"配置文件数据: {profile_response.json()}")
asyncio.run(cookie_example())
与传统 Requests 的比较
| 功能 | requests 库 |
浏览器上下文请求 |
|---|---|---|
| 会话管理 | 手动 Cookie 处理 | 通过浏览器自动 |
| 身份验证 | 提取并传递令牌 | 从浏览器继承 |
| CORS | 不适用 | 浏览器执行策略 |
| JavaScript | 无法执行 | 完全访问浏览器上下文 |
| Cookie Jar | 单独的实例 | 浏览器的原生 Cookie 存储 |
| 标头 | 手动设置 | 浏览器自动添加标准标头 |
| 用例 | 服务器端脚本 | 浏览器自动化 |
| 设置 | 外部库 | 内置于 Pydoll |
另请参阅
浏览器上下文请求是混合自动化的游戏规则改变者。结合 UI 自动化的强大功能和直接 API 调用的速度,同时保持完美的会话连续性!