剧作家 + 无痕抓取浏览器:捕获和重放完整的 HAR 会话
Scraping and Proxy Management Expert
TL;DR:
- HAR文件是结构化的HTTP存档,而不是截图或视频。 它的
log.entries数组存储每个捕获的HTTP请求的对象,包括请求和响应元数据,以及可用时的记录内容。 - Playwright在浏览器上下文级别记录HAR文件。 创建上下文时设置
record_har_path,并在调用context.close()时将存档刷新到磁盘。 - Scrapeless Scraping Browser提供远程浏览器会话。 Playwright通过CDP与其连接,加载页面,触发动态请求,并将结果流量保存到本地。
- HAR分析不需要浏览器。 一旦文件存在,Python的标准
json模块可以检查其URL、方法、状态、MIME类型、头部和嵌入的响应体。 - 捕获的请求可以在没有Playwright的情况下重新发出。 该示例在删除HTTP/2伪头并重新协商内容编码后,使用
urllib重建一个公共JSON请求。 - HAR重放有界限。 过期的cookie、CSRF令牌、Bearer凭证、WebSocket帧和变化的服务器数据可能会阻止后续请求再现原始响应。
- 免费开始。 新的Scrapeless账户包括免费的Scraping Browser运行时 — 在app.scrapeless.com注册。
介绍:先捕获,后决定什么重要
HAR文件是浏览器会话期间观察到的HTTP事务的JSON存档。它不是渲染页面的截图、DOM快照或会话视频。
每个捕获的请求作为一个对象出现在:
text
log.entries
一个条目可以包含请求方法、URL、头部、查询参数、邮寄数据、响应状态、响应头、时序信息和录制的响应内容。二进制内容可能以Base64等编码表示,而文本体可以直接出现在条目中。
本指南将Playwright连接到Scrapeless Scraping Browser,通过Chrome DevTools协议记录完整的页面加载和滚动序列,并关闭浏览器。
工作流程的后半部分不需要浏览器:
- 使用
json检查HAR结构。 - 查找捕获的API请求。
- 重新构建可重用的头部。
- 使用
urllib重新发出请求。 - 将新的JSON响应与存档中保存的主体进行比较。
每个显示的结果都来自捕获的目标会话。
使用HAR存档可以做什么
HAR捕获在重要的端点在页面加载之前未知时非常有用。
- 审核完整的页面加载。 审查一个会话中的HTML、样式表、脚本、字体、图像和API请求。
- 发现内部JSON端点。 捕获后在存档中搜索,而不是预测哪些请求会重要。
- 调试失败的页面行为。 在浏览器关闭后检查请求URL、响应状态、头部、MIME类型和正文。
- 保存网络证据。 存储一个可移植的JSON文物,以便稍后分析或转移到另一台机器上。
- 比较页面加载行为。 捕获不同的会话并比较它们的请求集、状态或响应内容。
- 提取捕获的响应数据。 阅读嵌入的JSON或文本体,而无需再次加载页面。
- 重建合格的请求。 通过标准的HTTP客户端重新发出公共或仍然经过身份验证的HTTP请求。
- 构建确定性浏览器测试。 使用Playwright的
route_from_har()功能将记录的响应返回到实时浏览器上下文中。
当广泛捕获比附加监听器到一个已知端点更有用时,HAR文件最有价值。
HAR捕获、视频录制和HAR路由是不同的
该领域的三个功能使用相似的术语,但解决不同的问题。
| 功能 | 记录或执行的内容 | 之后需要浏览器吗? |
|---|---|---|
Playwright record_har_path |
归档HTTP请求和响应数据 | 不需要,供离线检查 |
| Scrapeless会话录制 | 创建渲染会话的视觉重放 | 不需要,供仪表板播放 |
Playwright route_from_har() |
将记录的响应提供给浏览器上下文发出的请求 | 需要 |
本指南中的urllib示例 |
针对实时服务器重新发出一个捕获的请求 | 不需要 |
实时拦截
实时拦截观察请求的发生。当目标端点或响应模式已知时,它工作得很好。
一旦会话结束,任何未被监听器捕获的内容就消失了。
HAR捕获
HAR捕获广泛记录上下文的HTTP流量。重要的端点可以在浏览器关闭后选择。
这使得HAR捕获更适合:
- 会话后调试
- 网络清单
- API发现
- 审计页面加载的所有资源
- 保存响应体以供离线检查
无刮取会话录制
无刮取抓取浏览器支持单独的原生会话录制功能。这会产生可重播的可视记录,显示呈现的浏览器会话。
它并不替代 HAR 文件。视频显示了屏幕上出现的内容;而 HAR 则暴露了结构化的 HTTP 事务。
Playwright route_from_har()
Playwright 的 route_from_har() 将保存的 HAR 响应发送回实时浏览器上下文中的请求。它通常用于在浏览器测试中模拟后端行为。
本指南中的重播示例做了其他事情:它从存档中读取一个请求,并使用 urllib 提交一个新的实时 HTTP 请求。
为什么通过无刮取抓取浏览器捕获 HAR?
无刮取抓取浏览器提供了由 Playwright 通过 CDP 驱动的远程浏览器环境。
浏览器会话在云基础设施上运行,并通过连接参数支持地理代理选择。Playwright 仍然使用其正常的浏览器、上下文、页面和 HAR API。
对于这个工作流程,责任划分很简单:
| 组件 | 责任 |
|---|---|
| 无刮取抓取浏览器 | 运行远程 Chromium 会话并提供 CDP 端点 |
| Playwright | 创建上下文、驱动页面并记录 HAR |
| 本地文件系统 | 存储 session.har |
| Python 标准库 | 检查存档并重新发出选择的请求 |
HAR 录制本身是一个 Playwright 特性。将 Playwright 连接到无刮取使实时渲染阶段移动到受管理的远程浏览器中,同时将结果存档保留在运行 Python 脚本的机器上。
连接使用相同的 Chrome DevTools Protocol 网络域,浏览器工具使用它来观察网络活动。
抓取浏览器快速入门 涵盖了更广泛的 Playwright 和 Puppeteer 连接模型。
先决条件
您需要:
- Python 3.9 或更新版本
- Playwright 1.59.0,以便进行重现的运行
- 一个无刮取账户和 API 密钥
- 对
session.har将要创建的目录的写入权限 - 对公共目标页面的网络访问
Playwright 1.59.0 声明需要 Python 3.9 或更新版本。固定该版本使安装与本指南中捕获的结果匹配。
此工作流程不需要本地的 Chrome 安装。connect_over_cdp() 附加到已经在无刮取基础设施上运行的浏览器。
检查和请求重新发出的脚本仅使用 Python 的标准库。它们不导入 Playwright 或打开浏览器连接。
第 1 步 — 安装 Playwright
安装用于录制运行的版本:
bash
pip install "playwright==1.59.0"
由于脚本通过 CDP 连接到现有的远程浏览器,因此不会启动本地安装的浏览器可执行文件。
在 shell 中设置无刮取 API 密钥:
bash
export SCRAPELESS_API_KEY="your_scrapeless_api_key"
代码从环境读取密钥,而不是将其存储在源代码控制中。
第 2 步 — 构建无刮取 CDP URL
抓取浏览器连接 URL 作为查询参数携带 API 密钥、会话生命周期和代理位置:
python
import os
from urllib.parse import urlencode
API_KEY = os.environ["SCRAPELESS_API_KEY"]
def scraping_browser_url(proxy_country="US", session_ttl=180):
params = urlencode({
"token": API_KEY,
"sessionTTL": session_ttl,
"proxyCountry": proxy_country,
})
return f"wss://browser.scrapeless.com/api/v2/browser?{params}"
该函数生成的 URL 形式如下:
text
wss://browser.scrapeless.com/api/v2/browser?token=...&sessionTTL=180&proxyCountry=US
三个配置值为:
token:无刮取 API 密钥sessionTTL:最大会话生命周期proxyCountry:请求的代理国家
将 URL 构造保留在一个函数中,使得在多个捕获脚本中应用相同的连接设置更加容易。
第 3 步 — 捕获浏览器会话
Playwright 的 record_har_path 设置属于 browser.new_context(),而不是 connect_over_cdp()。
浏览器连接提供对 Chromium 的访问。上下文定义了将被记录的内容。
python
import os
from urllib.parse import urlencode
from playwright.sync_api import sync_playwright
API_KEY = os.environ["SCRAPELESS_API_KEY"]
HAR_PATH = "session.har"
def scraping_browser_url(proxy_country="US", session_ttl=180):
params = urlencode({
"token": API_KEY,
"sessionTTL": session_ttl,
"proxyCountry": proxy_country,
})
return f"wss://browser.scrapeless.com/api/v2/browser?{params}"
zh
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(scraping_browser_url())
context = browser.new_context(
record_har_path=HAR_PATH,
record_har_content="embed",
)
page = context.new_page()
page.goto(
"https://quotes.toscrape.com/scroll",
wait_until="networkidle",
)
for _ in range(3):
page.evaluate(
"window.scrollTo(0, document.body.scrollHeight)"
)
page.wait_for_timeout(800)
print(
"滚动后可见的引用元素数量:",
page.locator(".quote").count(),
)
context.close()
browser.close()
print(
"HAR已写入:",
HAR_PATH,
"-",
os.path.getsize(HAR_PATH),
"字节",
)
运行时打印了:
```text
滚动后可见的引用元素数量: 40
HAR已写入: session.har - 333269 字节
目标最初渲染了十条引用。每次滚动到接近底部时,都会触发另一个/api/quotes?page=N请求,使可见的总数增加到40条引用,跨越四个API页面。
该脚本无需预测哪个请求之后会变得重要。HAR还捕获了文档、样式表、JavaScript、字体和JSON调用。
获取您的免费计划API密钥:app.scrapeless.com
为什么需要context.close()
当浏览器上下文关闭时,HAR将被最终定稿。
Playwright将record_har_path文档化为浏览器上下文设置,并要求browser_context.close()才能保存HAR。仅关闭浏览器连接可能会在没有优雅刷新上下文伪影的情况下留下。
正确的关闭顺序是:
python
context.close()
browser.close()
因此,context.close()调用是捕获过程的一部分,而不是可选的清理。
record_har_content="embed"将记录的响应内容存储在HAR中,而不是在单独的伴侣文件中。这使得session.har在后续的JSON检查和主体比较中是自包含的。
历史HAR格式草案定义了顶级log对象及其必需的entries数组。每个条目表示一个导出的HTTP请求。
第4步 - 在没有浏览器的情况下检查HAR
上下文关闭后,session.har是一个本地JSON文档。
以下脚本只使用json、collections和pathlib:
python
import json
from collections import Counter
from pathlib import Path
har = json.loads(Path("session.har").read_text())
entries = har["log"]["entries"]
print("总条目:", len(entries))
by_type = Counter(
entry["response"]["content"]["mimeType"].split(";")[0]
for entry in entries
)
for mime_type, count in by_type.most_common():
print(f" {mime_type}: {count}")
print()
for entry in entries:
request = entry["request"]
response = entry["response"]
print(
f"{request['method']:4s} "
f"{response['status']:3d} "
f"{request['url']}"
)
捕获的文件包含:
text
总条目: 10
application/json: 4
text/css: 3
text/html: 1
application/javascript: 1
font/woff2: 1
GET 200 https://quotes.toscrape.com/scroll
GET 200 https://quotes.toscrape.com/static/bootstrap.min.css
GET 200 https://quotes.toscrape.com/static/main.css
GET 200 https://quotes.toscrape.com/static/jquery.js
GET 200 https://fonts.googleapis.com/css?family=Raleway:400,700
GET 200 https://fonts.gstatic.com/s/raleway/v37/1Ptug8zYS_SKggPNyC0ITw.woff2
GET 200 https://quotes.toscrape.com/api/quotes?page=1
GET 200 https://quotes.toscrape.com/api/quotes?page=2
GET 200 https://quotes.toscrape.com/api/quotes?page=3
GET 200 https://quotes.toscrape.com/api/quotes?page=4
这十个条目覆盖了五种MIME类型:
- 一个HTML文档
- 三个CSS响应
- 一个JavaScript响应
- 一个网页字体
- 四个JSON响应
一个实时监听器过滤到/api/quotes将观察到四个JSON请求,但忽略其他六个资源。HAR保留了所有十个条目供后续检查。
理解HAR条目结构
har["log"]["entries"]中的每个项都包含嵌套的请求和响应对象。
简化的条目具有以下结构:
json
{
"request": {
"method": "GET",
"url": "https://example.com/api/data",
"headers": []
},
"response": {
"status": 200,
"headers": [],
"content": {
"mimeType": "application/json",
"text": "{}"
}
}
}
有用的请求字段包括:
methodurlheadersqueryStringpostData
有用的响应字段包括:
statusstatusTextheaderscontentredirectURL
HAR内容并不保证在每个条目中都以直接可读的文本形式存储。根据资源和记录器的不同,`content.text` 可能缺失、解码文本,或者是一个编码表示,其 `encoding` 字段标识格式。
## 第5步 — 处理HTTP/2伪头部
捕获的 `page=1` API调用的请求头包含如下名称:
```text
:authority
:method
:path
:scheme
这些是 HTTP/2伪头部字段。
它们携带控制信息,该信息通常会出现在HTTP/1.1请求行或目标中:
:method指定请求方法。:scheme指定URI方案。:authority指定目标权限。:path指定路径和查询。
伪头部不是普通的HTTP头字段。像 urllib 这样的面向HTTP/1.1的客户端无法接受带冒号前缀的头名称。
重放脚本必须将它们的含义转换为URL和方法,然后从普通头映射中省略它们。
第6步 — 使用 urllib 重新发出一个捕获的请求
选定的 /api/quotes?page=1 请求现在在本地JSON文件中是一个字典。
下面的脚本:
- 找到捕获的条目。
- 读取其方法、URL和头部。
- 去除HTTP/2伪头部。
- 去除
accept-encoding。 - 创建一个新的
urllib.request.Request。 - 解析实时和捕获的响应体。
- 比较两个Python对象。
python
import json
import urllib.error
import urllib.request
from pathlib import Path
har = json.loads(Path("session.har").read_text())
entries = har["log"]["entries"]
target = next(
entry
for entry in entries
if entry["request"]["url"].endswith("page=1")
)
captured_request = target["request"]
# HTTP/2伪头部描述协议框架,不能作为普通HTTP/1.1风格的头传递。
#
# 还省略了accept-encoding,以便urllib可以协商一个
# 脚本可以直接解码的编码。
skip_headers = {"accept-encoding"}
headers = {
header["name"]: header["value"]
for header in captured_request["headers"]
if not header["name"].startswith(":")
and header["name"].lower() not in skip_headers
}
print("重放的头部数量:", len(headers))
request = urllib.request.Request(
captured_request["url"],
headers=headers,
method=captured_request["method"],
)
with urllib.request.urlopen(request, timeout=10) as response:
if response.status != 200:
raise urllib.error.HTTPError(
captured_request["url"],
response.status,
"意外的状态",
response.headers,
None,
)
live_data = json.loads(response.read())
captured_content = target["response"]["content"]
captured_data = json.loads(captured_content["text"])
print("状态:", response.status, "-- 没有运行浏览器进程")
print(
"第一条引用作者:",
live_data["quotes"][0]["author"]["name"],
)
print(
"与HAR已经捕获的响应体匹配:",
live_data == captured_data,
)
无浏览器重放的结果打印为:
text
重放的头部数量: 12
状态: 200 -- 没有运行浏览器进程
第一条引用作者: 阿尔伯特·爱因斯坦
与HAR已经捕获的响应体匹配: True
过滤后的映射包含12个普通头部。HTTP/2伪头部和 accept-encoding 被排除在外。
accept-encoding 是 一个HTTP内容协商字段。重放客户端可以宣传其支持的内容编码,而不是盲目复制浏览器的Brotli协商。
目标响应与此运行中存储在HAR中的JSON主体匹配。该比较在解析过的Python对象上进行,而不是在它们的序列化空格或键格式上进行。
这是一种语义请求重建,而不是对原始网络交换的逐字再现。新的请求可以使用不同的HTTP版本、头部顺序、压缩协商、连接和TLS会话。
你可以获得的结果
该工作流程生成三个可重用的工件或结果:
| 阶段 | 输出 | 需要浏览器吗? |
|---|---|---|
| 捕获 | session.har |
是 |
| 检查 | 请求清单和MIME类型摘要 | 否 |
| 重新发出 | 解析的实时响应和捕获主体比较 | 否 |
捕获的会话生成了:
text
HAR大小: 333269字节
HTTP条目: 10
JSON条目: 4
滚动后可见的引用: 40
重新发出的请求状态: 200
捕获/实时JSON匹配: True
这些数字描述了这个特定目标会话。不同的页面、浏览器版本、滚动时机、代理位置或页面响应可能会产生不同的请求集和文件大小。
确认一个脚本中的完整序列
单独的捕获、检查和重放程序更容易理解,但相同的阶段可以合并:
python
import json
import os
import urllib.error
import urllib.request
from collections import Counter
from pathlib import Path
from urllib.parse import urlencode
from playwright.sync_api import sync_playwright
API_KEY = os.environ["SCRAPELESS_API_KEY"]
HAR_PATH = "session.har"
def scraping_browser_url(proxy_country="US", session_ttl=180):
params = urlencode({
"token": API_KEY,
"sessionTTL": session_ttl,
"proxyCountry": proxy_country,
})
return f"wss://browser.scrapeless.com/api/v2/browser?{params}"
# 阶段 1:捕获。需要一个浏览器。
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(
scraping_browser_url()
)
context = browser.new_context(
record_har_path=HAR_PATH,
record_har_content="embed",
)
page = context.new_page()
page.goto(
"https://quotes.toscrape.com/scroll",
wait_until="networkidle",
)
for _ in range(3):
page.evaluate(
"window.scrollTo(0, document.body.scrollHeight)"
)
page.wait_for_timeout(800)
context.close()
browser.close()
print("=== 捕获 ===")
print(
"HAR 写入:",
HAR_PATH,
"-",
os.path.getsize(HAR_PATH),
"字节",
)
# 阶段 2:检查。浏览器已关闭。
har = json.loads(Path(HAR_PATH).read_text())
entries = har["log"]["entries"]
print("\n=== 检查(无浏览器) ===")
print("总条目:", len(entries))
by_type = Counter(
entry["response"]["content"]["mimeType"].split(";")[0]
for entry in entries
)
for mime_type, count in by_type.most_common():
print(f" {mime_type}: {count}")
# 阶段 3:重新发出一个请求。浏览器保持关闭。
target = next(
entry
for entry in entries
if entry["request"]["url"].endswith("page=1")
)
captured_request = target["request"]
skip_headers = {"accept-encoding"}
headers = {
header["name"]: header["value"]
for header in captured_request["headers"]
if not header["name"].startswith(":")
and header["name"].lower() not in skip_headers
}
request = urllib.request.Request(
captured_request["url"],
headers=headers,
method=captured_request["method"],
)
with urllib.request.urlopen(request, timeout=10) as response:
if response.status != 200:
raise urllib.error.HTTPError(
captured_request["url"],
response.status,
"意外状态",
response.headers,
None,
)
live_data = json.loads(response.read())
captured_data = json.loads(
target["response"]["content"]["text"]
)
print("\n=== 重新发出(无浏览器) ===")
print("状态:", response.status)
print(
"第一条报价作者:",
live_data["quotes"][0]["author"]["name"],
)
print(
"匹配捕获的主体:",
live_data == captured_data,
)
合并运行输出为:
text
=== 捕获 ===
HAR 写入: session.har - 333259 字节
=== 检查(无浏览器) ===
总条目: 10
application/json: 4
text/css: 3
text/html: 1
application/javascript: 1
font/woff2: 1
=== 重新发出(无浏览器) ===
状态: 200
第一条报价作者: 阿尔伯特·爱因斯坦
匹配捕获的主体: True
只有第一阶段导入并使用了 Playwright。后面的阶段操作于文件和选定的实时 HTTP 端点。
HAR 捕获了什么
HAR 文件表示浏览器上下文记录的 HTTP 操作。
根据记录器和配置,条目可以包含:
- 请求方法和 URL
- 查询字符串参数
- 请求头
- 请求 cookie
- 已发布的数据
- 响应状态
- 响应头
- 响应 cookie
- MIME 类型
- 响应内容
- 传输大小
- 定时信息
- 重定向细节
- 缓存信息
通过 record_har_content="embed",Playwright 将可用的响应内容存储在 HAR 内部。没有嵌入的内容,请求清单仍然可以有用,但归档可能不包含离线解析或比较所需的响应主体。
HAR 不保证的内容
HAR 文件是捕获的 HTTP 活动的持久记录,但它并不是每个浏览器行为的完整记录。
WebSocket 帧不会被归档
HAR 模型请求和响应事务。它不会保留在建立的 WebSocket 连接中携带的消息序列。
一个结合普通 HTTP 端点和实时 WebSocket 源的页面需要单独的捕获机制:
- HTTP 请求和响应流量的 HAR
- WebSocket 帧监听器用于 socket 消息
初始连接可能涉及 HTTP 升级,但正在进行的帧流超出了正常的 HAR 请求-响应模型。
HAR 不是 DOM 快照
该文件不会以 DOM 快照的方式保留最终的文档树。
响应体可以包含原始 HTML 或 JSON,但后续的 JavaScript 变更、元素状态、渲染布局和用户可见外观是单独的问题。
HAR 并不是视频
该存档不包含页面上出现内容的视觉时间线。
当目标是回顾可见的浏览器行为时,请使用 Scrapeless 进行会话录制。当目标是检查结构化的 HTTP 流量时,请使用 HAR 捕获。
某些内容可能缺失或编码
HAR 支持可选内容字段。录制器可以省略主体,二进制资源可能会被编码。
在解析 response.content.text 之前,请检查:
text字段是否存在。- 内容类型是否符合预期。
- 如果存在,则处理
encoding字段。 - 录制配置是否省略了主体。
当无浏览器重发有效时
当实时服务器仍然接受重建的请求时,捕获的请求可以成功重发。
公共的 /api/quotes 端点有效,因为它不依赖于即将过期的身份验证会话。
当原始请求使用以下内容时,重发会变得更加复杂:
- 会话 Cookie
- CSRF 令牌
- 短期持有凭证
- 签名 URL
- 每会话请求签名
- 仅存储在浏览器中的状态
- 由前一个导航步骤生成的数据
- 内容在每个会话中变化的请求体
HAR 可能保留原始凭证值,但它不能延长这些值的有效性。一旦这些值过期,可能需要新的浏览器会话来创建新的状态。
安全重播 HAR 数据
HAR 文件可能包含敏感的会话数据。
请求和响应头可能暴露:
- Cookie
- 授权头
- API 密钥
- 会话标识符
- 内部 URL
- 端点返回的个人数据
将 HAR 文件视为敏感文档处理:
- 不要将其提交到公共代码库。
- 在分享之前移除凭证。
- 限制对存档生产会话的访问。
- 避免不必要地记录完整头部。
- 仅存储调试或审计任务所需的捕获内容。
- 根据项目的保留要求删除存档。
本教程中的存档来自公共的、未经身份验证的演示页面。这些假设不应自动应用于经过身份验证的应用程序。
常见问题
HAR 文件未出现
最常见的原因是关闭浏览器时没有明确关闭上下文。
使用:
python
context.close()
browser.close()
还要确认进程是否可以向包含 HAR_PATH 的目录写入。
HAR 不包含响应体
确认上下文使用:
python
record_har_content="embed"
然后检查 entry["response"]["content"]["text"] 是否存在。某些资源可能被省略或以编码形式表示。
urllib 拒绝一个头部名称
删除任何以 : 开头的头部。这些是 HTTP/2 伪头部,而不是普通的头部字段。
URL 和请求方法已经携带了其相关的含义。
重发的响应意外被压缩
除非重放客户端支持每个宣传的内容编码,否则不要盲目复制浏览器的 accept-encoding 值。
让 HTTP 客户端协商一个它可以解码的编码。
新的响应与 HAR 不匹配
不匹配并不一定意味着重建代码是错误的。
服务器可能返回变化的内容、时间戳、随机字段、特定地理位置的结果,或与不再有效的凭证相关的数据。
比较预期保持稳定的字段,而不是假设每个端点始终返回相同的字节。
结论
Playwright 的 record_har_path 将浏览器上下文转换为持久的 HTTP 存档。
该工作流程有三个明确的阶段:
- 将 Playwright 连接到 Scrapeless 爬虫浏览器并捕获页面会话。
- 关闭浏览器并将
log.entries视为普通 JSON。 - 使用
urllib重建一个合格的请求,并将其实时响应与捕获的主体进行比较。
浏览器仅在产生存档时是必需的。一旦 context.close() 刷新了 HAR,文件可以在没有 Playwright 安装、浏览器进程或 CDP 连接的机器上进行解析。
这种分离使得 HAR 捕获在会话后调试、内部 API 发现、网络审计和请求重建中非常有用。它也使限制保持可见:HAR 不保留 WebSocket 帧、渲染的视觉状态或捕获凭证的未来有效性。
请查看 Scraping Browser 产品页面 以获取与捕获工作流相关的浏览器会话功能,并在从演示会话转向更大捕获工作负载时查看 Scrapeless 定价计划。
Chrome DevTools 协议解释 涵盖了浏览器连接所依赖的协议表面。
准备好归档真实的浏览器会话吗?
加入 Scrapeless 社区,与其他开发者比较 Playwright 捕获模式和浏览器调试工作流: Discord · Telegram。
在 app.scrapeless.com 注册以获取免费的 Scraping Browser 运行时,然后将捕获、检查和请求重构步骤适应于与您的项目相关的公共页面。
常见问题
问:什么是 HAR 文件?
HAR 文件是浏览器会话中捕获的 HTTP 交易的 JSON 存档。它的 log.entries 数组包含每个记录请求的一个对象,嵌套请求、响应、时间和内容信息。
HAR 文件不是截图、页面视频或完整的 DOM 快照。
问:HAR 捕获与实时请求拦截有什么不同?
HAR 捕获宽泛地记录浏览器上下文的 HTTP 流量,而实时拦截观察发生的匹配请求。
实时拦截在已知端点时很有用。HAR 捕获在重要请求可能只有在会话结束后才能发现时很有用。
问:读取 HAR 文件需要浏览器吗?
不需要。完成的 HAR 文件是一个普通的 JSON 文档,可以使用 Python 的 json 模块读取。
捕获时需要浏览器,但离线检查时不需要。
问:HAR 捕获是否包括 WebSocket 流量?
HAR 捕获不归档在已建立的 WebSocket 连接内交换的消息。
当页面依赖于实时套接字数据流时,请使用 WebSocket 帧监听器。HAR 仍然可以覆盖同一页面使用的普通 HTTP 流量。
问:Playwright HAR 录制和 Scrapeless 会话录制是一样的吗?
不是。Playwright HAR 录制创建结构化的网络档案,而 Scrapeless 会话录制创建渲染的浏览器会话的可视重放。
使用 HAR 进行请求和响应分析。当调查的对象是可见的页面行为时,使用会话录制。
问:urllib 示例和 Playwright 的 route_from_har() 是一样的吗?
不是。route_from_har() 为在实时 Playwright 浏览器上下文中发出的请求提供录制的响应。
urllib 示例从 HAR 中读取请求,并向实时服务器提交新请求,而不启动浏览器。
问:为什么 HAR 包含像 :authority 和 :method 这样的头部?
这些名称是 HTTP/2 伪头字段,用于在 HTTP/2 协议中承载请求控制数据。
它们不是普通的头部字段,因此 HTTP/1.1 样式的客户端必须通过请求方法和 URL 表示其含义,而不是直接复制带冒号的名称。
问:每个捕获的请求都能成功重新发出吗?
不可以。请求只能在实时服务器接受重构的方法、URL、主体、头和凭据时才可以重新发出。
依赖于过期的 cookie、CSRF 令牌、签名 URL 或短期有效的令牌的请求可能需要一个新的浏览器会话。
问:这个工作流需要单独的代理吗?
当 Scrapeless Scraping Browser 连接已经指定所需的代理国家时,不需要单独的代理配置。
这个示例在 CDP 连接 URL 中设置了 proxyCountry=US,因此浏览器会话使用该配置的出口。
问:共享 HAR 文件是否安全?
HAR 文件应被视为敏感文件,直到其内容被审核和清理。
它可能包含 cookies、授权头、API 密钥、内部端点、提交的表单数据或私人响应内容。在共享或提交文件之前,请删除敏感值。
问:HAR 录制是否仅与 Scrapeless Scraping Browser 一起工作?
不。record_har_path 是一个 Playwright 浏览器上下文选项,可以与兼容的本地或远程浏览器一起使用。
Scrapeless Scraping Browser 提供在捕获阶段使用的受管理的远程 Chromium 环境和代理配置。
在Scrapeless,我们仅访问公开可用的数据,并严格遵循适用的法律、法规和网站隐私政策。本博客中的内容仅供演示之用,不涉及任何非法或侵权活动。我们对使用本博客或第三方链接中的信息不做任何保证,并免除所有责任。在进行任何抓取活动之前,请咨询您的法律顾问,并审查目标网站的服务条款或获取必要的许可。



