python爬虫神器Pyppeteer入门及使用
前言先纠正标题里一处需要打折的说法。Pyppeteer 是一个真实可用的库——它是 PuppeteerNode.js 生态里 Google 出品的 headless Chrome 自动化库的非官方 Python 移植版用它可以在 Python 里驱动一个无头 Chromium 打开网页、执行 JavaScript、等元素出现、截图。叫它「神器」在它能解决「页面内容是 JS 渲染出来的、直接抓 HTML 拿不到数据」这个痛点上并不过分。但「神器」这个定语今天已经过时了Pyppeteer 的仓库长期处于无人维护状态作者自己在项目首页挂出提示建议改用 Playwright 的 Python 绑定。所以本文的定位是「读懂并安全使用存量项目」如果你在 2026 年从零选型请优先考虑 Playwright其次 Selenium。Pyppeteer 的支持范围是 Python 3.8 及以上它基于async/await语法所以只能在 Python 3 里用——Python 2.7 已在 2020 年 1 月 1 日停止维护那些print语句时代的老写法在这里没有任何立足之地——print ...要写成print(...).iteritems()要写成.items()xrange要写成range。最后用无头浏览器抓页面本质上还是抓取行为一样要讲合规遵守目标站点的 robots.txt 与使用条款控制请求频率不要用自动化手段去绕过登录验证、验证码或付费墙。反爬与反反爬是一场没有赢家的消耗战正确的方向是找官方 API 或取得授权。一、它解决什么问题普通的抓取库比如urllib、requests拿到的是服务器返回的原始 HTML。如果页面主体是前端用 JavaScript 渲染出来的你拿到的 HTML 里往往只有一个空的容器节点真正的数据在浏览器执行完脚本后才出现。Pyppeteer 的做法是启动一个真实的 Chromium 进程把页面加载、脚本执行、异步请求全部跑完再从浏览器的 DOM 里取数据。它因此能覆盖这几类场景场景普通请求库无头浏览器静态服务端渲染页面可以可以但更重前端 JS 渲染的列表拿不到数据可以等元素出现后再取需要点击、翻页、填表单交互困难原生支持需要截图 / 导出 PDF不支持支持资源开销极小大要跑一个浏览器一句话能用静态抓取解决的就不要上浏览器只有在页面确实依赖 JS 渲染、或者需要交互时才值得付出这份开销。二、最小可用示例Pyppeteer 是异步库入口是launch()协程。注意首次运行时它会下载一份 Chromium约 150MB如果你机器上已有合适的 Chrome可以先用pyppeteer-install或启动参数指定避免重复下载。# 适用于 Python 3.8pyppeteer 2.0.0import asynciofrom pyppeteer import launchasync def main():# headlessTrue 表示无界面运行browser await launch(headlessTrue)page await browser.newPage()# 等待导航完成await page.goto(https://quotes.toscrape.com/js/)# 等某个元素真正出现在 DOM 里再取避免拿到空值await page.waitForSelector(div.quote)quotes await page.querySelectorAll(div.quote)for node in quotes:text await page.evaluate((el) el.querySelector(span.text).textContent, node)author await page.evaluate((el) el.querySelector(small.author).textContent, node)print(author.strip(), |, text.strip())await browser.close()if __name__ __main__:# 推荐的写法自动创建并关闭事件循环asyncio.run(main())上面用的是quotes.toscrape.com/js/这个子页面专门用 JavaScript 渲染内容正好用来演示无头浏览器的价值直接请求它拿不到名言必须等脚本跑完。asyncio.run(main())是 Python 3.7 起引入的推荐写法。老教程里常见的是asyncio.get_event_loop().run_until_complete(main())其中无参数调用get_event_loop()在 Python 3.10 起、当当前线程没有运行中的事件循环时会发出DeprecationWarning新代码请用asyncio.run()。三、选择器、求值与方法命名Puppeteer 的方法名大量借用了 JavaScript 里的$、$$、$x符号而$不是合法的 Python 标识符所以 Pyppeteer 做了映射并提供短名字PuppeteerPyppeteer短别名作用Page.$()Page.querySelector()Page.J()取第一个匹配元素Page.$$()Page.querySelectorAll()Page.JJ()取全部匹配元素Page.$x()Page.xpath()Page.Jx()用 XPath 取元素用querySelector拿到的是元素句柄ElementHandle要通过page.evaluate()在页面上下文里把它转成数据例如取文本# 适用于 Python 3.8pyppeteer 2.0.0element await page.querySelector(h1)title await page.evaluate((el) el.textContent, element)page.evaluate()的参数可以是「表达式字符串」或「函数字符串」Pyppeteer 会尝试自动判断判断错时会报错。这时可以显式加force_exprTrue强制按表达式处理# 适用于 Python 3.8pyppeteer 2.0.0content await page.evaluate(document.body.textContent, force_exprTrue)需要整页 HTML 时用await page.content()需要等待网络空闲可以配合waitForSelector或waitForNavigation这类等待方法。方法名与参数以 Pyppeteer 官方文档reference 页为准因为它是 Puppeteer 的移植版两者并非逐字对应。四、与 Scrapy 的关系常见做法是「Scrapy 负责调度与管道Pyppeteer 负责渲染」。Scrapy 是同步/基于 Twisted 的框架Pyppeteer 是 asyncio 库两者的事件循环模型不同直接在一个 Spider 里asyncio.run()会踩到「事件循环已经运行」的坑。稳妥的集成方式是在下载中间件里、或在独立进程/独立服务里做渲染再把渲染后的 HTML 交回 Scrapy 解析。具体怎么接以 Scrapy 与 Pyppeteer 各自官方文档为准不要照抄不明来源的中间件代码。合规上要注意无头浏览器更容易被目标站点识别它终究带着自动化的特征但发现被识别不是去研究怎么伪装而是回头问一句——这个站点允许我以这种频率、这种方式抓吗如果对方明确要求走 API、要求登录、或者 robots.txt 里禁掉了相关路径答案就是「不抓」。常见坑点1. 直接用asyncio.run()调用协程里的写法❌browser launch()忘了await拿到的是一个协程对象后面调用newPage()报AttributeError。 ✅ 所有返回协程的方法都要awaitbrowser await launch()。2. 在已有事件循环里再asyncio.run()❌ 在 Jupyter 或别的 async 环境里直接asyncio.run(main())报事件循环已在运行。 ✅ 在已有循环里用await main()或把脚本放到独立进程/独立入口里跑。3. 元素还没出现就去取❌await page.goto(url)之后立刻querySelector(div.quote)拿到None。 ✅ 先await page.waitForSelector(div.quote)或在evaluate里做存在性判断。4. 忘记关闭浏览器进程残留❌ 出错路径上没有await browser.close()Chromium 进程越积越多。 ✅ 用try/finally保证browser.close()一定执行。5. 把老教程的get_event_loop()原样抄进新代码❌asyncio.get_event_loop().run_until_complete(main())在 3.10 触发DeprecationWarning。 ✅ 改用asyncio.run(main())。6. 把$直接写进 Python❌page.$(h1)语法错误。 ✅ 用page.querySelector(h1)或用别名page.J(h1)。7. 每抓一个页面就新开一个浏览器❌ 在循环里反复await launch()每次都要冷启动 Chromium慢且占内存。 ✅ 一个浏览器实例 多个页面Tab循环结束再统一关。8. 把「绕过检测」当成学习目标❌ 花大量时间研究怎么让自动化浏览器不被识别用来抓对方明确禁止的内容。 ✅ 先判断抓取是否被允许被禁止就停手转去找官方 API 或申请授权。总结关注点结论定位Puppeteer 的非官方 Python 移植用无头 Chromium 渲染页面适用场景前端 JS 渲染、需要交互、需要截图维护状态已长期无人维护官方建议改用 Playwright支持版本Python 3.8仅 Python 3依赖async/await事件循环用asyncio.run()别用无参get_event_loop()开销比纯请求库大得多能用静态抓取就别上浏览器合规尊重 robots 与条款不绕登录墙不研究伪装绕过Pyppeteer 是理解「无头浏览器抓取」这套思路的好教材示例短、API 直观但选型时要看仓库的活跃度而不是看谁的教程标题里带「神器」两个字。真正决定一个爬虫好不好用的从来不是库的名气而是你有没有先把合规和边界想清楚。