目录:
Pyppeteer 是 Google Puppeteer(一个用于控制 Chrome/Chromium 浏览器的 Node.js 库)的非官方 Python 端口实现。允许开发者通过 Python 程序化地控制 Chromium 或 Chrome 浏览器,实现网页自动化操作,如爬取动态渲染内容、自动化测试、生成截图与 PDF 等。
相较于传统的 Selenium,Pyppeteer 因其免驱动配置、基于异步协程而效率更高,成为处理 JavaScript 渲染页面的强大工具。
底层基于 Python 的 asyncio 异步库构建,所有主要操作都是协程(coroutine),天然支持异步并发,这在处理大量网页时能显著提升效率。例如,在爬取多个基金净值数据的对比实验中,异步执行相比顺序执行速度提升了约 6 倍。
无头浏览器本身占用内存较多。在生产环境中应使用 headless=True 模式,并通过 args 添加 –no-sandbox、–disable-gpu 等参数以提高稳定性。对于无限滚动页面,合理设置视口 (setViewport) 和窗口大小可能有助于内容正确加载。
对于反爬虫,除了使用 –disable-infobars 和注入 JS 隐藏 webdriver 属性外,还可以考虑启用无痕模式 (createIncognitoBrowserContext) 来隔离环境。
安装依赖:
$ pip install pyppeteer
安装后,建议运行 pyppeteer-install 命令来预先下载 Chromium。
如果本机已经安装过 Chrome,可以使用 executablePath 参数指定自定义的 Chrome/Chromium 可执行文件路径,绕过自动下载,我在 MacOS 上使用本地 Chrome 示例如下:
import asyncio
from pyppeteer import launch
async def main():
browser = await launch(
headless=False,
args=['--disable-infobars'],
executablePath="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
)
page = await browser.newPage()
await page.goto('https://www.baidu.com')
# ... 后续操作
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
page.title() 可以获取网页标题,需要注意的是所有方法都是异步的,前面要加上 await,比如:
print(await page.title())
page.content() 可以获取网页 HTML 完整内容,前面也需要加上 await。
import asyncio
from pyppeteer import launch
async def main():
browser = await launch(
headless=False,
args=['--disable-infobars'],
executablePath="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
)
page = await browser.newPage()
await page.goto('https://www.baidu.com')
print(f'网页标题: {await page.title()}')
html_content = await page.content()
# print(f'网页内容: {html_content}')
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
# 定位搜索框并输入文本
await page.type('#kw', 'Python Pyppeteer', {'delay': 100}) # delay模拟输入间隔
# 定位并点击搜索按钮
await page.click('#su')
# 或者先定位元素再操作
search_box = await page.querySelector('#kw')
await search_box.type('Hello World')
# 获取元素的属性和文本内容
elements = await page.querySelectorAll('a.news-title')
for elem in elements:
text = await (await elem.getProperty('textContent')).jsonValue()
link = await (await elem.getProperty('href')).jsonValue()
print(f'标题: {text}, 链接: {link}')
page.evaluate() 可以执行 JS 代码。
比如需要获取视口大小:
dimensions = await page.evaluate('''() => {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio,
}
}''')
print(dimensions)
比如滚动页面到底部:
await page.evaluate('window.scrollTo(0, document.body.scrollHeight);')
比如隐藏 WebDriver 特征(重要反反爬措施)
await page.evaluate('''() => {
Object.defineProperty(navigator, 'webdriver', { get: () => false });
}''')
page.screenshot() 可以方便地将页面保存为图片。
import asyncio
from pyppeteer import launch
async def main():
browser = await launch(
headless=False,
args=['--disable-infobars'],
executablePath="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
)
page = await browser.newPage()
await page.goto('https://blog.dkvirus.com')
await page.screenshot({'path': 'screenshot.png', 'fullPage': True}) # 截图
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
page.pdf() 可以方便地将页面保存为 PDF 文档。
import asyncio
from pyppeteer import launch
async def main():
browser = await launch(
headless=False,
args=['--disable-infobars'],
executablePath="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
)
page = await browser.newPage()
await page.goto('https://blog.dkvirus.com')
await page.pdf({
'path': 'page.pdf',
'format': 'A4',
'printBackground': True, # 启用背景
'preferCSSPageSize': True, # 启用 CSS 控制
}) # 生成 PDF
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
生成的 PDF 里面的内容是矢量图,放大也不会变模糊,关键是生成速度太快了。
对于 box-shadow 盒子阴影支持并不好,直接从浏览器上打印 PDF 也有这个问题,解决方法是在全局样式中设置禁用盒子阴影,加上如下 CSS 代码:
* { box-shadow: none!important; }
通过设置请求拦截并监听 request 和 response 事件,可以优化爬取效率或分析数据接口。
await page.setRequestInterception(True)
async def intercept_request(req):
# 阻止图片、媒体等非必要请求以提升速度
if req.resourceType in ['image', 'stylesheet', 'font', 'media']:
await req.abort()
else:
await req.continue_()
page.on('request', intercept_request)
# 监听响应,获取API数据
async def intercept_response(res):
if 'api/data' in res.url:
data = await res.json()
print(data)
page.on('response', intercept_response)
import asyncio
from pathlib import Path
from pyppeteer import launch
async def main():
browser = await launch(
headless=False,
args=['--disable-infobars'],
executablePath="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
)
page = await browser.newPage()
await page.goto(f'file:///{Path("test.html").absolute()}')
print(await page.title())
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
↶ 返回首页 ↶