网页越来越复杂,Playwright 如何结合代理ip实现稳定采集

随着 React、Vue、Next.js 等现代前端框架的普及,传统 requests 爬虫越来越难直接获取完整页面内容。同时,各类网站不断升级反爬策略,仅依靠浏览器自动化也难以支撑长时间、大规模采集。本文将从现代网页的发展趋势出发,介绍 Playwright 的优势,并结合动态代理 IP,构建一套更加稳定、高效的网页采集方案。

目录

引言

一、为什么现代网页越来越复杂?

[二、为什么越来越多项目开始使用 Playwright?](#二、为什么越来越多项目开始使用 Playwright?)

[动态代理为什么开始成为 Playwright 的"标配"?](#动态代理为什么开始成为 Playwright 的"标配"?)

[三、代理 IP 在 Playwright 中到底解决了什么?](#三、代理 IP 在 Playwright 中到底解决了什么?)

为什么浏览器自动化仍然容易被限制?

四、工程实践中的几点建议


引言

如果几年前问大家:"Python 爬虫应该用什么?"

相信大多数人的回答都是:

requests + BeautifulSoup

或者:

requests + lxml

因为那个时代的大多数网站都是服务端渲染(SSR),服务器直接返回完整 HTML,requests 获取源码之后即可解析。

例如:

复制代码
import requests

html = requests.get("https://example.com").text
print(html)

但是近几年,越来越多开发者发现:

  • requests 返回的 HTML 越来越少;
  • 页面源码只有一个 <div id="root"></div>
  • 浏览器能够正常打开,代码却抓不到数据;
  • 即使拿到了接口,也越来越容易出现 403、429 等访问限制。

很多人第一反应是 网站反爬越来越厉害了

其实并不完全如此。真正发生变化的是 现代网页的架构

一、为什么现代网页越来越复杂?

过去,一个网页通常是这样的流程:

复制代码
浏览器 ------  HTTP请求 ------ 服务器 ------ 返回完整 HTML

浏览器拿到 HTML 后直接显示。

而 requests 做的事情,其实就是模拟浏览器发送 HTTP 请求,因此能够直接获取完整页面。

但是今天,越来越多的网站采用了:

  • React
  • Vue
  • Angular
  • Next.js
  • Nuxt

等现代前端框架。

浏览器真正访问的是:

复制代码
浏览器 -> HTML(几乎空)-> JavaScript -> Ajax / Fetch -> API -> DOM 渲染

也就是说:

requests 获取到的源码可能只有:

复制代码
<body>
    <div id="root"></div>
</body>

真正的数据,是浏览器执行 JavaScript 后动态生成的。

因此很多开发者误以为网站加密了;而实际上只是浏览器替开发者完成了 JavaScript 渲染。


除此之外,现在的网站还有越来越完善的访问控制机制,例如:

  • JavaScript Challenge
  • Cloudflare 防护
  • 浏览器指纹检测
  • Cookie 校验
  • LocalStorage
  • SessionStorage
  • IP Reputation(IP信誉)
  • 请求频率限制

这些机制叠加之后,仅依赖 requests 已经很难完成复杂网页的数据采集。

因此,越来越多项目开始使用浏览器自动化框架。


二、为什么越来越多项目开始使用 Playwright?

如果说 requests 是:

HTTP 请求工具

那么 Playwright 更像是:

真正控制浏览器。

它启动的是 Chromium、Firefox 或 WebKit 浏览器,而不是简单发送 HTTP 请求。

整个流程变成:

复制代码
Python -> Playwright -> Chromium 浏览器 -> 执行 JavaScript -> 网页完整渲染 -> 获取 DOM

因此,对于:

  • React
  • Vue
  • Next.js
  • 无限滚动页面
  • 登录后页面
  • SPA 单页应用

Playwright 都具有天然优势。

相比 Selenium,Playwright 还拥有不少优点:

  • 自动等待元素加载
  • 多浏览器支持
  • Browser Context 隔离
  • 更快的执行速度
  • 更丰富的 API
  • 官方持续维护

例如访问一个页面,仅需要几行代码:

复制代码
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)

    page = browser.new_page()

    page.goto("https://baidu.com")

    print(page.title())

    browser.close()

相比 requests,它获取到的是:

浏览器真正渲染完成后的页面。

因此,Playwright 已经逐渐成为现代网页采集的重要工具。


动态代理为什么开始成为 Playwright 的"标配"?

不过,当很多开发者开始使用 Playwright 后,很快又会遇到新的问题。

浏览器虽然能够正常打开页面,但如果:

  • 长时间运行;
  • 高并发采集;
  • 多任务同时访问;
  • 不同地区的数据抓取;

仍然可能触发网站的访问限制。

原因也很简单。

浏览器解决的是页面渲染问题, 而不是网络身份问题。

因此,在很多生产项目中,浏览器自动化通常都会配合代理 IP 一起使用。

相比传统维护本地代理池,现在越来越多代理服务开始提供 HTTP API 获取代理 的方式。

例如测试过程中使用的 B2Proxy,可以直接通过 API 获取代理 IP,返回 JSON 数据,程序无需提前维护大量代理列表,只需要在浏览器启动之前获取一次代理即可。

这种方式最大的优势是:

  • 可以按数量动态获取代理;
  • 支持不同国家或地区节点;
  • 返回 JSON,便于 Python 直接解析;
  • 不需要自行维护代理池和更新机制。

对于 Playwright 这类浏览器自动化项目来说,代理获取已经可以像调用普通 HTTP 接口一样简单。

三、代理 IP 在 Playwright 中到底解决了什么?

很多刚接触 Playwright 的开发者都会有一个疑问

为什么浏览器自动化仍然容易被限制?

很多开发者第一次接触 Playwright 时都会有一个误区,认为浏览器自动化已经模拟了真实浏览器,就不再需要代理 IP。实际上,Playwright 和代理 IP 解决的是两个完全不同的问题。Playwright 负责浏览器渲染、JavaScript 执行、页面交互等浏览器层面的能力,而代理 IP 决定的是请求最终以什么网络身份访问目标网站。浏览器能够正常打开页面,并不意味着目标网站会一直接受来自同一个 IP 的大量访问请求。

对于一个短时间运行的脚本来说,固定 IP 往往不会出现明显问题,但在真实项目中,一个采集任务可能持续运行数小时甚至数天。当所有请求都来自同一个出口 IP 时,即使浏览器行为完全正常,也可能因为访问频率过高、IP 信誉下降或者地区限制等原因触发网站风控。因此,大规模网页采集通常都会将浏览器自动化与代理 IP 结合使用,Playwright 负责解决页面渲染问题,而代理 IP 则负责网络出口和访问身份,两者共同组成完整的采集方案。

与过去维护代理 IP 列表相比,现在越来越多代理服务开始提供 API 获取模式。程序无需提前保存几百甚至几千个代理地址,只需要在启动浏览器之前请求一次代理接口,即可获得最新可用的代理 IP。

这种方式不仅降低了维护成本,也更适合自动化项目持续运行。例如 B2Proxy 提供的 动态代理ip 就采用了 API 获取模式,接口返回标准 JSON 数据,可以直接在 Python 中解析,无论是 requests 还是 Playwright,都能够快速完成代理配置。

获取代理后,Playwright 配置代理实际上非常简单,只需要在启动浏览器时传入 proxy 参数即可。

复制代码
from playwright.sync_api import sync_playwright
import requests


# 注意要将下面连接换成自己的api 获取api前往:https://www.b2proxy.com/?utm_t=1&utm_i=192 (需要海外网络)
api = "http://global.rrp.bestgo.work:8089/gen?zone=custom&ptype=1&count=10&proto=http&stype=json&sessType=rotating"

proxy = requests.get(api).json()["data"][0]

with sync_playwright() as p:
    browser = p.chromium.launch(
        headless=False,
        proxy={
            "server": f"http://{proxy['ip']}:{proxy['port']}"
        }
    )

    page = browser.new_page()
    page.goto("https://httpbin.org/ip")
    print(page.text_content("body"))

    browser.close()

整个过程几乎不需要额外修改业务代码,只是在浏览器启动之前增加了一次代理获取过程。相比维护本地代理池,这种方式最大的优势是代理始终保持最新状态,同时还能根据不同业务动态调整代理数量、国家地区以及协议类型,更容易集成到长期运行的采集项目中。

在实际项目中,更推荐将代理获取封装成独立模块,而不是在每一个采集脚本中重复调用代理接口。浏览器只负责页面采集,代理管理模块负责获取代理、异常切换以及失败重试,两者相互独立,后续即使更换代理服务,也无需修改采集逻辑,只需要替换代理提供层即可,这也是目前大多数大型采集系统采用的架构方式。

四、工程实践中的几点建议

当 Playwright 与代理 IP 结合之后,一个简单的网页采集脚本基本就具备了生产环境的雏形。不过,在实际项目中,还需要关注几个容易被忽略的细节。

首先,不建议每次任务都重新启动浏览器。Chromium 的启动成本相对较高,更推荐复用 Browser 实例,通过多个 Browser Context 隔离不同采集任务。这样不仅可以降低资源消耗,也能减少浏览器启动时间。

例如,一个 Browser 可以创建多个 Context:

复制代码
browser = playwright.chromium.launch(headless=True)

for _ in range(5):
    context = browser.new_context()
    page = context.new_page()

这种方式比不断创建 Browser 更轻量,也是 Playwright 官方推荐的使用方式。

其次,代理管理尽量不要直接写在业务代码中,而是封装成一个统一模块。浏览器只负责页面采集,代理获取交给 Proxy Manager 处理,后续无论更换代理服务还是增加代理验证,都不会影响业务逻辑。

例如:

复制代码
def get_proxy():
    res = requests.get(API_URL).json()
    proxy = res["data"][0]
    return f"http://{proxy['ip']}:{proxy['port']}"

业务代码只需要:

复制代码
browser = playwright.chromium.launch(
    proxy={
        "server": get_proxy()
    }
)

整个采集流程会更加清晰。

另外,并发数量并不是越高越好。相比一次启动几十个浏览器,更推荐根据机器性能控制 Browser 数量,每个 Browser 下创建多个 Context,再配合异步任务进行调度。这样既能保证采集效率,也能降低目标网站对异常访问的识别概率。

在测试过程中,我这里使用的是B2proxy

为什么选择 B2Proxy 您通往 可靠 代理的门户https://www.b2proxy.com/?utm_t=1&utm_i=192 它提供了标准 HTTP API 获取代理,只需要在启动浏览器之前请求一次接口,就可以获得最新代理 IP,返回 JSON 数据后即可直接配置到 Playwright 中。相比维护本地代理池,这种 API 化的方式更适合长期运行的自动化采集项目,如果需要切换国家、协议或代理数量,也只需要调整接口参数即可,不需要修改业务代码。

最后,建议为采集任务增加简单的异常重试机制。当页面访问失败时,不要立即退出,而是重新获取代理并重新创建 Browser Context。例如:

复制代码
try:
    page.goto(url, timeout=30000)
except Exception:
    browser.close()
    # 获取新的代理后重新启动浏览器

这种方式虽然代码不多,但对于长期运行的采集任务来说,可以显著提高整体稳定性。

现代网页采集已经从简单的 HTTP 请求演变为浏览器自动化、代理调度和异常恢复共同协作的工程体系。Playwright 负责页面渲染和交互,代理 IP 提供稳定的网络出口,再配合合理的浏览器复用与异常处理策略,基本可以覆盖绝大多数现代网页采集场景。对于需要长期维护的项目,这种架构相比传统 requests 爬虫更具扩展性,也更容易适应不断变化的网站环境。