这篇 wiki 来自对 Saving Images From a Headless Browser 一文的转译,描述如何从 headless browser 中抓取图片。
适用的场景是:
- 有些图片不适合用其他手段(如 Python 爬虫)去下载,如:
- 写爬虫成本高过操作 headless browser
- 图片是仅在登录状态可见
- 图片是一次性的,如验证码等
下面介绍几种方案来下载 headless browser 中的图片。
屏幕截图
这种方式比较简单粗暴,即是用 Puppeteer 的 API 定位到你要截图的元素,再调用相应的截图 API 去截图。缺点是只能截取到该图片在浏览器中展示的大小,比如在网页做了 responsive 的情况下,一个 2000x1500 的图可能仅以 1000x750 的大小展示出来,那么这种方式只会截取到 1000x750 的大小。代码示例如下:
Python 版:
import asyncio
from pyppeteer import launch
async def main():
browser = await launch({'headless': True, 'args': ['--no-sandbox']})
page = await browser.newPage()
await page.setViewport({"width": 1280, "height": 926})
await page.goto('https://intoli.com/blog/saving-images/')
await page.waitForSelector('#svg')
# Select the #svg img element and save the screenshot.
svg_image = await page.querySelector('#svg')
await svg_image.screenshot({
'path': 'logo-screenshot.png',
'omitBackground': True,
})
await browser.close()
if __name__ == '__main__':
asyncio.get_event_loop().run_until_complete(main())
Node.js 版:
const puppeteer = require('puppeteer');
(async () => {
// Set up browser and page.
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.setViewport({ width: 1280, height: 926 });
// Navigate to this blog post and wait a bit.
await page.goto('https://intoli.com/blog/saving-images/');
await page.waitForSelector('#svg');
// Select the #svg img element and save the screenshot.
const svgImage = await page.$('#svg');
await svgImage.screenshot({
path: 'logo-screenshot.png',
omitBackground: true,
});
await browser.close();
})();
通过页面 JavaScript 获取图片
浏览器提供了 Data URLs 用来序列化二进制数据:
data:<image format>;base64,<image data>
可以通过 base64 decode 获得图片内容。
用这个思路,你可以将图片内容画到一个新建的 Canvas 上,然后调用 canvas.toDataURL()
获取数据。这种方式会受到 CORS 的限制,并不是太完善。代码参考英文原文。
用 DevTools 协议提取图片
Puppeteer 跟 Chrome 之间用 DevTools Protocol API 进行通讯。可以用 Page.getResourceContent
协议来获取图片资源的内容,也以 base64 方式传输。
根据这个 issue,pyppeteer 并没有提供对 DevTools Protocol 协议的封装,需要自行开发。puppeteer 库则有私有 API 做了封装。