用Playwright打造可靠的企业级采集方案--从单机验证到集群化落地

简介: 本项目将单机Playwright爬虫逐步演进为分布式集群,解决脚本不稳定、限速、维护难等问题。以招聘数据采集为例,实现从页面解析、代理IP轮换、Redis任务队列到多机并发的完整链路,结合MongoDB/Elasticsearch落库与可视化,形成可复用的生产级爬虫架构,适用于数据分析、岗位监控等场景。

---从单机验证到集群化落地

为什么要做这个项目(背景与动机)

在公司做数据产品时,我们常常遇到三个痛点:脚本跑不稳、页面渲染抓不到数据、以及规模化后调度和重试逻辑变得难以维护。最开始通常是一两个 Python 脚本在开发机上跑通,但到了生产环境,问题就会接踵而来:单机负载、被目标站点限速、以及不同页面结构带来的脆弱性。
本项目的目标是把一个可验证的单机 Playwright 爬虫,逐步演进成能在多台机器上并行工作的集群化方案,并把关键实现、思路与示例代码记录下来,方便求职/展示或直接在企业内复用。

我们要拿到什么数据(数据目标)

本文以招聘网站为示例,目标字段很常见也很实用,便于后续做分析:

  • 职位名称(title)
  • 公司名(company)
  • 城市 / 工作地点(city)
  • 薪资(salary)
  • 发布时间(publish_time)

这些字段既能做趋势分析,也能用于构建岗位搜索引擎或行业报告。

技术栈与选型理由(为什么选 Playwright)

  • Playwright:相比传统的 requests,它能原生处理 JS 渲染、等待节点、拦截资源等,稳定性和可控性更好。
  • 代理 IP(示例:亿牛云):高频访问需要代理来分摊流量并降低被限制的概率。
  • Redis(任务队列) + 多进程/多机器:用于实现任务分发、去重与伸缩。
  • MongoDB / Elasticsearch:分别用于原始数据落盘和后续的检索/分析/可视化。

这种组合能在保证可维护性的同时兼顾扩展与性能。

核心实现(从单机到分布式的演进)

1)单机版:先把逻辑跑通

这是最直接的版本,用来确认页面结构、JS 渲染及提取逻辑是否稳健。代码示例(异步 Playwright):

# single_playwright.py
import asyncio
from playwright.async_api import async_playwright

# ====== 代理配置(示例:亿牛云) ======
proxy_host = "proxy.16yun.cn"
proxy_port = "3100"
proxy_user = "16YUN"
proxy_pass = "16IP"

async def crawl_page(keyword: str, page_num: int = 1):
    """
    单页抓取示例函数:访问并解析职位卡片,返回字典列表
    """
    async with async_playwright() as pw:
        # 启动浏览器(无头/有头可根据调试需要切换)
        browser = await pw.chromium.launch(headless=True,
                                           proxy={
   
                                               "server": f"http://{proxy_host}:{proxy_port}",
                                               "username": proxy_user,
                                               "password": proxy_pass
                                           })
        page = await browser.new_page()
        url = f"https://example-job-site.com/search?kw={keyword}&p={page_num}"
        await page.goto(url, timeout=30000)
        # 等待职位列表出现(根据实际页面调整选择器)
        await page.wait_for_selector('.job-card', timeout=15000)

        cards = await page.query_selector_all('.job-card')
        results = []
        for c in cards:
            # 下面的选择器只是示例,实际按目标站点调整
            title = await c.query_selector_eval('.job-title', 'el => el.textContent.trim()')
            company = await c.query_selector_eval('.company', 'el => el.textContent.trim()')
            salary = await c.query_selector_eval('.salary', 'el => el.textContent.trim()')
            city = await c.query_selector_eval('.city', 'el => el.textContent.trim()')
            results.append({
   
                "title": title,
                "company": company,
                "salary": salary,
                "city": city
            })

        await browser.close()
        return results

if __name__ == "__main__":
    rows = asyncio.run(crawl_page("Python 爬虫", page_num=1))
    for r in rows:
        print(r)

