Serverless+Playwright的组合值得用吗?我们做了个测试

37 阅读5分钟

——从一次冷启动事故开始谈

如果说过去的爬虫架构像一辆固定路线的公交车,那么 Serverless 的出现,让开发者突然拥有了“随叫随到的无人驾驶出租车”。
不需要长时间维护服务器,不需要考虑资源闲置成本,只要写好脚本、部署到云端,触发时自动运行,空闲时不产生费用——听起来特别完美。

于是,越来越多团队开始尝试把数据抓取、文本分析、异步调度等工作往 Serverless 上迁移,尤其是定时任务型爬虫、事件触发型采集器、有波动特征的流量抓取任务。理论上,Serverless 可以天然适应这类模式:来任务就执行,不来就安静。

然而,技术世界里,总有一些看起来“特别合适”的组合,真正落地时却暴露出意想不到的问题。Serverless + 爬虫,就是其中一个典型案例——尤其是涉及浏览器自动化(如 Playwright/Chromium)和反爬策略敏感的网站,例如知乎。

这篇内容,就是基于一次真实迁移过程里发生的故障、排查和改进,来看看 Serverless 爬虫到底能不能走远,它的瓶颈在哪里,以及该怎么绕过去。


整个事故的时间线

为了让问题更直观,我们把整个过程按照时间顺序展开,像复盘一样:

09:00 —— 新架构上线

项目正式切到 Serverless 环境。部署方式是 AWS Lambda + Playwright,并接入代理池。
迁移后的第一感受是轻松:没有常驻服务、没有监控守护进程、没有资源浪费。

09:10 —— 流量突然上涨

由于某个关键词相关话题登上趋势榜,触发器调用次数升级,Lambda 实例快速横向扩容。
乍看一切正常,扩展能力符合预期。

09:11 —— 出现第一批错误

运行时延开始从原本 6-8 秒增长到 15 秒以上,日志里出现 Navigation Timeout。
简单翻译一下:不是代码坏了,而是执行环境启动得太慢,浏览器又比较重,冷启动直接拖垮了任务执行窗口。

09:12 —— 失败率继续上升

随着更多实例被触发,每个实例都重复初始化 Playwright、创建浏览器上下文、完成代理认证。
再叠加知乎对短时间重复访问的限制,失败任务占比一度突破 40%。

09:15 —— 代理池也被挤压

Serverless 扩容是瞬间并发,但代理出口带宽、并发连接数、可分配 IP 资源却不是无限的。
结果就是:部分请求卡在 TLS 认证,部分 IP 被限制访问,还有部分请求压根没排到出口。

10:00 —— 临时降载 + 补偿机制上线

通过降低并发上限、增加延迟、启用备用代理池,任务逐渐恢复正常。
这次事故让我们重新审视了“Serverless + 浏览器爬虫”这一组合的现实挑战。


问题到底出在哪里?

总结下来,问题不是某一块技术失效,而是多层叠加导致的连锁反应。

1. 冷启动是核心问题

浏览器环境不是轻量级脚本,Playwright 或 Chromium 启动本身就需要时间,而 Serverless 容器生命周期短,不可复用,导致初始化成本被无限放大。

2. 并发不是免费资源

Serverless 的扩容能力很强,但:

  • 目标网站有限流
  • 代理池有限并发
  • 数据写入管道有限速

换句话说,外部环境不支持你无限扩容。

3. 可观测性差

传统服务器可跟踪进程状态,Serverless 则是一次性实例,排查日志需要聚合和关联分析。
故障定位难度提升。

4. 反爬策略敏感度提高

知乎对高频访问的敏感度不低,而 Serverless 容易形成短时间分布式并发峰值,这种流量特征更接近异常行为。


那怎么优化?

把 Serverless 用好,不是简单迁移,而是要改造架构,让它适配运行方式。

我们做了下面这些调整:

问题解决方向
冷启动太慢使用 Browserless/Playwright Remote,让浏览器不随实例重复启动
并发压缩代理池设计访问调度器,控制速率并动态轮换 IP
网络波动导致超时增加重试规则、指数退避策略、任务补偿
监控碎片化引入链路追踪与日志聚合
被目标站点识别为机器人增加行为随机化策略、UA池、Cookie池

最终架构不再是“Serverless 单兵作战”,而是“调度层 + 执行层 + 浏览器服务 + 代理管理”。


示例代码: Serverless 爬虫核心逻辑

下面是教学示例,重点展示代理设置、UA 处理以及 Playwright 搜索逻辑。

"""
示例:Playwright + 代理 + 随机 UA
目标站点:知乎搜索页
注意:此代码用于技术研究,不用于违规用途
"""

import asyncio
import random
from playwright.async_api import async_playwright

# === 代理配置(替换为你自己的亿牛云代理账号信息 www.16yun.cn) ===
PROXY = {
    "server": "http://proxy.16yun.cn:12345",
    "username": "your_username",
    "password": "your_password"
}

# User-Agent 池,避免每次访问特征一致
UA_LIST = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X) AppleWebKit/605.1.15 Safari/605.1.15"
]


async def crawl(keyword: str):
    playwright = await async_playwright().start()

    browser = await playwright.chromium.launch(
        headless=True,
        args=["--disable-blink-features=AutomationControlled"]
    )

    # 使用代理 + 随机 UA
    context = await browser.new_context(
        proxy=PROXY,
        user_agent=random.choice(UA_LIST)
    )

    page = await context.new_page()

    await page.goto(f"https://www.zhihu.com/search?q={keyword}", timeout=15000)

    await page.wait_for_selector("div.SearchResult-Card")
    cards = await page.query_selector_all("div.SearchResult-Card")

    results = []
    for c in cards[:3]:
        title = await c.inner_text()
        results.append(title)

    await browser.close()
    await playwright.stop()
    return results


if __name__ == "__main__":
    print(asyncio.run(crawl("AI 爬虫")))

最后的结论

Serverless 不是爬虫的终极答案,但它绝不是一个伪需求。
对于任务周期性、流量波动明显、不需要 24 小时在线运行的采集系统,它依然有很高价值。

但要让它能跑稳,我们必须接受一个现实:

Serverless 不是“把脚本丢进去就能飞”的万能模型,而是需要配合浏览器常驻服务、访问调度器、代理策略和缓存体系,才能真正发挥优势。

换句话说,Serverless 爬虫能走多远,不取决于云服务,而取决于架构是否适配它的运行逻辑。