
我最近把一套用 RPA-Python 思路写的 Web 巡检脚本搬到了 Netlify 上做成了一个每天自动执行、结果直接以网页形式展示的测试站点。整个过程其实就 5 步本地写脚本、固定依赖、接 Netlify、配定时触发、加通知。整套链路落地之后我再也不用每天手动打开浏览器做回归检查也不用担心测试报告散落在本地文件里没人看。这篇文章就围绕这个标题展开——RPA-Python 本身不神秘它就是用脚本模拟真实用户操作浏览器、填表、点击、断言页面状态Netlify 作为前端托管平台恰好能把这种定期执行的“测试机器人”变成一个云上运行的定时任务顺便把生成的 HTML 报告当作静态站点发布出去。适合已经在用 Selenium、Playwright 写自动化脚本但还停留在“本机跑跑”阶段的开发者也适合想给团队搭建一个低成本测试看板但不想自己维护服务器的同学。下面从头到尾讲清楚尽量把每个选择和每一步操作背后的原因也讲透。1. 方案拆解一个“会自己干活”的测试站点1.1 这个集成到底解决了什么问题先说痛点。Web 测试自动化这个事大多数人走到“本地能跑”就停了。脚本写了一堆能登录、能点击、能断言但跑完以后报告扔在本地过两天就被覆盖或者依赖本机的 Chrome、Python 环境换一台电脑就起不来再想定时跑就得开一台服务器挂着。这些问题叠加在一起自动化脚本的价值就打了大折扣。RPA-Python 的思路在这里刚好补位。RPA机器人流程自动化强调的本来是“模拟人操作业务系统”但它和 Web 自动化测试的本质其实是同一件事用程序驱动浏览器完成真实用户会做的动作再对结果做校验。区别只是业务目标不同——RPA 关注的是流程能不能走通测试关注的是页面有没有按预期渲染。把这个认知拉通之后你就发现现有的 Playwright、Selenium 脚本完全可以套一个“巡检机器人”的壳当作一个长期运行的线上服务来看待。Netlify 在这个链路里承担三个角色第一它提供一个和 Git 仓库联动的构建环境每次 push 或调用构建 Hook都会在干净容器里重新安装依赖、跑脚本第二它的构建产物天然就是一个静态网站测试报告直接发布成可访问的 URL第三它允许通过外部定时任务触发构建这样“每天自动跑一次测试”就不需要你维护任何服务器。也就是说这套方案用一个已经存在的托管平台同时解决了执行环境、展示层、定时调度三个问题。这也是我为什么选它而不是去折腾一台云主机的核心原因——少维护一个东西长期就少一堆事。1.2 为什么选择构建触发而不是 Serverless 定时函数我最初也考虑过用 Netlify 的定时函数Scheduled Functions来执行测试逻辑。毕竟那看起来更“原生”直接在 Functions 里写定时逻辑好像很省事。但深入了解之后放弃了原因很实在。Netlify Functions 底层是函数即服务FaaS适合短小轻量的请求处理运行时长和内存限制都比较严格。常规的 RPA-Python 脚本要启动浏览器、加载页面、模拟点击输入、截图再等网络响应一套流程跑下来少说几十秒复杂业务甚至几分钟。硬塞进函数里很容易打到超时上限。更麻烦的是FaaS 环境里想要跑完整 Chromium 无头浏览器涉及的依赖安装和系统库配置非常别扭远不如构建环境来得灵活。反过来看 Netlify 的构建任务它的定位是“跑构建、出产物”。部署一个静态站点本来就需要完整执行环境大部分时候还有十几分钟的构建时间窗口。我只不过把构建命令从单纯的eleventy或hugo换成了“先跑 Python 脚本、再输出报告目录”。浏览器依赖Chromium在构建环境里可以通过前置脚本一次性装好脚本跑完以后生成的 HTML 报告直接作为站点发布。构建时间窗口、系统依赖、产物托管三个问题一次全解决。这是一个很关键的选型判断不是所有云功能都适合跑长任务稍微把思路从“执行函数”转向“构建产物”反而能撬动平台的既有能力。定时触发方面Netlify 提供了 Build Hooks构建钩子给它发一个 POST 请求就能触发一次新构建配合 GitHub Actions 的 cron 定时任务调用逻辑非常简单。整套链路没有常驻服务没有额外计费成本就非常低。2. 五步落地从零到定时运行的完整流程2.1 第一步本地写一个可重复执行的 RPA-Python 巡检脚本本地脚本是整个方案的核心其他步骤都是围绕它展开的。我的建议是先把“单人可复现”这个目标放在第一位脚本要能在干净环境里一次跑通要有明确的退出码要把结果输出到独立目录不能依赖本地乱七八糟的特定路径。技术选型上我更倾向 Playwright 而不是老牌的 Selenium。原因有三第一Playwright 的自动等待机制对页面加载时序更宽容写出来的脚本不容易因为“元素还没出现就点击”这种问题崩掉第二它内置了截图和比较能力生成报告时非常顺手第三无头浏览器启动时的系统依赖安装更标准化方便后面部署到 Netlify。下面是我实际使用过的一个基础脚本骨架。import json import time import os from datetime import datetime from playwright.sync_api import sync_playwright BASE_URL os.getenv(RPA_BASE_URL, https://example.com/login) USERNAME os.getenv(RPA_USERNAME, demo) PASSWORD os.getenv(RPA_PASSWORD, demo) RESULT_DIR report os.makedirs(RESULT_DIR, exist_okTrue) def check_login(page): 模拟真实用户登录并校验登录后的关键元素 page.goto(BASE_URL, timeout30000) page.fill(#username, USERNAME) page.fill(#password, PASSWORD) page.click(button[typesubmit]) page.wait_for_selector(text欢迎回来, timeout15000) def run_snapshot(): with sync_playwright() as p: browser p.chromium.launch( headlessTrue, args[--disable-dev-shm-usage] ) page browser.new_page(viewport{width: 1440, height: 900}) result {timestamp: datetime.now().isoformat(), checks: []} try: check_login(page) result[checks].append({name: login, status: pass}) except Exception as exc: result[checks].append({name: login, status: fail, error: str(exc)}) page.screenshot(pathf{RESULT_DIR}/latest.png, full_pageTrue) browser.close() with open(f{RESULT_DIR}/result.json, w, encodingutf-8) as f: json.dump(result, f, ensure_asciiFalse, indent2) has_fail any(item[status] fail for item in result[checks]) print(json.dumps(result, ensure_asciiFalse, indent2)) return 1 if has_fail else 0 if __name__ __main__: exit_code run_snapshot() raise SystemExit(exit_code)这段代码做了几件关键的事。通过os.getenv读取账号密码不把敏感信息写死在代码里。断言失败时把异常记进结果而不是让脚本直接中断这样后面才能生成完整报告。每次运行都用固定文件名result.json和latest.png方便集成站点时使用固定链接。最终退出码非 0 即失败后面 CI 和通知逻辑都要依赖这个退出码。本地执行验证pip install playwright playwright install chromium python run_rpa.py跑通后当前目录会出现一个report/文件夹里面有截图和结果 JSON。这一步就完成了。2.2 第二步固定依赖、封装入口让脚本能在陌生环境跑起来本地能跑不算本事关键是在陌生容器里也能跑。Netlify 构建环境是一个全新的 Ubuntu 容器它预装了 Python但你需要的 Playwright 库和 Chromium 系统依赖不会默认存在。所以第二步就是把这些安装逻辑固化下来。我习惯把安装命令写成一个setup.sh然后在构建配置里先执行它再执行run_rpa.py。#!/usr/bin/env bash set -e pip install -r requirements.txt python -m playwright install chromium python -m playwright install-deps chromium注意这里的python -m playwright install-deps chromium它的作用是安装 Chromium 在 Ubuntu 上运行所需的系统库比如各类libnss3、libatk等。这一步在本地开发机上通常不需要因为本地桌面环境已经有了这些库但在干净的云构建容器里缺了它浏览器一启动就会报错。我见过很多人卡在这个位置其实加上这一行就解决了。requirements.txt也要严格控制版本不要用pip install playwright这种隐式安装。写死版本的好处是后面排查问题时有据可查。playwright1.40.0 requests2.31.0在仓库根目录放一个.env.example写清楚脚本运行需要哪些环境变量。RPA_BASE_URLhttps://example.com/login RPA_USERNAMEyour_account RPA_PASSWORDyour_password RPA_WEBHOOK_URLhttps://your-webhook-url同时必须把.env和report/加进.gitignore避免本地测试结果被误提交进仓库。这一步看起来琐碎但它是整个方案能否稳定迁移到线上的基础。很多自动化项目“本地跑得好好的上云就挂”根子往往就在依赖和配置的隐性假设没有被显式化。2.3 第三步接入 Netlify把报告目录变成站点脚本和依赖都准备好之后就可以在 Netlify 上创建一个新站点并把它连到代码仓库。设置里最关键的一项就是构建命令和发布目录。我最终用的netlify.toml配置长这样[build] command bash setup.sh python run_rpa.py publish report [build.environment] PYTHON_VERSION 3.11command里先跑环境准备脚本再跑测试脚本。两条命令用连接前者失败就不会继续跑后者。publish指向report目录这意味着/latest.png、/result.json会自动变成网站的静态资源。PYTHON_VERSION显式指定 Python 版本避免容器默认版本和你本地开发版本不一致。第一次构建很可能会遇到一些奇怪的问题不用慌。常见的是安装 Chromium 时网络比较慢构建超时解决办法是在 Netlify 的构建设置里延长构建超时时间或者把setup.sh的输出写到日志里观察具体卡在哪一步。第一次构建通过后你会得到一个形如https://your-site.netlify.app的地址以后每次构建的结果都会自动发布到这个地址下。有一个细节值得多说一句python run_rpa.py的退出码如果非 0Netlify 会认为“构建失败”然后站点不会更新。这个行为一开始让我困扰但后来反而成了好事。因为我可以在脚本里控制退出码只有全部检查项通过才返回 0有任何页面异常就返回 1。这样构建结果本身就成了一个“测试是否通过”的信号灯构建日志里还能直接看到失败原因。如果你希望“失败了也把报告发布出来”那就要在脚本外层用|| true之类的写法包一下但我个人不建议这么做因为失败时保留旧版本站点反而能对上一次正常状态形成对照。2.4 第四步用 Build Hook 加定时器让测试自动跑到这一步站点已经能在 push 代码时自动构建、自动跑测试、自动发布报告。但这还只是“持续集成”不是“定时巡检”。要让脚本每天早上自动跑一次得靠 Build Hook 配合一个外部定时调度器。在 Netlify 的站点管理后台找到 Build Hooks构建钩子入口创建一个新 Hook。创建后会得到一个形如https://api.netlify.com/build_hooks/xxxxx的专属地址。任何时候向这个地址发一个 POST 请求Netlify 就会触发一次新的构建。外部调度器我选的是代码仓库自带的 Actions 定时任务因为仓库已经存在不用引入额外服务。下面是daily-rpa.yml的示例。name: daily-rpa on: schedule: - cron: 0 22 * * * workflow_dispatch: jobs: trigger: runs-on: ubuntu-latest steps: - name: Trigger Netlify Build run: | curl -X POST \ -H Content-Type: application/json \ -d {} \ ${{ secrets.NETLIFY_HOOK_URL }}schedule使用 UTC 时区的 cron 表达式。如果你希望北京时间每天早上 8 点跑那对应 UTC 时间就是前一天晚上 22 点所以0 22 * * *表示每天 22:00 UTC。workflow_dispatch允许你手动触发一次方便调试。Hook URL 不要直接写进配置文件应该放到 Actions 的Secrets配置里以NETLIFY_HOOK_URL命名。定时任务触发后Netlify 会立刻开始构建脚本在云端容器里跑一次完整巡检然后把结果发布成最新的latest.png和result.json。这一套下来不需要任何人盯着每天都会有新鲜出炉的测试报告挂到公网上。2.5 第五步加通知、留历史真正“无人值守”定时执行只是让测试自动跑了但如果测试失败你还是要第一时间知道。这就是第五步的逻辑失败通知和结果留档。在run_rpa.py的代码里我加了一个简单的通知函数。脚本跑完以后如果有失败的检查项就调用 Webhook 地址把结果推送出来。import requests def send_notification(result, report_url): webhook os.getenv(RPA_WEBHOOK_URL) if not webhook: return summary result.get(summary, {}) text ( fRPA巡检结果\n f时间{result[timestamp]}\n f通过{summary.get(pass, 0)}失败{summary.get(fail, 0)}\n f详情{report_url} ) requests.post(webhook, json{text: text}, timeout10)Webhook 的具体格式取决于你的消息接收端。我用过办公协作软件的群机器人也用过邮件发送本质上都是往一个固定地址 POST 一段 JSON。如果你没有现成的 Webhook用 SMTP 发邮件也是可行的只是要多配几个邮件服务器相关的环境变量。注意这个通知函数里requests.post必须放在哨兵异常的try外面或者也做异常保护避免“通知程序自己挂掉导致主程序退出码异常”这种哭笑不得的问题。结果留档方面我建议在站点首页放一个简短的说明页把latest.png和result.json作为固定链接嵌进去。这样你打开网站一眼就能看到“今天这个系统的登录页面快照长什么样检查项是否全部通过”。如果你还想保留多天历史可以把脚本里写入 JSON 的路径改成带日期的文件名例如result-2024-06-01.json然后在report目录下自动生成一个index.html作为索引页。Netlify 每次构建会把整个report目录发布出去历史文件只要提交进构建产物就会一直保留在网站上。到这里“本地脚本 云构建 定时触发 失败通知 网页展示”的完整闭环就形成了。后面我就不需要每天都打开电脑跑一遍脚本只需要在通知里收到异常时打开最新报告看一眼快照和检查详情就能判断问题出在哪里。3. 关键配置与避坑细节3.1 环境变量管理账号密码千万别写进仓库这个坑我踩过一次说出来都觉得自己蠢。第一次搭站点的时候图省事直接把测试账号的密码写在run_rpa.py里提交到了仓库。虽然那个仓库是私有的但任何不小心拿到仓库权限的人都能看到测试系统的密码。而且一旦仓库以后转公开账号信息等于裸奔。正确的做法是脚本里只允许通过os.getenv读取环境变量然后分别在 Netlify 后台和 GitHub Actions Secrets 里配置对应的变量。Netlify 站点后台有 Environment Variables环境变量设置项你可以在里面添加RPA_USERNAME、RPA_PASSWORD、RPA_BASE_URL等变量构建时这些变量会自动注入到容器的环境里。本地开发时则在.env文件里维护一份同样的变量配合python-dotenv之类的工具读取。有一点容易漏Netlify 构建环境在拉取仓库代码时netlify.toml里的环境变量优先级低于后台配置的变量。如果你在两个地方都配置了同一个变量以后台的为准。调试时如果发现脚本读到的值和预期不符优先检查后台变量是否覆盖了配置文件里的默认值。3.2 无头浏览器的系统依赖和资源限制Playwright 的playwright install-deps chromium能解决大部分系统库缺失问题但这不代表万事大吉。Netlify 的构建容器是共享环境Chromium 跑起来的内存占用通常不小如果你的页面比较重可能会出现浏览器崩溃或者截图空白的情况。我在脚本里挂了两个参数来缓解--disable-dev-shm-usage和--no-sandbox。第一个是让 Chromium 不用共享内存目录容器里 /dev/shm 往往很小第二个是避免无头模式下沙箱权限问题。另外脚本内部一定要设置合理的超时时间。我在goto、wait_for_selector上都给了 15 到 30 秒的上限防止某个页面元素迟迟不出现整个构建任务被拖到 Netlify 的超时阈值。总构建时长建议控制在 10 分钟以内留有余量毕竟云构建排队和网络请求本身也有额外耗时。还有一点值得注意Playwright 的浏览器执行文件体积不小第一次构建时安装可能要占掉不少时间和带宽。如果你频繁触发构建这部分成本是重复计算的。我后来用了一个技巧把setup.sh里的安装步骤拆分基础依赖每次都装很快浏览器二进制则尽量利用构建缓存。Netlify 支持依赖缓存配置把~/.cache/ms-playwright加到缓存目录列表里第二次构建就会明显加快。3.3 报告链接与文件策略我在脚本里固定使用latest.png和result.json作为文件名这样每次构建都会覆盖旧文件网站上的固定链接一直是“最新状态”。这种策略的好处是链接稳定你把https://your-site.netlify.app/latest.png发到群里任何时候点开都是最近一次执行结果。但如果你有“保留历史、方便对比”的需求固定文件名就不够用了。我的做法是折中生成带时间戳的历史快照同时保留latest系列文件。timestamp datetime.now().strftime(%Y%m%d-%H%M%S) page.screenshot(pathf{RESULT_DIR}/snapshot-{timestamp}.png, full_pageTrue) page.screenshot(pathf{RESULT_DIR}/latest.png, full_pageTrue)这样latest.png永远指向最新而历史文件存放在站点目录中通过一个index.html列出所有快照。注意 Netlify 并不会自动帮你清理构建产物历史文件越多站点体积越大时间长了会拖慢访问速度。我一般会在脚本里加一个清理逻辑只保留最近 30 天的快照文件。4. 常见问题与排查记录整套方案我跑了几个月遇到过一些典型问题。整理成表格方便你直接对照。现象可能原因解决方式构建日志显示找不到 Chromium漏了playwright install chromium在setup.sh中显式安装浏览器二进制浏览器启动时提示缺少系统库容器缺少运行 Chromium 的动态依赖补上python -m playwright install-deps chromium脚本提示读取不到环境变量变量只配置在netlify.toml但后台有同名变量覆盖检查后台变量保持单一处维护定时任务到点没触发cron 时区与预期不一致确认仓库定时任务默认 UTC 时区换算北京时间构建 Hook 请求返回 404Hook URL 被删除或站点重置重新创建 Hook并更新到 Secrets每次构建都很慢浏览器二进制未缓存配置 Netlify 缓存~/.cache/ms-playwrightGit push 后报告是旧的脚本返回非 0Netlify 视为构建失败未发布查看构建日志定位脚本断言失败的具体项登录环节不稳定偶发失败页面响应慢或验证码干扰增加等待与重试机制失败时保存页面快照留证挑几个详细说说。第一个是定时任务时区的问题。GitHub Actions 的schedule默认按 UTC 时间执行很多人配置0 8 * * *以为早上 8 点跑实际跑的时候国内时间已经是下午 4 点。我的调整办法很简单先把想要的北京时间换算成 UTC 时间写进 cron再用workflow_dispatch手动触发验证一次确认确实能在预期时间附近执行。第二个是“登录环节不稳定”。这个是 RPA 类项目最常见的烦恼。我的页面登录有验证码验证码一出现脚本就无能为力。后来我在被测系统允许的前提下把登录环节进行了测试专用通道改造绕过了验证码如果被测系统不支持这种处理我给脚本加了重试逻辑第一次失败后等 10 秒重试一次同时把失败页面截图保存下来方便人工复核。注意你的被测系统如果对验证码有强校验脚本的稳定性会大打折扣要么调整业务侧策略要么在报告里明确标注“需要人工介入”。第三个是关于 Netlify 构建失败不回滚的问题。默认情况下构建失败后站点会回滚到上一个成功构建其实这个行为是合理的。但如果你误以为“构建失败等于脚本没跑”那就看错了。日志里脚本的输出还在只是退出码非 0 导致发布步骤中止。我排查这类问题时第一步永远是打开 Netlify 构建日志搜索result或error关键字先把脚本的运行结果找出来再判断是页面异常还是环境问题。5. 我的体会与下一步可以怎么玩整套方案稳定运作了小半年我的核心体会是RPA-Python 加 Netlify 这件事真正的价值不是“自动化跑脚本”而是“把脚本变成了一个谁都能访问的线上服务”。过去测试报告是工程师自己看现在它变成站点上的一张快照产品、运营都能打开同一个链接确认系统状态这种信息透明带来的协作效率提升比我预期的大得多。期间也有一个意外收获因为报告地址固定我还把/result.json接入了团队内部的数据面板每天可以自动统计巡检通过率。后期如果想进一步扩展我觉得有几个方向很值得尝试。一是多站点并行巡检把不同的被测环境拆成多个 Netlify 站点或者在一个脚本里按环境循环执行并在报告里加一个环境维度二是引入视觉回归对比相邻两天的页面截图像素级差异直接标红这样不仅能发现功能性问题也能捕捉 UI 样式漂移三是把结果写入简单的云数据库做历史趋势分析和巡检通过率报表让这套小系统慢慢长成一个有数据积累的测试看板。最后再分享一个实用技巧。如果你不想依赖 GitHub Actions 做定时调度也可以考虑 Netlify 官方支持的一些外部 cron 服务或者干脆用一个轻量的边缘函数定时发请求。但无论用哪种触发方式都建议保留一个“手动触发入口”我的做法是保留workflow_dispatch和一个单独用来触发构建的命令行脚本这样任何时候想立即跑一次巡检点按钮或敲一行命令就行了。整个链路不复杂但它把“定时、自动、展示、通知”四件事一次性打通了对个人项目和中小团队来说性价比非常高。