说明:先把单页、单关键词跑通,再处理翻页、异常与重试逻辑。单机阶段以稳定为先,别急着并发。


2)改造为可并发的单机版本(充分利用单机资源)

当单页逻辑稳定后,我们会把单页函数包装成任务池,利用 asyncio 并发多个标签页(或多个浏览器实例)来提高吞吐。示例要点:

  • 限制并发并设置合理的超时
  • 处理网络异常与元素不存在的情况(捕获并重试)
  • 记录日志与关键指标(成功率、耗时)

(此处略去并发包装的完整代码,实际项目中可用 asyncio.Semaphore 控制并发数。)

3)走向分布式:Redis 作任务队列(生产者 / 消费者模型)

把任务放到 Redis 列表或流(stream)里,多台机器从队列里取任务并执行。示例消费端伪代码:

# worker.py (伪代码,需加异常处理与日志)
import asyncio
import aioredis
from single_playwright import crawl_page  # 引入前面实现的单页函数

REDIS_URL = "redis://127.0.0.1:6379/0"
TASK_QUEUE = "job_tasks"

async def worker_loop():
    redis = await aioredis.from_url(REDIS_URL)
    while True:
        task = await redis.lpop(TASK_QUEUE)
        if not task:
            await asyncio.sleep(1)
            continue
        # 假设任务是 JSON 字符串,包含 keyword 和 page_num
        task = task.decode("utf-8")
        # 这里简单演示,生产环境请解析 JSON 并做失败重试、幂等处理
        keyword = task
        print("取到任务:", keyword)
        results = await crawl_page(keyword)
        # 结果入库/发送到下游(例如 MongoDB/Elasticsearch)
        # save_results(results)

关键点:

  • 任务应包含去重标识(比如 URL 哈希或唯一 ID)以避免重复抓取
  • 设计任务超时与重试策略(比如 N 次重试后写入失败队列由人工检查)
  • 监控队列深度、消费速率以判断是否需要扩容

运营与工程化要点(实用经验)

  • 代理策略:不要把所有流量都打到一个代理 IP,建议轮换或使用多个出口;对静态资源设置拦截,减少不必要带宽消耗。
  • 资源拦截:Playwright 支持 route.abort() 拦截图片、视频、广告等资源,显著减轻网络压力。
  • 健康检查:为每个爬虫进程加上心跳上报(Prometheus / Pushgateway)便于发现挂死或慢速任务。
  • 错误收集:记录页面快照(在非敏感场景),异常堆栈与请求/响应,便于定位问题。
  • 容器化:把爬虫打包为 Docker 镜像,用 Kubernetes 编排能显著简化部署与弹性伸缩。
  • 数据质量控制:入库前做字段校验、去重与时间窗口过滤,避免脏数据污染分析维度。

数据落盘与可视化(展示成就)

落库示例(MongoDB 文档结构):

{
   
  "title": "Python 爬虫工程师",
  "company": "某某科技",
  "salary": "20k-35k",
  "city": "上海",
  "publish_time": "2025-09-01",
  "source": "example-job-site",
  "crawl_at": "2025-09-23T10:00:00"
}

落库后可以做的展示(列举思路):

  • 城市分布热力图(Kibana / Superset)
  • 薪资区间柱状图(按岗位/城市分组)
  • 岗位关键词云(用于挖掘热门技能)
  • 时间序列分析(岗位量随时间变化)

这些可视化既能作为产品端仪表盘,也可以作为简历/作品集中突出的数据展示部分。

