最稳妥方式是通过 launch 的 user_agent 参数设置全局 UA,它作用于所有页面请求且不可动态修改;route 拦截仅适用于特定请求但需全量替换 headers;set_extra_http_headers 对 UA 无效因 Chromium 硬性限制;移动端应优先使用 devices 模板而非手动拼接 UA。

用 launch 启动时通过 user_agent 参数设置全局 UA
Playwright 的浏览器实例在启动时就能指定默认 User-Agent,这是最常用也最稳妥的方式。它会作用于所有后续页面请求(包括导航、资源加载、fetch 等),且无需手动拦截或重写。
注意:这个参数只在 launch 或 launch_persistent_context 时生效,不能在已启动的浏览器中动态修改。
-
user_agent必须是字符串,例如"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" - 不同浏览器内核对 UA 格式无强校验,但若填空或含非法字符,部分网站可能拒绝响应
- Chrome 和 Firefox 行为一致;WebKit(Safari)下某些 UA 字符串可能触发兼容性降级,建议用其默认前缀微调
from playwright.sync_api import sync_playwright
<p>with sync_playwright() as p:
browser = p.chromium.launch(
user_agent="MyBot/1.0 (Custom UA)"
)
page = browser.new_page()
page.goto("<a href="https://www.php.cn/link/7ac09a39f051ff62eefbafd363b1bbb3">https://www.php.cn/link/7ac09a39f051ff62eefbafd363b1bbb3</a>")
print(page.text_content("pre")) # 查看实际发送的 headers
browser.close()用 route 拦截并修改单个请求的 UA(不推荐用于全局)
如果你只需要改某几个特定请求(比如 API 调用),而不是整个页面行为,可以用 page.route 拦截并覆写 User-Agent。但要注意:这仅影响被匹配到的请求,且必须在 page.goto 前设置,否则可能错过初始 HTML 请求。
关键限制:Playwright 的 route 不允许直接修改原始请求头中的 User-Agent(底层 Chromium 禁止),你只能通过 route.continue_(headers=...) 全量替换 headers —— 这意味着你得自己补全其他必要头(如 Accept、Sec-Fetch-* 等),否则可能触发 400 或 CORS 拒绝。
立即学习“Python免费学习笔记(深入)”;
- 务必保留原始 headers 中除
User-Agent外的关键字段,建议先route.fetch获取原始 headers 再修改 - 匹配路径用 glob 或正则,避免过度匹配(例如
"**/api/**"比"**"更安全) - 该方式无法影响
page.goto加载的主文档请求(因为 route 设置太晚),除非配合context.route+ 提前启动上下文
为什么 setExtraHTTPHeaders 对 UA 无效?
Playwright 的 page.set_extra_http_headers 看似能加 header,但它明确排除了 User-Agent、Connection、Host 等受控头——这是 Chromium 的硬性限制,不是 Playwright 的 bug。调用后不会报错,但 UA 完全不会变化。
常见误操作:在 page.goto 后调用 set_extra_http_headers,以为能影响后续请求。实际上它只对之后由脚本发起的 fetch/XMLHttpRequest 生效(且仍不支持 UA)。
- 有效场景:给 JS 发起的
fetch("/api/data")添加X-Api-Version这类自定义头 - 无效场景:试图覆盖页面导航、图片/CSS/JS 加载、或者任何浏览器自动发起的请求的
User-Agent - 替代方案:真要动态切 UA,只能重建
context或browser实例(开销大,慎用)
移动端 UA 和设备模拟别混用
很多人想设移动端 UA,就直接填个手机 UA 字符串。但光改 UA 不够:缺少 viewport、touch 事件支持、User Agent 中的 Mobile 标识可能被忽略,甚至某些站点会检查 maxTouchPoints 或 platform。
正确做法是用 Playwright 内置的设备参数,它会自动配齐 UA、viewport、userAgentMetadata、hasTouch 等:
from playwright.sync_api import sync_playwright
from playwright.sync_api import devices
<p>with sync_playwright() as p:
iphone = devices["iPhone 13"]
browser = p.webkit.launch()
context = browser.new_context(**iphone) # 自动带 Mobile UA 和 touch 支持
page = context.new_page()
page.goto("<a href="https://www.php.cn/link/7ac09a39f051ff62eefbafd363b1bbb3">https://www.php.cn/link/7ac09a39f051ff62eefbafd363b1bbb3</a>")自行拼接移动端 UA 容易漏掉 Sec-CH-UA-Mobile 这类 Client Hints,而设备模板会一并处理。真要微调 UA,也在设备配置基础上改 user_agent 字段,不要从零手写。
UA 是个表面简单、实则牵一发而动全身的配置项。浏览器内核限制、服务端 UA 检查逻辑、Client Hints 新标准、以及 Playwright 各层 API 的作用域差异,都会让“改个 UA”变得不那么直觉。动手前先想清楚:你要覆盖的是哪一类请求?是否需要连带 viewport/touch 一起模拟?有没有服务端在验 Sec-CH-UA?这些比代码多写一行更重要。


















