文章目录学习目标一、开篇为什么你的Selenium脚本总是“玄学失败”1.1 回忆一下Selenium时代的“黑历史”1.2 Selenium的三大核心痛点1.3 Playwright是什么1.4 Playwright vs Selenium一图看懂二、10分钟快速上手Playwright2.1 环境要求2.2 安装Playwright2.3 同步模式 vs 异步模式2.4 第一个Playwright脚本三、傻瓜式定位器比Selenium好用100倍3.1 什么是定位器Locator3.2 推荐内置定位器四、自动等待告别time.sleep(3)4.1 Playwright的“天生优势”4.2 等待策略详解4.3 手动等待不得已才用五、核心操作方法速查5.1 页面导航5.2 交互操作5.3 数据提取5.4 截图和PDF六、完整实战爬取动态翻页产品列表6.1 完整代码可直接运行6.2 代码分段拆解七、高级特性让爬虫更强大7.1 浏览器上下文Context7.2 网络拦截7.3 无头模式7.4 移动端模拟八、常见报错与解决方案报错1playwright._impl._errors.TimeoutError: Timeout 30000ms exceeded报错2page.goto()超时但页面已经打开了报错3page.screenshot() 截图空白或截不到内容报错4locator.click() 时元素被遮挡报错5浏览器启动卡死报错6Playwright被网站检测九、Playwright在爬虫中的适用场景9.1 当前最适合的地方9.2 不太建议的地方十、Playwright vs Selenium vs Requests选型速查十一、总结本课核心知识清单承上启下的位置十二、课后作业作业1Playwright环境配置必做作业2模拟搜索与截图必做作业3无限滚动数据采集必做作业4多种定位器对比选做作业5异步爬虫热身选做作业6Playwright隐形对抗进阶《20节课精通网页爬虫》系列课程导航学习目标学完这一课你将能够看清Selenium的三大硬伤——知道为什么你的Selenium脚本总是“随机崩溃”“跑得慢”“维护累”理解Playwright的设计哲学——明白为什么它有自动等待、原生异步、直连协议这些颠覆性特性完成环境配置——用一条指令搞定Playwright安装和浏览器驱动下载掌握核心语法——学会sync_playwright上下文管理、goto()、locator()、click()、fill()等基础操作用好定位器——熟练使用get_by_text()、get_by_role()、get_by_placeholder()等语义化定位方式编写动态爬虫——完整实现一个“打开网页→定位元素→点击翻页→提取数据→保存结果”的实战项目解决常见坑点——遇到超时、元素找不到、浏览器启动失败等问题时知道从哪排查一、开篇为什么你的Selenium脚本总是“玄学失败”1.1 回忆一下Selenium时代的“黑历史”如果你用过Selenium写爬虫下面这些场景一定不陌生同一个脚本今天跑得好好的明天就崩了——原因只是网络慢了200毫秒你得写WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, “submit-btn”)))这种又长又难记的代码就为了等一个按钮出现你的回归测试套件跑了两个半小时每次上线前都要提前半天启动更别提那令人窒息的“Element not interactable”——明明元素就在页面上就是点不了这些问题不是你的错而是Selenium的底层架构跟不上时代了。1.2 Selenium的三大核心痛点痛点一执行速度和稳定性是“随机的”Selenium走的是“三层转发”你的代码 → WebDriver协议 → 浏览器驱动 → 浏览器。每一步都有序列化、反序列化和网络延迟就像寄快递中间经手人越多越容易丢件、延误。对比实测打开电商首页→搜索“手机”→加入购物车重复100次——Selenium平均耗时4.2秒标准差0.8秒Playwright平均耗时1.3秒标准差0.1秒Playwright快了3.2倍稳定性也大幅提升。痛点二等待写到你怀疑人生想等一个元素出现Selenium需要WebDriverWait(driver,10).until(EC.presence_of_element_located((By.ID,“submit-btn”)))还没开始测逻辑光等待就写了三四行。多层嵌套后的可读性更是灾难。痛点三维护成本暴增经验证的团队从Selenium迁移到Playwright后回归时间缩短了60%脚本维护成本下降了70%。不少团队至少30%的时间花在“救火”上而不是设计更有效的测试。1.3 Playwright是什么Playwright是微软在2020年推出的开源浏览器自动化库旨在彻底解决Selenium的架构缺陷。设计哲学可以概括为一次API跑遍所有现代浏览器Chromium、Firefox、WebKit代码直接和浏览器的DevTools协议对话没有中间代理自动等待元素就绪告别“time.sleep()猜数字”原生支持异步天生适配高并发1.4 Playwright vs Selenium一图看懂对比维度SeleniumPlaywright底层通信WebDriver协议三层转发CDP直接连接速度相对慢资源消耗高快40-50%资源效率更高自动等待需显式/隐式等待代码冗长内置智能等待元素就绪再操作异步支持“硬塞”的异步原生asyncio浏览器上下文每个会话一个完整浏览器轻量级浏览器上下文类似于隐身模式网络拦截有限支持一级支持跨浏览器Chrome、Firefox、Safari、IEChromium、Firefox、WebKit安装复杂度需手动下载驱动playwright install一键搞定测试稳定性易因网络抖动失败更稳定维护成本下降70%Playwright专为现代JavaScript动态网页React、Vue、Angular设计内置处理登录、弹窗、无限滚动等功能速度更快对Python用户更友好。二、10分钟快速上手Playwright2.1 环境要求Python 3.8推荐3.9-3.11兼容性最好pip包管理器2.2 安装Playwright只需两条命令# 第一步安装Playwright Python库pipinstallplaywright# 第二步安装浏览器二进制文件Chromium、Firefox、WebKitplaywrightinstallPlaywright默认自带Chromium、Firefox和WebKit首次运行时必须显式下载。这里需要特别注意很多人直接跑了代码中的p.chromium.launch()却报TimeoutError就是因为漏掉了playwright install这一步。国内用户建议加镜像加速playwrightinstall--driver-path https://mirrors.tuna.tsinghua.edu.cn/github-release/microsoft/playwright-cli/latest/2.3 同步模式 vs 异步模式Playwright for Python提供两种API模式同步模式sync_api代码自上而下一行写完就能看到对应窗口在动适合绝大多数中小爬虫任务。异步模式async_api基于asyncio性能更强、支持并发适合大规模高并发爬取——难度也相对更高。新手建议从同步模式开始。绝大多数自动化脚本用同步模式已经足够代码简单直观。异步性能比Selenium高30%以上但作为第一课我们不展开先跑通同步模式再考虑进阶。2.4 第一个Playwright脚本打开Chrome浏览器 → 访问百度 → 搜索“Python Playwright” → 截图保存fromplaywright.sync_apiimportsync_playwrightwithsync_playwright()asp:# 启动Chrome浏览器headlessFalse显示窗口调试用slow_mo让动作变慢方便观察browserp.chromium.launch(headlessFalse,slow_mo500)pagebrowser.new_page()# 访问百度page.goto(“https://www.baidu.com”)# 定位搜索框填充内容page.locator(“#kw”).fill(“Python Playwright 教程”)# 点击搜索按钮page.locator(“#su”).click()# 等待网络请求完成page.wait_for_load_state(“networkidle”)# 截图保存page.screenshot(path“baidu_search.png”)browser.close()print(“脚本执行完成截图已保存”)运行这个脚本时你会看到Chrome窗口自动打开、输入搜索词、点击按钮……就像有个隐形人在替你操作。用slow_mo500每个操作间隔0.5秒能帮你在调试阶段看清浏览器的一举一动生产环境要删掉。三、傻瓜式定位器比Selenium好用100倍3.1 什么是定位器Locator在Playwright中locator代表一种“在页面上随时查找元素的方法”。每个locator是一个惰性对象操作末尾统一加入await在同步模式中不需要await。Playwright提供了多种get_by_xxx系列的语义化定位方法——也就是说你不用再去写find_element(By.XPATH, “//div[class’xxx’]/…”)这种又臭又长的字符串而是用“找文本”“找占位符”这样更像人话的语法去描述你想找的目标。3.2 推荐内置定位器定位器方法用途示例get_by_role(role, name…)按ARIA角色定位最稳定page.get_by_role(“button”, name“登录”).click()get_by_text(“text”)按文本内容定位page.get_by_text(“下一页”).click()get_by_label(“label”)按表单标签定位page.get_by_label(“用户名”).fill(“admin”)get_by_placeholder(“placeholder”)按输入框占位符定位page.get_by_placeholder(“请输入手机号”).fill(“13800138000”)get_by_alt_text(“text”)按图片替代文本定位page.get_by_alt_text(“logo”).click()get_by_title(“title”)按title属性定位page.get_by_title(“关闭”).click()get_by_test_id(“id”)按data-testid定位page.get_by_test_id(“submit”).click()locator(“css/xpath”)自定义CSS/XPath选择器page.locator(“#kw”).fill(“hello”)核心建议优先使用面向用户的属性get_by_role()、get_by_text()这类定位器即便网页改版也不太会失效。四、自动等待告别time.sleep(3)4.1 Playwright的“天生优势”Selenium需要你手动写显式等待或隐式等待。而Playwright在操作元素之前会自动等待元素满足以下条件已附加到DOM可见稳定没有动画接收事件没有其他元素遮挡启用你不需要写任何额外的等待代码Playwright会在背后智能等待。4.2 等待策略详解Playwright提供了几个核心等待策略配合page.goto()使用等待策略说明适用场景“load”等待load事件触发传统服务端渲染SSR页面“domcontentloaded”等待DOM构建完成只需解析HTML结构时“networkidle”等待网络活动空闲推荐SPA动态应用、懒加载内容“commit”等待已收到响应极少使用对于现代JavaScript动态网站用默认“load”可能导致假超时DOM已就绪但load事件迟迟没触发。正确做法是压箱底的一行wait_until”networkidle”能救你90%的坑# ✅ 推荐等网络空闲适合SPApage.goto(“https://spa-site.com”,wait_until”networkidle”)# ✅ 最稳等某个特定元素出现page.goto(“https://shop.com”)page.wait_for_selector(“.product-card”,timeout10000)4.3 手动等待不得已才用# 等待选择器出现page.wait_for_selector(“#result”, timeout5000)# 等待元素可见page.wait_for_selector(“#loading”, state”hidden”)# 等待网络空闲page.wait_for_load_state(“networkidle”)# 不推荐固定等待治标不治本importtime time.sleep(3)# ❌五、核心操作方法速查5.1 页面导航# 跳转页面page.goto(“https://example.com”,wait_until”networkidle”)# 后退/前进page.go_back()page.go_forward()# 刷新page.reload()# 获取当前URLcurrent_urlpage.url5.2 交互操作# 点击元素page.click(“#submit-btn”)# 填充输入框page.fill(“#username”, “admin”)# 从下拉框选择page.select_option(“#country”, “CN”)# 勾选checkbox/radiopage.check(“#agree”)# 悬停page.hover(“.dropdown-menu”)5.3 数据提取# 获取元素文本textpage.text_content(“.title”)# 获取元素内部HTMLhtmlpage.inner_html(“.content”)# 获取属性值hrefpage.get_attribute(“a.link”,“href”)# 获取输入框值valuepage.input_value(“#search-input”)# 获取多个元素elementspage.query_selector_all(“.item”)foreleminelements:print(elem.text_content())5.4 截图和PDF# 截图整个页面page.screenshot(path“fullpage.png”)# 截某个元素logopage.locator(“.logo”)logo.screenshot(path“logo.png”)# 生成PDF仅Chromiumpage.pdf(path“page.pdf”)六、完整实战爬取动态翻页产品列表我们用一个公开的动态网站来实战——假设要爬取一个使用“加载更多”按钮分页的商品列表例如http://quotes.toscrape.com/js/这类专为爬虫练习的动态页面将商品名称和价格保存到CSV。为了便于你理解以下代码基于同步模式最终效果是浏览器打开网页 → 点击“加载更多”翻页3次 → 收集数据 → 存储。6.1 完整代码可直接运行 Playwright实战爬取动态翻页商品列表 数据字段产品名称、价格 运行次数翻页3次采集多页数据 importcsvfromplaywright.sync_apiimportsync_playwrightdefscrape_products():products_data[]withsync_playwright()asp:# 启动Chromium浏览器headlessTrue后台运行生产用headlessFalse调试用browserp.chromium.launch(headlessFalse,slow_mo300)pagebrowser.new_page()# 访问目标网站target_url“https://quotes.toscrape.com/js/”print(f“访问页面:{target_url}”)page.goto(target_url,wait_until“networkidle”)# 收集数据page_num1whilepage_num3:# 最多翻页3次print(f“\\\\n正在采集第{page_num}页...”)# 等待商品卡片加载具体选择器请查看网页实际结构page.wait_for_selector(“.quote”,timeout10000)# 获取所有商品卡片quotespage.query_selector_all(“.quote”)print(f“本页找到{len(quotes)}条数据”)forquoteinquotes:# 提取名言文本text_elemquote.query_selector(“.text”)texttext_elem.text_content()iftext_elemelse“N/A”# 提取作者author_elemquote.query_selector(“.author”)authorauthor_elem.text_content()ifauthor_elemelse“N/A” products_data.append({“text”:text,“author”:author,“page”:page_num})print(f“-{text}—{author}”)# 查找下一页按钮并点击next_btnpage.query_selector(“li.nexta”)ifnext_btn:next_btn.click()# 等待下一页加载完成page.wait_for_selector(“.quote”,timeout10000)page_num1else:print(“找不到下一页按钮停止翻页”)breakbrowser.close()returnproducts_datadefsave_to_csv(data,filename“quotes.csv”):ifnotdata:print(“没有数据可保存”)returnwithopen(filename,‘w’,newline”,encoding’utf-8-sig’)asf:writercsv.DictWriter(f,fieldnames[“text”,“author”,“page”])writer.writeheader()writer.writerows(data)print(f“✅ 成功保存{len(data)}条数据到{filename}”)if__name__“__main__”:datascrape_products()save_to_csv(data)6.2 代码分段拆解① 上下文管理器模式with sync_playwright() as p:自动处理启动和清理退出这个代码块时浏览器自动关闭即使程序报错也不会留下僵尸进程。② 启动浏览器browser p.chromium.launch(headlessFalse, slow_mo300)headlessFalse让你看到浏览器窗口调试完改为True提速slow_mo300放慢动作方便观察生产环境务必删掉。③ 页面导航与等待page.goto(url, wait_until”networkidle”)一直等到页面所有网络请求都静下来才返回适合SPA应用。如果SPA实在太慢也可以先goto再单独用wait_for_selector等某个具体元素出现。④ 元素查找再用page.query_selector_all(“.quote”)返回匹配选择器的所有元素和Selenium类似但返回的更干净。⑤ 查找下一页page.query_selector(“li.next a”)返回None说明没有下一页。拿到元素后直接.click()再wait_for_selector刷新后的商品卡片出现。七、高级特性让爬虫更强大7.1 浏览器上下文Context浏览器上下文相当于一个独立的隐身会话。Cookie、LocalStorage、会话数据互相隔离可以在一个浏览器实例中同时运行多个上下文——爬多个账号的数据互不干扰。# 创建两个独立的上下文context1browser.new_context()page1context1.new_page()context2browser.new_context()page2context2.new_page()7.2 网络拦截Playwright可以直接拦截和修改网络请求这在需要修改请求头、绕过某些检测时特别有用deflog_response(response):print(f“响应:{response.url}-{response.status}”)# 监听所有响应page.on(“response”,log_response)# 拦截并修改路由比如拦截图片请求提升速度defblock_images(route):ifroute.request.resource_type“image”:route.abort()# 取消图片加载else:route.continue_()page.route(“**/*”,block_images)7.3 无头模式headlessTrue让浏览器在后台运行不显示窗口。适合在生产服务器上跑爬虫browserp.chromium.launch(headlessTrue)# 后台运行资源占用更少7.4 移动端模拟# 模拟iPhone 12iphonep.devices[“iPhone12”]pagebrowser.new_page(**iphone)page.goto(“https://m.taobao.com”)八、常见报错与解决方案报错1playwright._impl._errors.TimeoutError: Timeout 30000ms exceeded原因浏览器二进制文件没有安装。不少人直接调用p.chromium.launch()之前忘了执行playwright install。解决playwrightinstallchromium# 只装Chromium省空间报错2page.goto()超时但页面已经打开了原因wait_until默认等待load事件而SPA页面靠JS渲染DOM已就绪但load事件没触发导致假超时反过来有些页面load很早完成但核心JS还没运行完你等错生命周期的阶段。解决page.goto(url,wait_until“networkidle”)# 或更稳妥先goto不管等待再手动等元素page.goto(url,wait_until“domcontentloaded”)page.wait_for_selector(“.main-content”,timeout10000)报错3page.screenshot()截图空白或截不到内容原因截图时机不对或者页面带懒加载组件React Suspense、IntersectionObserver截图前没等关键区域可见。解决# 等关键元素可见再截page.wait_for_selector(“.main-content”,state“visible”)page.screenshot(path“page.png”)# 滚动到底部后再等新内容page.evaluate(“window.scrollTo(0,document.body.scrollHeight)”)page.wait_for_timeout(1000)# 等懒加载动画报错4locator.click()时元素被遮挡Playwright默认会等待元素可操作可见、稳定、未被遮挡。如果元素确实被挡有备选方案# 强制点击绕过可见性检查page.locator(“#submit”).click(forceTrue)报错5浏览器启动卡死原因Linux服务器缺少图形环境和系统依赖库如libnss3、libatk-bridge2.0-0等无头模式资源不足或未调用playwright install-deps。解决# Linux上安装系统依赖playwright install-deps chromium# 无头模式 禁用沙箱仅Docker允许browserp.chromium.launch(headlessTrue,args[“--no-sandbox”])报错6Playwright被网站检测Playwright的默认设置可能会被反爬系统识别。解决方案如下按推荐顺序排列修改User-Agentcontextbrowser.new_context(user_agent“Mozilla/5.0(Windows NT10.0;Win64;x64)AppleWebKit/537.36”)加入更多伪装启动参数browserp.chromium.launch(headlessFalse,args[“--disable-blink-featuresAutomationControlled”])安装第三方隐形插件pipinstallplaywright-stealth在脚本中添加fromplaywright_stealthimportstealth_sync stealth_sync(page)# 在page对象上打补丁这段补丁会修改navigator.webdriver、chrome.runtime等浏览器特征常用站点基本无法分辨真人还是自动化。九、Playwright在爬虫中的适用场景9.1 当前最适合的地方SPA单页应用React/Vue/Angular数据量小但需要完整JS执行链Playwright天然支持复杂登录流程处理验证码、二次认证、多步骤提交无限滚动/懒加载页面只需滚动到底部再等新节点出现即可代码极其简单多选项卡/多窗口交互Playwright的Context管理比Selenium优雅得多9.2 不太建议的地方纯静态页面Requests BeautifulSoup快得多已经有清晰XHR接口的网站直接调用API比渲染快几十倍极大规模分布式爬取成本较高考虑Scrapy Splash等方案十、Playwright vs Selenium vs Requests选型速查场景推荐方案原因数据在HTML源码中静态Requests BS4/XPath最快0.1秒级响应有清晰的XHR接口直接调用接口极致高效无需浏览器简单动态少量JSPlaywright同步代码简洁调试友好复杂动态/交互频繁Playwright异步并发性能更强老旧系统/特定浏览器兼容Selenium虽然慢但社区广、浏览器覆盖面更全十一、总结本课核心知识清单知识点掌握程度Playwright vs Selenium核心差异能说出三大痛点和架构区别环境安装pip install playwrightplaywright install同步模式启动浏览器with sync_playwright() as p:语义化定位器get_by_text、get_by_role等一系列方法自动等待机制理解networkidle和服务器的“load”等待差异核心操作goto、click、fill、query_selector_all翻页/点击“加载更多”能写出无限滚动/按钮翻页的采集循环常见报错解决驱动安装超时、截图空白、被检测等问题隐形插件playwright-stealth使用场景和方法承上启下的位置承上解决了上一课Selenium留下的“慢、脆、难维护”三大痛点启下为第15课讲解异步高并发爬虫做铺垫十二、课后作业作业1Playwright环境配置必做在你的电脑上完成Playwright的安装和配浏览器下载。运行以下测试脚本确保能正常打印“✅ Playwright 环境配置成功”fromplaywright.sync_apiimportsync_playwrightwithsync_playwright()asp:browserp.chromium.launch(headlessTrue)pagebrowser.new_page()page.goto(“https://httpbin.org/anything”)print(“✅ Playwright 环境配置成功”)browser.close()作业2模拟搜索与截图必做用Playwright完成以下操作打开Binghttps://www.bing.com在搜索框中输入“Playwright 爬虫”点击搜索按钮等待搜索结果加载完成截图保存为“bing_search.png”提取第一条搜索结果的标题和链接打印到控制台作业3无限滚动数据采集必做访问https://quotes.toscrape.com/scroll一个无限滚动加载的名言网站编写Playwright脚本打开页面后自动滚动至少5次每次滚动到底部等待新内容加载完成收集所有的名言和作者将收集的至少30条数据写入CSV文件前提每次滚动之间加等待避免太快压垮页面请求。提示用page.evaluate(“window.scrollTo(0, document.body.scrollHeight)”)做滚动滚动后用page.wait_for_timeout(1000)等新内容渲染完成再用page.query_selector_all(“.quote”)抓取。作业4多种定位器对比选做在你的搜索脚本中分别用以下4种定位器找到搜索框locator(“#search”))← ID选择器get_by_placeholder(“搜索”)get_by_role(“textbox”)get_by_label(“搜索”)比较哪种方式在你的目标网站上最稳定写出结论和理由。作业5异步爬虫热身选做将作业2改成异步模式async_api使用asyncio.run()启动。比较同步模式和异步模式在执行多个页面并发打开时的耗时差异。这是为下一节课预热作业6Playwright隐形对抗进阶在目标网站https://arh.antoinevastel.com/bots/areyouheadless上测试两种模式普通Playwright启动加上playwright_stealth隐形补丁分别打印页面返回的检测结果写成一份简短的实验报告。结束语Playwright让浏览器自动化既智能又亲民。你不需要再为等待元素而焦虑不需要再为驱动版本问题烦恼更不需要忍受脚本“随机崩溃”的玄学。你的爬虫工具箱又一次升级了。下一课我们会进入异步爬虫的世界——用Playwright的异步模式和Scrapy框架实现高效并发让爬虫从“爬得准”升级到“爬得快”。《20节课精通网页爬虫》系列课程导航GO 感谢您耐心阅读到这里 如果本文对您有所启发欢迎 点赞 收藏 分享给更多需要的伙伴。️ 期待在评论区看到您的想法, 共同进步。 关注我持续获取更多干货内容 我们下篇文章见