相关文章
|
23天前
|
人工智能 自然语言处理 JavaScript
借助Playwright MCP实现UI自动化测试:全面指南与实战案例
本文介绍了Playwright与MCP协议结合的UI自动化测试新方法。通过自然语言指令替代传统脚本编写,详细讲解了环境搭建、核心工具和实战案例,展示了从登录测试到报告生成的完整流程。这种创新方式显著降低了技术门槛,让非专业人员也能快速创建可靠的自动化测试。
|
4月前
|
Web App开发 开发框架 前端开发
Playwright与PyTest结合指南
本教程介绍如何结合Playwright与PyTest进行Web自动化测试,涵盖环境搭建、测试编写、配置管理、Fixtures使用及高级技巧,助你高效构建稳定、可维护的测试方案。
|
5月前
|
数据采集 缓存 NoSQL
分布式新闻数据采集系统的同步效率优化实战
本文介绍了一个针对高频新闻站点的分布式爬虫系统优化方案。通过引入异步任务机制、本地缓存池、Redis pipeline 批量写入及身份池策略,系统采集效率提升近两倍,数据同步延迟显著降低,实现了分钟级热点追踪能力,为实时舆情监控与分析提供了高效、稳定的数据支持。
200 1
分布式新闻数据采集系统的同步效率优化实战
|
1月前
|
数据采集 Web App开发 调度
我为什么彻底切到Playwright
本文分享从Puppeteer迁移到Playwright的实战经验,详解架构升级动因、模块重构与核心代码。Playwright凭借更强的隔离性、原生反检测支持、简洁代理配置及多浏览器兼容,彻底解决Puppeteer时代资源争抢、稳定性差等痛点,助力构建高可用、易维护的现代数据系统。
|
3月前
|
存储 前端开发 安全
实现“永久登录”:针对蜻蜓Q系统的用户体验优化方案(前端uni-app+后端Laravel详解)-优雅草卓伊凡
实现“永久登录”:针对蜻蜓Q系统的用户体验优化方案(前端uni-app+后端Laravel详解)-优雅草卓伊凡
215 5
|
3月前
|
存储 算法 安全
“卧槽,系统又崩了!”——别慌,这也许是你看过最通俗易懂的分布式入门
本文深入解析分布式系统核心机制:数据分片与冗余副本实现扩展与高可用,租约、多数派及Gossip协议保障一致性与容错。探讨节点故障、网络延迟等挑战,揭示CFT/BFT容错原理,剖析规模与性能关系,为构建可靠分布式系统提供理论支撑。
239 2
|
1月前
|
存储 JSON 前端开发
绕过验证码与登录:Playwright 自动化测试的身份认证策略
在Playwright自动化测试中,登录和验证码常成“拦路虎”。本文介绍四种绕过策略:复用Cookie/LocalStorage状态、调用API获取Token、测试环境禁用验证码、使用第三方测试账号。核心思想是“绕过而非破解”,提升测试效率与稳定性。推荐优先使用状态复用,避免重复登录,让测试聚焦核心业务逻辑。
|
1月前
|
人工智能 自然语言处理 监控
小白必备:轻松上手自动化测试的强大工具
本文介绍Playwright MCP如何通过结合自然语言处理与测试自动化,实现从需求描述到代码生成的转变。该方案大幅降低脚本编写和维护成本,提升测试稳定性,为传统自动化测试提供智能化升级路径。
|
8月前
|
数据采集 消息中间件 Kubernetes
容器化爬虫部署:基于K8s的任务调度与自动扩缩容设计
随着业务复杂度提升,传统定时任务和手工扩缩容难以满足高并发与实时性需求。本文对比两种基于 Kubernetes 的爬虫调度与扩缩容方案:CronJob+HPA 和 KEDA。从调度灵活性、扩缩容粒度、实现难度等维度分析,并提供 YAML+Python 示例。方案 A(CronJob+HPA)适合固定定时任务,配置简单;方案 B(KEDA)支持事件驱动,适合高并发与异步触发场景。根据实际需求可混合使用,优化资源利用与效率。
283 4
|
3月前
|
数据采集 弹性计算 Kubernetes
单机扛不住,我把爬虫搬上了 Kubernetes:弹性伸缩与成本优化的实战
本文讲述了作者在大规模爬虫项目中遇到的挑战,包括任务堆积、高失败率和成本失控。通过将爬虫项目迁移到Kubernetes并使用HPA自动伸缩、代理池隔离和Redis队列,作者成功解决了这些问题,提高了性能,降低了成本,并实现了系统的弹性伸缩。最终,作者通过这次改造学到了性能、代理隔离和成本控制的重要性。
141 2
单机扛不住,我把爬虫搬上了 Kubernetes:弹性伸缩与成本优化的实战