用 Playwright 写爬虫先搞定 Ubuntu 环境Python 自动化测试之外的实战场景当你在搜索引擎里输入Python爬虫时前几页结果大概率充斥着Requests和BeautifulSoup的教程。但如果你尝试抓取一个现代前端框架构建的网站很快会发现这些传统工具在面对动态渲染内容时的无力感。这时候Playwright这个微软开源的浏览器自动化工具就成为了破局利器——它不仅能完美处理SPA单页应用的数据抓取还能模拟真实用户操作绕过部分反爬机制。不过在兴奋地写下第一行爬虫代码之前我们需要先解决一个更基础的问题如何在Ubuntu服务器上为Playwright搭建稳定的运行环境与本地开发机不同服务器环境通常没有图形界面依赖库也可能残缺不全。更棘手的是爬虫任务往往需要长时间稳定运行这对浏览器实例的资源管理提出了更高要求。1. 为什么选择Playwright作为爬虫工具在讨论具体配置之前有必要先理解Playwright相较于传统爬虫方案的技术优势。我曾在一个电商价格监控项目中同时尝试过三种方案传统HTTP请求、Selenium和Playwright。结果Playwright不仅成功率最高运行效率也比Selenium提升了40%以上。核心优势对比特性RequestsBS4SeleniumPlaywright动态内容渲染❌ 不支持✅ 支持✅ 支持多标签页管理❌ 不支持⚠️ 有限支持✅ 原生支持执行速度⚡️ 极快 较慢 快反爬绕过能力❌ 弱✅ 中等✅ 强资源占用⚡️ 极低 高 中等特别是Playwright的自动等待机制让代码不再需要到处写time.sleep()。当页面元素加载完成时它会自动继续执行这大大提高了爬虫的稳定性。另一个杀手级功能是网络请求拦截可以直接捕获AJAX请求而不必渲染整个页面。2. Ubuntu环境准备不只是apt install那么简单在干净的Ubuntu 22.04 LTS系统上仅安装playwright包是不够的。我们的目标是构建一个可以7×24小时稳定运行的爬虫环境这需要更全面的依赖管理。以下是我在AWS EC2实例上验证过的完整配置流程# 先更新系统并安装基础编译工具 sudo apt update sudo apt upgrade -y sudo apt install -y build-essential python3-pip libssl-dev # 安装浏览器运行所需的媒体库 sudo apt install -y \ libgstreamer-plugins-base1.0-0 \ libgstreamer1.0-0 \ gstreamer1.0-plugins-good \ gstreamer1.0-plugins-bad \ gstreamer1.0-plugins-ugly \ libx264-dev \ libnss3 \ libatk1.0-0 \ libatk-bridge2.0-0 \ libdrm2 \ libxkbcommon0 \ libxcomposite1 \ libxdamage1 \ libxrandr2 \ libgbm1 \ libgtk-3-0 \ libasound2注意如果服务器内存小于2GB建议添加交换空间以避免浏览器进程被OOM Killer终止sudo fallocate -l 2G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile echo /swapfile none swap sw 0 0 | sudo tee -a /etc/fstab安装Python环境时强烈建议使用venv创建隔离环境python3 -m venv playwright_env source playwright_env/bin/activate pip install playwright1.40.0 # 指定稳定版本3. 浏览器管理为爬虫任务优化配置Playwright默认会安装Chromium、Firefox和WebKit三个浏览器引擎。但对爬虫任务来说这既浪费存储空间又可能导致资源竞争。更专业的做法是# 只安装Chromium并跳过其他浏览器 playwright install chromium在服务器环境中以下几个启动参数能显著提升稳定性from playwright.sync_api import sync_playwright with sync_playwright() as p: browser p.chromium.launch( headlessTrue, args[ --disable-gpu, --single-process, # 单进程模式更节省资源 --no-zygote, --no-sandbox, --disable-setuid-sandbox, --disable-dev-shm-usage # 避免/tmp空间不足 ], timeout60000 # 延长启动超时时间 ) context browser.new_context( viewport{width: 1920, height: 1080}, user_agentMozilla/5.0 (Windows NT 10.0; Win64; x64) ... ) page context.new_page() # 你的爬虫代码...关键参数解析--disable-dev-shm-usage解决Docker环境中常见的/dev/shm内存不足问题--single-process牺牲部分稳定性换取更低内存占用适合简单页面timeout60000云服务器启动浏览器可能比本地慢4. 实战技巧高效稳定的爬虫架构设计基于Playwright的爬虫与传统爬虫在架构上有显著不同。以下是经过多个生产项目验证的最佳实践1. 浏览器实例复用策略不要为每个请求都创建新浏览器而是采用分层复用结构主进程 ├── 浏览器实例池 (3-5个) ├── 上下文池 (每个浏览器5-10个) ├── 页面池 (每个上下文10-20个)实现代码示例from threading import Lock class BrowserPool: def __init__(self, max_browsers3): self.browsers [] self.lock Lock() def get_browser(self): with self.lock: if len(self.browsers) max_browsers: browser p.chromium.launch(headlessTrue) self.browsers.append(browser) return browser return random.choice(self.browsers)2. 智能请求调度算法结合Playwright的网络拦截功能可以实现先轻量级探测再完整渲染的策略async def handle_request(route, request): if request.resource_type in {image, stylesheet, font}: await route.abort() elif /api/data in request.url: # 直接拦截API请求而不渲染页面 await route.continue_() api_response await request.response() data await api_response.json() process_data(data) await route.fulfill(responseapi_response) else: await route.continue_() page.route(**/*, handle_request)3. 反反爬技巧组合拳指纹混淆定期更换UserAgent、屏幕分辨率、时区行为模拟添加随机鼠标移动和滚动动作流量分散通过不同上下文使用不同代理IPdef get_random_fingerprint(): return { viewport: random.choice([ {width: 1366, height: 768}, {width: 1920, height: 1080}, {width: 1536, height: 864} ]), user_agent: random.choice(USER_AGENTS), timezone_id: random.choice([America/New_York, Asia/Shanghai, Europe/London]) } context browser.new_context(**get_random_fingerprint())5. 性能监控与异常处理长期运行的爬虫必须配备完善的监控体系。以下是在Ubuntu上实施的方案关键指标采集# 在爬虫代码中添加性能埋点 start_time time.time() try: page.goto(url, timeout15000) # ...处理逻辑... except Exception as e: log_error(f请求失败: {url} - {str(e)}) metrics.counter(page_failures, tags[url:url]) finally: metrics.timing(page_load, time.time() - start_time)系统级监控脚本保存为monitor.sh#!/bin/bash while true; do # 记录内存使用 mem_usage$(free -m | awk /Mem/{print $3}) # 记录浏览器进程数 browser_count$(ps aux | grep -E [c]hromium|[f]irefox | wc -l) echo $(date %Y-%m-%d %H:%M:%S),$mem_usage,$browser_count /var/log/playwright_monitor.csv sleep 30 done自动恢复机制def auto_recover(): # 清理残留进程 os.system(pkill -f chromium|firefox) # 重启浏览器池 browser_pool.restart() def run_with_retry(task, max_retries3): for attempt in range(max_retries): try: return task() except PlaywrightTimeoutError: if attempt max_retries - 1: auto_recover() raise time.sleep(5 ** attempt)6. 本地开发与服务器环境的差异处理在Mac/Windows本地开发时能跑的脚本直接扔到Ubuntu服务器可能会遇到各种问题。以下是常见差异及解决方案环境变量差异import os # 判断运行环境 is_server os.getenv(DEPLOY_ENV) production browser_args [ --disable-gpu, --no-sandbox ] if is_server: browser_args.extend([ --disable-dev-shm-usage, --single-process ])路径处理统一方案from pathlib import Path # 跨平台缓存目录设置 CACHE_DIR Path.home() / .cache / my_spider CACHE_DIR.mkdir(parentsTrue, exist_okTrue) # 截图保存路径 screenshot_path CACHE_DIR / screenshots / f{page_id}.png page.screenshot(pathstr(screenshot_path))依赖检查脚本在项目根目录创建check_env.pyimport subprocess import sys def check_ubuntu_deps(): required [libgstreamer-plugins-base1.0-0, libnss3] missing [] for pkg in required: result subprocess.run( [dpkg, -s, pkg], stdoutsubprocess.PIPE, stderrsubprocess.PIPE ) if result.returncode ! 0: missing.append(pkg) if missing: print(f缺少依赖: {, .join(missing)}) print(安装命令: sudo apt install -y .join(missing)) sys.exit(1) if __name__ __main__: check_ubuntu_deps() print(环境检查通过)7. 高级技巧无头浏览器的视觉调试即使是在headless模式下也有办法看到浏览器状态这对调试复杂爬虫非常有用1. 远程调试端口browser p.chromium.launch( headlessTrue, args[--remote-debugging-port9222] )然后在本地机器上通过SSH端口转发访问ssh -L 9222:localhost:9222 userserver浏览器访问localhost:9222即可看到远程浏览器状态。2. 视频录制context browser.new_context(record_video_dirvideos/) page context.new_page() # ...操作页面... context.close() # 会自动保存视频3. 控制台日志重定向page.on(console, lambda msg: print(f[CONSOLE] {msg.text})) page.on(pageerror, lambda err: print(f[ERROR] {err})) page.on(requestfailed, lambda req: print( f[REQ FAIL] {req.url} - {req.failure.error_text} ))8. 资源清理与维护长时间运行后Playwright可能会积累大量临时文件。这里有几个维护建议定期清理脚本#!/bin/bash # 清理Playwright缓存 rm -rf ~/.cache/ms-playwright # 清理旧日志 find /var/log/playwright -type f -mtime 7 -delete # 重启服务 systemctl restart my_spider.service内存泄漏检测在Python启动时添加这些参数PYTHONMALLOCmalloc python -X tracemalloc25 my_spider.py然后定期检查日志中的内存分配情况。浏览器实例健康检查def check_browser_health(browser): try: context browser.new_context() page context.new_page() page.goto(about:blank, timeout5000) html page.content() context.close() return about:blank in html except: return False