这次我们来看一个很有意思的项目——一个“不是用来上网而是用来替你干活”的浏览器。这听起来有点反直觉浏览器不就是用来访问网页的吗但它的核心思路是把浏览器从一个“内容消费工具”变成了一个“自动化执行工具”。简单来说你可以把它理解为一个高度可编程的、能模拟人类操作网页的自动化机器人平台。它最核心的价值在于能够通过脚本或配置自动完成一系列原本需要手动在浏览器中重复的操作。比如自动填写表单、批量下载文件、监控网页内容变化、定时执行网页任务、甚至进行复杂的网页数据抓取与处理。对于需要处理大量网页交互、数据采集或流程自动化的开发者、运营和数据分析师来说这能极大提升效率。本文将带你深入了解这类“自动化浏览器”的核心能力、典型应用场景并重点演示如何基于一个流行的开源框架——Playwright——来搭建你自己的“干活浏览器”。我们会从环境准备、脚本编写、到实际运行和问题排查一步步拆解让你看完就能动手实践。1. 核心能力速览在深入代码之前我们先通过一个表格快速了解这类自动化浏览器的核心规格和它能做什么。这有助于你判断它是否适合解决你手头的问题。能力项说明与典型实现项目类型浏览器自动化框架/无头浏览器控制工具主要功能模拟用户操作点击、输入、滚动、获取页面内容、执行JavaScript、处理弹窗、文件上传/下载、网络请求拦截与模拟等。核心开源方案Playwright (微软)、Puppeteer (谷歌)、Selenium。本文以 Playwright 为例因其跨浏览器支持好、API 现代。运行模式支持有头可见浏览器窗口和无头后台运行模式方便调试和部署。编程语言支持通常支持 Node.js、Python、Java、.NET 等。Python 因易上手而广泛使用。硬件门槛极低。主要依赖 CPU 和内存无需独立显卡。普通电脑即可运行。是否支持批量任务是。核心优势之一可通过脚本轻松实现批量打开网页、处理数据。是否支持定时/计划任务是。可结合系统定时任务如 crontab, Windows 任务计划程序或框架内调度器实现。是否提供 API 服务是。可通过封装为 HTTP API 服务如使用 FastAPI、Flask供其他系统调用。适合场景网页数据采集需遵守 robots.txt 及网站条款、自动化测试、RPA机器人流程自动化、监控报警、内容聚合、定期报表生成等。2. 适用场景与使用边界2.1 它适合谁能解决什么问题开发者与测试工程师用于 Web 应用的端到端E2E自动化测试确保功能稳定。数据分析师与研究员需要从多个网站定时、批量抓取公开数据如股价、天气、新闻进行分析。运营与市场人员自动执行重复性网页操作如批量发布内容、监控竞品信息、收集用户反馈。个人效率追求者自动化每日需要手动访问的网页任务如自动签到、抢购监控需谨慎合规、信息聚合。2.2 不适合什么场景绕过付费墙或登录验证用于获取未授权访问的内容是违法且不道德的。对反爬虫机制严格的网站进行高频访问这可能对目标网站造成压力并可能导致你的 IP 被封禁。务必遵守robots.txt协议并设置合理的请求间隔。完全替代人工进行需要高度主观判断的操作自动化工具擅长规则明确的重复任务不擅长创造性或模糊决策。2.3 法律与合规边界这是最重要的部分必须严格遵守尊重版权与条款仅抓取公开可用、且网站服务条款允许的数据。禁止抓取受版权保护或明确禁止爬取的内容。遵守 robots.txt在访问任何网站前检查其robots.txt文件通常位于https://网站域名/robots.txt并遵守其中的禁止爬取规则。控制访问频率在脚本中增加随机延迟如time.sleep(random.uniform(1, 3))避免对目标服务器造成拒绝服务攻击DoS。个人信息保护如果处理到个人信息必须确保有合法依据并采取严格的数据安全措施。明确用途将自动化工具用于学习、测试、效率提升及合法合规的数据收集。3. 环境准备与前置条件我们将以Playwright for Python作为实战框架。它的优势是 API 清晰支持 Chromium、Firefox 和 WebKit 三大浏览器引擎。3.1 基础环境清单在开始之前请确保你的系统满足以下条件操作系统Windows 10/11, macOS, 或 Linux (如 Ubuntu) 均可。Python 版本建议使用 Python 3.8 及以上版本。你可以通过命令行检查python --version # 或 python3 --version包管理工具pip需要是最新版本。pip install --upgrade pip网络连接需要能正常访问互联网以下载 Playwright 和浏览器二进制文件。磁盘空间预留约 1 GB 空间用于安装浏览器。3.2 安装 Playwright打开你的终端Windows 下可用 PowerShell 或 CMD执行以下命令安装 Playwright 的 Python 包pip install playwright安装完成后需要安装 Playwright 所需的浏览器内核。这一步可能会花费一些时间因为它会下载 Chromium、Firefox 和 WebKit。playwright install如果你想只安装特定的浏览器以节省时间和空间可以指定playwright install chromium # 只安装 Chromium最常用 # playwright install firefox # playwright install webkit4. 编写你的第一个“干活”脚本环境就绪我们来写一个最简单的脚本感受一下浏览器如何被“编程”。这个脚本将打开百度首页搜索一个关键词并截图保存结果。创建一个新文件命名为first_worker.py。import asyncio from playwright.async_api import async_playwright import time async def main(): # 启动 Playwright管理浏览器上下文 async with async_playwright() as p: # 启动一个 Chromium 浏览器实例headlessFalse 表示有界面方便调试 browser await p.chromium.launch(headlessFalse, slow_mo1000) # slow_mo 让操作变慢便于观察 # 创建一个新的浏览器页面标签页 page await browser.new_page() print(正在导航到百度...) # 访问百度 await page.goto(https://www.baidu.com) # 等待搜索输入框出现并填充内容 search_box page.locator(input#kw) await search_box.fill(Playwright 自动化) print(已输入搜索关键词。) # 点击“百度一下”按钮 search_button page.locator(input#su) await search_button.click() print(已点击搜索按钮。) # 等待搜索结果页面加载通过等待某个结果元素出现 await page.wait_for_selector(div.result.c-container, timeout10000) # 对搜索结果页面进行截图 await page.screenshot(path./baidu_search_result.png, full_pageTrue) print(f截图已保存至: ./baidu_search_result.png) # 为了演示等待3秒让你看到结果 await asyncio.sleep(3) # 关闭浏览器 await browser.close() # 运行异步主函数 asyncio.run(main())脚本解读与运行导入模块async_playwright是异步 API 的入口。启动浏览器p.chromium.launch(headlessFalse)会打开一个你能看到的 Chrome 窗口。在生产环境或服务器上通常设置为headlessTrue无头模式。页面导航与交互page.goto()用于跳转page.locator()是 Playwright 强大的选择器用于定位页面元素这里用了 CSS 选择器input#kwfill()和click()模拟输入和点击。等待与截图wait_for_selector()确保目标元素加载完成后再继续避免脚本因页面加载慢而失败。screenshot()保存页面快照。运行脚本在终端中进入脚本所在目录执行python first_worker.py你会看到一个浏览器窗口自动打开完成搜索并截图然后关闭。5. 功能测试与效果验证模拟真实工作流一个简单的搜索截图只是开始。下面我们测试几个更贴近“干活”场景的复杂功能。5.1 测试一处理登录与表单提交很多自动化任务需要先登录。以下脚本模拟登录一个假设的网站请替换为你的测试目标。import asyncio from playwright.async_api import async_playwright async def handle_login(): async with async_playwright() as p: browser await p.chromium.launch(headlessFalse) page await browser.new_page() # 1. 导航到登录页 await page.goto(https://example.com/login) print(已到达登录页面。) # 2. 定位并填写用户名、密码 # 假设输入框的 id 分别是 ‘username’ 和 ‘password’ await page.fill(#username, your_username) await page.fill(#password, your_password) print(凭据已填写。) # 3. 点击登录按钮假设选择器是 ‘button[type“submit”]’ await page.click(button[typesubmit]) # 4. 验证登录成功等待跳转后页面出现特定元素如用户头像 try: await page.wait_for_selector(.user-avatar, timeout5000) print(登录成功) # 登录后可以继续其他操作... # 例如导航到用户仪表盘 # await page.goto(https://example.com/dashboard) except Exception as e: print(f登录可能失败未找到成功标识。错误: {e}) # 可以在这里截图用于调试 await page.screenshot(path./login_failed.png) await asyncio.sleep(2) await browser.close() asyncio.run(handle_login())验证点脚本能否成功导航到登录页、填充信息、点击提交并最终检测到代表登录成功的页面元素如.user-avatar。如果失败截图有助于分析是选择器问题还是网站反爬机制。5.2 测试二批量任务与数据提取假设你需要从某个列表页批量获取文章标题和链接。import asyncio from playwright.async_api import async_playwright import csv async def batch_scrape_articles(): async with async_playwright() as p: browser await p.chromium.launch(headlessTrue) # 无头模式后台运行 page await browser.new_page() await page.goto(https://example-news-site.com/list) print(已加载列表页。) # 等待文章列表容器加载 await page.wait_for_selector(.article-list) # 使用 page.locator().all() 获取所有匹配的元素句柄 article_elements await page.locator(.article-list .item).all() articles_data [] for i, element in enumerate(article_elements): # 在每个元素句柄的上下文中提取信息 title await element.locator(h2 a).text_content() link await element.locator(h2 a).get_attribute(href) # 确保链接是绝对路径 if link and not link.startswith(http): link fhttps://example-news-site.com{link} if title and link: articles_data.append({title: title.strip(), link: link}) print(f提取到: {title[:50]}...) # 保存到 CSV 文件 if articles_data: keys articles_data[0].keys() with open(articles.csv, w, newline, encodingutf-8-sig) as f: dict_writer csv.DictWriter(f, fieldnameskeys) dict_writer.writeheader() dict_writer.writerows(articles_data) print(f数据已保存至 articles.csv共 {len(articles_data)} 条。) else: print(未提取到任何数据。) await browser.close() asyncio.run(batch_scrape_articles())验证点脚本能否在无头模式下稳定运行准确提取所有目标元素的信息并正确保存为结构化的 CSV 文件。检查输出文件的内容和格式是否正确。5.3 测试三文件下载与管理自动化下载是常见需求。Playwright 可以设置下载路径并监听下载事件。import asyncio from playwright.async_api import async_playwright import os async def handle_download(): async with async_playwright() as p: # 启动浏览器并设置下载保存路径 browser await p.chromium.launch(headlessFalse, downloads_path./downloads) page await browser.new_page() # 监听下载事件 async def on_download(download): print(f开始下载: {download.suggested_filename}) # 等待下载完成文件会自动保存到启动时设置的 downloads_path save_path await download.path() print(f下载完成保存至: {save_path}) page.on(download, on_download) # 导航到一个有文件下载链接的页面示例 await page.goto(https://example.com/download-page) # 假设下载链接是一个带有 ‘.pdf’ 的链接 download_link page.locator(a[href$.pdf]).first if await download_link.count() 0: # 触发下载注意有些网站可能需要先处理一些交互 async with page.expect_download() as download_info: await download_link.click() download await download_info.value print(f成功触发下载: {download.url}) else: print(未找到符合条件的下载链接。) await asyncio.sleep(5) # 留出下载时间 await browser.close() # 确保下载目录存在 os.makedirs(./downloads, exist_okTrue) asyncio.run(handle_download())验证点脚本能否正确监听并处理下载事件文件是否成功保存到指定的./downloads目录下。6. 封装为 API 服务与计划任务让“干活浏览器”7x24小时待命有两种常见方式封装成 API 供其他系统调用或者设置为定时任务。6.1 封装为 FastAPI 服务我们可以将上面的数据抓取功能包装成一个 HTTP API这样任何能发送 HTTP 请求的程序都能调用它。创建一个新文件api_server.pyfrom fastapi import FastAPI, BackgroundTasks from pydantic import BaseModel import asyncio import uuid from playwright.async_api import async_playwright import json import os app FastAPI(title自动化浏览器 Worker API) # 存储任务状态和结果的内存字典生产环境应使用数据库 tasks {} class ScrapeTask(BaseModel): url: str selector: str app.post(/api/scrape) async def create_scrape_task(task: ScrapeTask, background_tasks: BackgroundTasks): 创建一个新的网页抓取任务 task_id str(uuid.uuid4()) tasks[task_id] {status: pending, result: None, error: None} # 将实际任务加入后台执行 background_tasks.add_task(run_scrape_task, task_id, task.url, task.selector) return {task_id: task_id, status: accepted} app.get(/api/task/{task_id}) async def get_task_status(task_id: str): 查询任务状态和结果 task_info tasks.get(task_id) if not task_info: return {error: Task not found} return task_info async def run_scrape_task(task_id: str, url: str, selector: str): 后台执行 Playwright 抓取任务 try: async with async_playwright() as p: browser await p.chromium.launch(headlessTrue) page await browser.new_page() await page.goto(url) # 简单示例获取指定选择器的文本内容 elements await page.locator(selector).all() results [] for elem in elements: text await elem.text_content() if text: results.append(text.strip()) await browser.close() tasks[task_id][status] completed tasks[task_id][result] results except Exception as e: tasks[task_id][status] failed tasks[task_id][error] str(e) if __name__ __main__: import uvicorn uvicorn.run(app, host0.0.0.0, port8000)运行与调用安装 FastAPI 和 Uvicorn:pip install fastapi uvicorn启动服务:python api_server.py调用 API:# 创建任务 curl -X POST http://127.0.0.1:8000/api/scrape \ -H Content-Type: application/json \ -d {url: https://news.ycombinator.com, selector: .titleline a} # 返回示例: {task_id:xxxx-xxxx, status:accepted} # 查询任务结果使用返回的 task_id curl http://127.0.0.1:8000/api/task/xxxx-xxxx6.2 设置为系统定时任务Cron Job对于需要每天/每周固定时间运行的任务可以结合操作系统的计划任务。创建一个独立的脚本daily_report.py它包含完整的抓取和报告逻辑。# daily_report.py import asyncio from playwright.async_api import async_playwright from datetime import datetime import json async def generate_daily_report(): print(f[{datetime.now()}] 开始执行每日报告任务...) async with async_playwright() as p: browser await p.chromium.launch(headlessTrue) page await browser.new_page() # ... 你的抓取和分析逻辑 ... # 例如抓取某个网站的关键指标 await page.goto(https://example.com/metrics) data await page.text_content(.metric-value) report { date: datetime.now().isoformat(), metric: data.strip() if data else N/A } # 保存报告 with open(f./reports/report_{datetime.now().date()}.json, w) as f: json.dump(report, f, indent2) print(f[{datetime.now()}] 报告生成完成。) await browser.close() if __name__ __main__: asyncio.run(generate_daily_report())然后在 Linux/macOS 上使用crontab -e添加一行设定每天上午9点执行0 9 * * * cd /path/to/your/script /usr/bin/python3 /path/to/your/script/daily_report.py /path/to/your/script/cron.log 21在 Windows 上可以使用“任务计划程序”来配置。7. 资源占用与性能观察Playwright 启动的浏览器实例会消耗内存和 CPU 资源这是需要关注的重点。内存占用一个典型的无头 Chromium 进程大约占用 100-300 MB 内存具体取决于页面复杂度。同时运行多个页面或浏览器实例时内存占用会线性增加。CPU 使用率在页面加载、渲染和执行 JavaScript 时会有 CPU 峰值。在空闲或无头模式下CPU 占用很低。性能优化建议使用无头模式headlessTrue这是生产环境的标配节省资源且更稳定。复用浏览器上下文避免为每个小任务都启动/关闭浏览器。可以启动一个浏览器实例然后创建多个独立的页面browser.new_page()来处理不同任务。合理设置超时与等待使用page.wait_for_selector、page.wait_for_function等明确等待比固定的time.sleep更高效。限制并发如果需要处理大量 URL使用信号量asyncio.Semaphore或任务队列来控制同时打开的页面数量防止内存耗尽。及时清理任务完成后确保关闭页面await page.close()和浏览器await browser.close()。监控方法在运行脚本时可以打开系统的任务管理器Windows或htopLinux/macOS来观察chromium或chrome进程的资源消耗情况。8. 常见问题与排查方法在编写和运行自动化脚本时你肯定会遇到各种问题。下表列出了一些典型问题及解决思路。问题现象可能原因排查方式解决方案脚本启动后浏览器闪退或无法启动1. 浏览器驱动未正确安装。2. 系统缺少依赖库多见于Linux。3. 端口冲突或已有浏览器进程残留。1. 运行playwright install --dry-run检查。2. 查看 Playwright 安装日志。3. 检查系统进程。1. 重新运行playwright install。2. 根据 Playwright 官方文档安装系统依赖。3. 结束残留的chromium进程。元素定位失败TimeoutError1. 选择器写错了或页面结构已变。2. 页面未完全加载或元素在 iframe 内。3. 网站有反爬机制如检测到自动化工具。1. 使用浏览器开发者工具F12验证选择器。2. 增加等待时间或使用page.wait_for_load_state(‘networkidle’)。3. 尝试有头模式(headlessFalse)看页面是否正常。1. 更新选择器使用更稳定的属性如>页面加载非常慢或卡住1. 网络问题或目标网站响应慢。2. 页面有大量异步加载资源如无限滚动。3. 脚本等待策略不佳。1. 检查网络增加page.goto(timeout60000)。2. 观察网络面板看是否有请求阻塞。3. 检查wait_for_*函数的使用。1. 设置合理的超时时间。2. 使用page.wait_for_load_state(‘domcontentloaded’)而非‘networkidle’。3. 对特定操作进行等待而非全局等待。在无头模式下运行结果与有头模式不同1. 某些网站针对无头浏览器进行了检测和屏蔽。2. 视口viewport大小不同影响页面布局。1. 对比有头和无头模式下的页面截图或HTML。2. 检查浏览器启动参数。1. 尝试使用playwright.chromium.launch(headlessTrue, args[‘--disable-blink-featuresAutomationControlled’])。2. 设置与有头模式相同的视口大小await page.set_viewport_size({‘width’: 1920, ‘height’: 1080})。文件下载不成功1. 下载路径未设置或没有写入权限。2. 下载被浏览器拦截或需要额外交互。1. 检查downloads_path参数和目录权限。2. 在有头模式下观察下载过程。1. 确保downloads_path是有效路径且脚本有权限写入。2. 监听download事件并使用download.save_as()方法指定路径。内存使用持续增长内存泄漏1. 页面、上下文或浏览器实例未正确关闭。2. 在循环中不断创建新页面而未关闭旧页面。1. 使用async with语句确保资源释放。2. 监控内存使用趋势。1. 确保每个page和browser在 finally 块或async with中被关闭。2. 考虑定期重启浏览器实例。9. 最佳实践与使用建议要让你的“干活浏览器”稳定、高效、可维护请遵循以下建议从简单开始逐步复杂化先在一个简单的静态页面上测试你的选择器和操作逻辑确保基础流程跑通再应用到目标网站。使用稳定的选择器优先选择id、>
基于Playwright的浏览器自动化实战:从网页操作到API服务封装
这次我们来看一个很有意思的项目——一个“不是用来上网而是用来替你干活”的浏览器。这听起来有点反直觉浏览器不就是用来访问网页的吗但它的核心思路是把浏览器从一个“内容消费工具”变成了一个“自动化执行工具”。简单来说你可以把它理解为一个高度可编程的、能模拟人类操作网页的自动化机器人平台。它最核心的价值在于能够通过脚本或配置自动完成一系列原本需要手动在浏览器中重复的操作。比如自动填写表单、批量下载文件、监控网页内容变化、定时执行网页任务、甚至进行复杂的网页数据抓取与处理。对于需要处理大量网页交互、数据采集或流程自动化的开发者、运营和数据分析师来说这能极大提升效率。本文将带你深入了解这类“自动化浏览器”的核心能力、典型应用场景并重点演示如何基于一个流行的开源框架——Playwright——来搭建你自己的“干活浏览器”。我们会从环境准备、脚本编写、到实际运行和问题排查一步步拆解让你看完就能动手实践。1. 核心能力速览在深入代码之前我们先通过一个表格快速了解这类自动化浏览器的核心规格和它能做什么。这有助于你判断它是否适合解决你手头的问题。能力项说明与典型实现项目类型浏览器自动化框架/无头浏览器控制工具主要功能模拟用户操作点击、输入、滚动、获取页面内容、执行JavaScript、处理弹窗、文件上传/下载、网络请求拦截与模拟等。核心开源方案Playwright (微软)、Puppeteer (谷歌)、Selenium。本文以 Playwright 为例因其跨浏览器支持好、API 现代。运行模式支持有头可见浏览器窗口和无头后台运行模式方便调试和部署。编程语言支持通常支持 Node.js、Python、Java、.NET 等。Python 因易上手而广泛使用。硬件门槛极低。主要依赖 CPU 和内存无需独立显卡。普通电脑即可运行。是否支持批量任务是。核心优势之一可通过脚本轻松实现批量打开网页、处理数据。是否支持定时/计划任务是。可结合系统定时任务如 crontab, Windows 任务计划程序或框架内调度器实现。是否提供 API 服务是。可通过封装为 HTTP API 服务如使用 FastAPI、Flask供其他系统调用。适合场景网页数据采集需遵守 robots.txt 及网站条款、自动化测试、RPA机器人流程自动化、监控报警、内容聚合、定期报表生成等。2. 适用场景与使用边界2.1 它适合谁能解决什么问题开发者与测试工程师用于 Web 应用的端到端E2E自动化测试确保功能稳定。数据分析师与研究员需要从多个网站定时、批量抓取公开数据如股价、天气、新闻进行分析。运营与市场人员自动执行重复性网页操作如批量发布内容、监控竞品信息、收集用户反馈。个人效率追求者自动化每日需要手动访问的网页任务如自动签到、抢购监控需谨慎合规、信息聚合。2.2 不适合什么场景绕过付费墙或登录验证用于获取未授权访问的内容是违法且不道德的。对反爬虫机制严格的网站进行高频访问这可能对目标网站造成压力并可能导致你的 IP 被封禁。务必遵守robots.txt协议并设置合理的请求间隔。完全替代人工进行需要高度主观判断的操作自动化工具擅长规则明确的重复任务不擅长创造性或模糊决策。2.3 法律与合规边界这是最重要的部分必须严格遵守尊重版权与条款仅抓取公开可用、且网站服务条款允许的数据。禁止抓取受版权保护或明确禁止爬取的内容。遵守 robots.txt在访问任何网站前检查其robots.txt文件通常位于https://网站域名/robots.txt并遵守其中的禁止爬取规则。控制访问频率在脚本中增加随机延迟如time.sleep(random.uniform(1, 3))避免对目标服务器造成拒绝服务攻击DoS。个人信息保护如果处理到个人信息必须确保有合法依据并采取严格的数据安全措施。明确用途将自动化工具用于学习、测试、效率提升及合法合规的数据收集。3. 环境准备与前置条件我们将以Playwright for Python作为实战框架。它的优势是 API 清晰支持 Chromium、Firefox 和 WebKit 三大浏览器引擎。3.1 基础环境清单在开始之前请确保你的系统满足以下条件操作系统Windows 10/11, macOS, 或 Linux (如 Ubuntu) 均可。Python 版本建议使用 Python 3.8 及以上版本。你可以通过命令行检查python --version # 或 python3 --version包管理工具pip需要是最新版本。pip install --upgrade pip网络连接需要能正常访问互联网以下载 Playwright 和浏览器二进制文件。磁盘空间预留约 1 GB 空间用于安装浏览器。3.2 安装 Playwright打开你的终端Windows 下可用 PowerShell 或 CMD执行以下命令安装 Playwright 的 Python 包pip install playwright安装完成后需要安装 Playwright 所需的浏览器内核。这一步可能会花费一些时间因为它会下载 Chromium、Firefox 和 WebKit。playwright install如果你想只安装特定的浏览器以节省时间和空间可以指定playwright install chromium # 只安装 Chromium最常用 # playwright install firefox # playwright install webkit4. 编写你的第一个“干活”脚本环境就绪我们来写一个最简单的脚本感受一下浏览器如何被“编程”。这个脚本将打开百度首页搜索一个关键词并截图保存结果。创建一个新文件命名为first_worker.py。import asyncio from playwright.async_api import async_playwright import time async def main(): # 启动 Playwright管理浏览器上下文 async with async_playwright() as p: # 启动一个 Chromium 浏览器实例headlessFalse 表示有界面方便调试 browser await p.chromium.launch(headlessFalse, slow_mo1000) # slow_mo 让操作变慢便于观察 # 创建一个新的浏览器页面标签页 page await browser.new_page() print(正在导航到百度...) # 访问百度 await page.goto(https://www.baidu.com) # 等待搜索输入框出现并填充内容 search_box page.locator(input#kw) await search_box.fill(Playwright 自动化) print(已输入搜索关键词。) # 点击“百度一下”按钮 search_button page.locator(input#su) await search_button.click() print(已点击搜索按钮。) # 等待搜索结果页面加载通过等待某个结果元素出现 await page.wait_for_selector(div.result.c-container, timeout10000) # 对搜索结果页面进行截图 await page.screenshot(path./baidu_search_result.png, full_pageTrue) print(f截图已保存至: ./baidu_search_result.png) # 为了演示等待3秒让你看到结果 await asyncio.sleep(3) # 关闭浏览器 await browser.close() # 运行异步主函数 asyncio.run(main())脚本解读与运行导入模块async_playwright是异步 API 的入口。启动浏览器p.chromium.launch(headlessFalse)会打开一个你能看到的 Chrome 窗口。在生产环境或服务器上通常设置为headlessTrue无头模式。页面导航与交互page.goto()用于跳转page.locator()是 Playwright 强大的选择器用于定位页面元素这里用了 CSS 选择器input#kwfill()和click()模拟输入和点击。等待与截图wait_for_selector()确保目标元素加载完成后再继续避免脚本因页面加载慢而失败。screenshot()保存页面快照。运行脚本在终端中进入脚本所在目录执行python first_worker.py你会看到一个浏览器窗口自动打开完成搜索并截图然后关闭。5. 功能测试与效果验证模拟真实工作流一个简单的搜索截图只是开始。下面我们测试几个更贴近“干活”场景的复杂功能。5.1 测试一处理登录与表单提交很多自动化任务需要先登录。以下脚本模拟登录一个假设的网站请替换为你的测试目标。import asyncio from playwright.async_api import async_playwright async def handle_login(): async with async_playwright() as p: browser await p.chromium.launch(headlessFalse) page await browser.new_page() # 1. 导航到登录页 await page.goto(https://example.com/login) print(已到达登录页面。) # 2. 定位并填写用户名、密码 # 假设输入框的 id 分别是 ‘username’ 和 ‘password’ await page.fill(#username, your_username) await page.fill(#password, your_password) print(凭据已填写。) # 3. 点击登录按钮假设选择器是 ‘button[type“submit”]’ await page.click(button[typesubmit]) # 4. 验证登录成功等待跳转后页面出现特定元素如用户头像 try: await page.wait_for_selector(.user-avatar, timeout5000) print(登录成功) # 登录后可以继续其他操作... # 例如导航到用户仪表盘 # await page.goto(https://example.com/dashboard) except Exception as e: print(f登录可能失败未找到成功标识。错误: {e}) # 可以在这里截图用于调试 await page.screenshot(path./login_failed.png) await asyncio.sleep(2) await browser.close() asyncio.run(handle_login())验证点脚本能否成功导航到登录页、填充信息、点击提交并最终检测到代表登录成功的页面元素如.user-avatar。如果失败截图有助于分析是选择器问题还是网站反爬机制。5.2 测试二批量任务与数据提取假设你需要从某个列表页批量获取文章标题和链接。import asyncio from playwright.async_api import async_playwright import csv async def batch_scrape_articles(): async with async_playwright() as p: browser await p.chromium.launch(headlessTrue) # 无头模式后台运行 page await browser.new_page() await page.goto(https://example-news-site.com/list) print(已加载列表页。) # 等待文章列表容器加载 await page.wait_for_selector(.article-list) # 使用 page.locator().all() 获取所有匹配的元素句柄 article_elements await page.locator(.article-list .item).all() articles_data [] for i, element in enumerate(article_elements): # 在每个元素句柄的上下文中提取信息 title await element.locator(h2 a).text_content() link await element.locator(h2 a).get_attribute(href) # 确保链接是绝对路径 if link and not link.startswith(http): link fhttps://example-news-site.com{link} if title and link: articles_data.append({title: title.strip(), link: link}) print(f提取到: {title[:50]}...) # 保存到 CSV 文件 if articles_data: keys articles_data[0].keys() with open(articles.csv, w, newline, encodingutf-8-sig) as f: dict_writer csv.DictWriter(f, fieldnameskeys) dict_writer.writeheader() dict_writer.writerows(articles_data) print(f数据已保存至 articles.csv共 {len(articles_data)} 条。) else: print(未提取到任何数据。) await browser.close() asyncio.run(batch_scrape_articles())验证点脚本能否在无头模式下稳定运行准确提取所有目标元素的信息并正确保存为结构化的 CSV 文件。检查输出文件的内容和格式是否正确。5.3 测试三文件下载与管理自动化下载是常见需求。Playwright 可以设置下载路径并监听下载事件。import asyncio from playwright.async_api import async_playwright import os async def handle_download(): async with async_playwright() as p: # 启动浏览器并设置下载保存路径 browser await p.chromium.launch(headlessFalse, downloads_path./downloads) page await browser.new_page() # 监听下载事件 async def on_download(download): print(f开始下载: {download.suggested_filename}) # 等待下载完成文件会自动保存到启动时设置的 downloads_path save_path await download.path() print(f下载完成保存至: {save_path}) page.on(download, on_download) # 导航到一个有文件下载链接的页面示例 await page.goto(https://example.com/download-page) # 假设下载链接是一个带有 ‘.pdf’ 的链接 download_link page.locator(a[href$.pdf]).first if await download_link.count() 0: # 触发下载注意有些网站可能需要先处理一些交互 async with page.expect_download() as download_info: await download_link.click() download await download_info.value print(f成功触发下载: {download.url}) else: print(未找到符合条件的下载链接。) await asyncio.sleep(5) # 留出下载时间 await browser.close() # 确保下载目录存在 os.makedirs(./downloads, exist_okTrue) asyncio.run(handle_download())验证点脚本能否正确监听并处理下载事件文件是否成功保存到指定的./downloads目录下。6. 封装为 API 服务与计划任务让“干活浏览器”7x24小时待命有两种常见方式封装成 API 供其他系统调用或者设置为定时任务。6.1 封装为 FastAPI 服务我们可以将上面的数据抓取功能包装成一个 HTTP API这样任何能发送 HTTP 请求的程序都能调用它。创建一个新文件api_server.pyfrom fastapi import FastAPI, BackgroundTasks from pydantic import BaseModel import asyncio import uuid from playwright.async_api import async_playwright import json import os app FastAPI(title自动化浏览器 Worker API) # 存储任务状态和结果的内存字典生产环境应使用数据库 tasks {} class ScrapeTask(BaseModel): url: str selector: str app.post(/api/scrape) async def create_scrape_task(task: ScrapeTask, background_tasks: BackgroundTasks): 创建一个新的网页抓取任务 task_id str(uuid.uuid4()) tasks[task_id] {status: pending, result: None, error: None} # 将实际任务加入后台执行 background_tasks.add_task(run_scrape_task, task_id, task.url, task.selector) return {task_id: task_id, status: accepted} app.get(/api/task/{task_id}) async def get_task_status(task_id: str): 查询任务状态和结果 task_info tasks.get(task_id) if not task_info: return {error: Task not found} return task_info async def run_scrape_task(task_id: str, url: str, selector: str): 后台执行 Playwright 抓取任务 try: async with async_playwright() as p: browser await p.chromium.launch(headlessTrue) page await browser.new_page() await page.goto(url) # 简单示例获取指定选择器的文本内容 elements await page.locator(selector).all() results [] for elem in elements: text await elem.text_content() if text: results.append(text.strip()) await browser.close() tasks[task_id][status] completed tasks[task_id][result] results except Exception as e: tasks[task_id][status] failed tasks[task_id][error] str(e) if __name__ __main__: import uvicorn uvicorn.run(app, host0.0.0.0, port8000)运行与调用安装 FastAPI 和 Uvicorn:pip install fastapi uvicorn启动服务:python api_server.py调用 API:# 创建任务 curl -X POST http://127.0.0.1:8000/api/scrape \ -H Content-Type: application/json \ -d {url: https://news.ycombinator.com, selector: .titleline a} # 返回示例: {task_id:xxxx-xxxx, status:accepted} # 查询任务结果使用返回的 task_id curl http://127.0.0.1:8000/api/task/xxxx-xxxx6.2 设置为系统定时任务Cron Job对于需要每天/每周固定时间运行的任务可以结合操作系统的计划任务。创建一个独立的脚本daily_report.py它包含完整的抓取和报告逻辑。# daily_report.py import asyncio from playwright.async_api import async_playwright from datetime import datetime import json async def generate_daily_report(): print(f[{datetime.now()}] 开始执行每日报告任务...) async with async_playwright() as p: browser await p.chromium.launch(headlessTrue) page await browser.new_page() # ... 你的抓取和分析逻辑 ... # 例如抓取某个网站的关键指标 await page.goto(https://example.com/metrics) data await page.text_content(.metric-value) report { date: datetime.now().isoformat(), metric: data.strip() if data else N/A } # 保存报告 with open(f./reports/report_{datetime.now().date()}.json, w) as f: json.dump(report, f, indent2) print(f[{datetime.now()}] 报告生成完成。) await browser.close() if __name__ __main__: asyncio.run(generate_daily_report())然后在 Linux/macOS 上使用crontab -e添加一行设定每天上午9点执行0 9 * * * cd /path/to/your/script /usr/bin/python3 /path/to/your/script/daily_report.py /path/to/your/script/cron.log 21在 Windows 上可以使用“任务计划程序”来配置。7. 资源占用与性能观察Playwright 启动的浏览器实例会消耗内存和 CPU 资源这是需要关注的重点。内存占用一个典型的无头 Chromium 进程大约占用 100-300 MB 内存具体取决于页面复杂度。同时运行多个页面或浏览器实例时内存占用会线性增加。CPU 使用率在页面加载、渲染和执行 JavaScript 时会有 CPU 峰值。在空闲或无头模式下CPU 占用很低。性能优化建议使用无头模式headlessTrue这是生产环境的标配节省资源且更稳定。复用浏览器上下文避免为每个小任务都启动/关闭浏览器。可以启动一个浏览器实例然后创建多个独立的页面browser.new_page()来处理不同任务。合理设置超时与等待使用page.wait_for_selector、page.wait_for_function等明确等待比固定的time.sleep更高效。限制并发如果需要处理大量 URL使用信号量asyncio.Semaphore或任务队列来控制同时打开的页面数量防止内存耗尽。及时清理任务完成后确保关闭页面await page.close()和浏览器await browser.close()。监控方法在运行脚本时可以打开系统的任务管理器Windows或htopLinux/macOS来观察chromium或chrome进程的资源消耗情况。8. 常见问题与排查方法在编写和运行自动化脚本时你肯定会遇到各种问题。下表列出了一些典型问题及解决思路。问题现象可能原因排查方式解决方案脚本启动后浏览器闪退或无法启动1. 浏览器驱动未正确安装。2. 系统缺少依赖库多见于Linux。3. 端口冲突或已有浏览器进程残留。1. 运行playwright install --dry-run检查。2. 查看 Playwright 安装日志。3. 检查系统进程。1. 重新运行playwright install。2. 根据 Playwright 官方文档安装系统依赖。3. 结束残留的chromium进程。元素定位失败TimeoutError1. 选择器写错了或页面结构已变。2. 页面未完全加载或元素在 iframe 内。3. 网站有反爬机制如检测到自动化工具。1. 使用浏览器开发者工具F12验证选择器。2. 增加等待时间或使用page.wait_for_load_state(‘networkidle’)。3. 尝试有头模式(headlessFalse)看页面是否正常。1. 更新选择器使用更稳定的属性如>页面加载非常慢或卡住1. 网络问题或目标网站响应慢。2. 页面有大量异步加载资源如无限滚动。3. 脚本等待策略不佳。1. 检查网络增加page.goto(timeout60000)。2. 观察网络面板看是否有请求阻塞。3. 检查wait_for_*函数的使用。1. 设置合理的超时时间。2. 使用page.wait_for_load_state(‘domcontentloaded’)而非‘networkidle’。3. 对特定操作进行等待而非全局等待。在无头模式下运行结果与有头模式不同1. 某些网站针对无头浏览器进行了检测和屏蔽。2. 视口viewport大小不同影响页面布局。1. 对比有头和无头模式下的页面截图或HTML。2. 检查浏览器启动参数。1. 尝试使用playwright.chromium.launch(headlessTrue, args[‘--disable-blink-featuresAutomationControlled’])。2. 设置与有头模式相同的视口大小await page.set_viewport_size({‘width’: 1920, ‘height’: 1080})。文件下载不成功1. 下载路径未设置或没有写入权限。2. 下载被浏览器拦截或需要额外交互。1. 检查downloads_path参数和目录权限。2. 在有头模式下观察下载过程。1. 确保downloads_path是有效路径且脚本有权限写入。2. 监听download事件并使用download.save_as()方法指定路径。内存使用持续增长内存泄漏1. 页面、上下文或浏览器实例未正确关闭。2. 在循环中不断创建新页面而未关闭旧页面。1. 使用async with语句确保资源释放。2. 监控内存使用趋势。1. 确保每个page和browser在 finally 块或async with中被关闭。2. 考虑定期重启浏览器实例。9. 最佳实践与使用建议要让你的“干活浏览器”稳定、高效、可维护请遵循以下建议从简单开始逐步复杂化先在一个简单的静态页面上测试你的选择器和操作逻辑确保基础流程跑通再应用到目标网站。使用稳定的选择器优先选择id、>