需求描述
出于业务需要,经常要批量生成二维码。之前都是上 草料二维码 这个网站,手动生成一堆二维码,效率低,容易出错。每次做这件事的时候,都是一边生成二维码,一边在想:怎样能把这个操作自动化了呢?
方案调研
想起来之前在 GitHub 上看过一个项目,好像是什么无头浏览器(headless browser),查了查自己的 GitHub 收藏列表,果然找到了,就是 GoogleChrome/puppeteer 这个项目。
应用过程
流程梳理
梳理了一下生成二维码的一系列操作,分为以下几个步骤:
- 打开 草料网址二维码生成器 这个页面
- 等待页面加载完毕
- 在文本框中输入需要生成二维码的网址
- 点击按钮『生成二维码』
- 等待页面自动跳转
- 下载页面中显示的二维码图片
这么一看,整个流程是不是很简单?的确如此,不过里面有一个大坑,后面会讲到。
代码实战
打开页面
首先试试用 puppeteer 打开网页并截图,这个在项目官网上是有现成的代码的:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://cli.im/url');
await page.screenshot({
path: 'cli.png'
});
await browser.close();
})();
执行代码之后,查看生成的截图,虽然把网页截下来了,但是页面排版都错位了,有些内容没有显示出来。
![01](https://camo.githubusercontent.com/826767f6d1ce8da81166a462291929414d10afe0420a933226db274cffcdc180/687474703a2f2f3778713467782e636f6d312e7a302e676c622e636c6f7564646e2e636f6d2f70757070657465657273637265656e73686f742d30312e706e67)
参考资料:
查看官方文档,说页面尺寸默认设置为 800px * 600px,如果需要改变这个尺寸,就要用 Page.setViewport() 这个方法。在 Chrome 中打开空白标签页,查看页面尺寸为 1920px * 935px,那就设置成这个尺寸吧。
...
await page.goto('https://cli.im/url');
await page.setViewport({
width: 1920,
height: 935,
});
await page.screenshot({
path: 'cli.png'
});
...
这回再查看生成的截图,嗯,网页内容完整显示了,那就继续下一步。
![02](https://camo.githubusercontent.com/dbdb685793e81378bd1324be06fd22c6a8c0e8e10208e448a2360edfb0fef95f/687474703a2f2f3778713467782e636f6d312e7a302e676c622e636c6f7564646e2e636f6d2f70757070657465657273637265656e73686f742d30322e706e67)
参考资料:
文本框输入内容
页面加载完成之后,就需要在文本框中输入需要生成二维码的网址了。虽然在上一步的操作中,截图显示页面的内容都加载完成了。但是保险起见,还是需要等待文本框确实可用之后,再输入内容。
那么就需要先调用 page.waitForSelector()
这个方法,等待元素加载完毕。然后再调用 page.type()
方法,在文本框中输入网址:
...
// 文本框元素的 id 属性值为 url_content
await page.waitForSelector('#url_content');
await page.type(
'#url_content',
'http://www.baidu.com/', {
delay: 50,
});
执行代码,查看生成的截图,嗯,文本框中成功输入内容了,继续下一步。
![03](https://camo.githubusercontent.com/1a205f7505aa920a7a6410d1b8326f53541285b5b5c31a8247fb73e1b0b411e8/687474703a2f2f3778713467782e636f6d312e7a302e676c622e636c6f7564646e2e636f6d2f70757070657465657273637265656e73686f742d30332e706e67)
参考资料:
点击按钮生成二维码
接下来就该模拟点击按钮的操作,让网页生成二维码了。这个操作,用 page.click()
就可以实现了。
这里要和上一步一样,等待按钮确实可用之后,再去点击它。
...
await page.waitForSelector('#click-create');
await page.click('#click-create', {
delay: 50,
});
查看这一步操作完成后的截图,会发现页面停在“正在生成二维码”这里了,本应显示二维码图片的区域,显示的是一个表示“加载中”的图片,这是什么情况?
在浏览器中执行同样的操作,发现点击按钮之后,网站会跳转到新的页面,然后在新的页面上显示二维码。猜测 puppeteer 本身并不会自动跟随页面的跳转,那该怎么办咧?
上网用 puppeteer follow redirect
、puppeteer 自动跳转
之类的关键字搜索,都没有找到可用的结果。
于是又去翻官方 API 文档,看了看文档左侧的分类列表,觉得这个功能应该归在 Page
这个类的下面,于是就挨个浏览这个类提供的方法,咦,page.waitForNavigation()
这个方法很可能就是正在找的东西,把它加到代码里试试看。
...
await page.waitForNavigation();
![05](https://camo.githubusercontent.com/0d4732075dcdb83d060865f87b65e57253225c744e8649f65d57c083cdf42e5d/687474703a2f2f3778713467782e636f6d312e7a302e676c622e636c6f7564646e2e636f6d2f70757070657465657273637265656e73686f742d30352e706e67)
啊哈,就是它了!这回可以看到生成的二维码了。其实之前自己在写代码的时候并没有这么顺利,差不多花了半天的时间才定位到问题的原因,真是台上一分钟,台下十年功呐。
参考资料:
下载二维码图片
终于到了最后一步了:下载二维码图片。先用 Chrome 的开发者工具查看图片这个元素的信息,能够看到这个元素的 id
为 qrimage
,并且用 document.querySelector('#qrimage')
这条语句确定了当前页面中没有重复 id
的元素。
那么,就根据 SAVING IMAGES FROM A HEADLESS BROWSER 这篇文章中的方法,将图片保存至本地。
const qrcode = await page.$('#qrimage');
await qrcode.screenshot({
path: 'screenshot.png',
});
在本机查看生成的截图,啊哈,保存成功!
为了确保代码没有问题,再用 草料二维码扫描器 检查刚才生成的二维码,嗯,的确没问题,大功告成!
参考资料:
二维码批量生成及下载
照着前面的方法一步步操作,已经可以把二维码图片下载到本地了。但是,研究前面这些内容,最终是为了实现二维码批量生成及下载。
查看前面『流程梳理』这一小节的内容,再思考二维码批量生成及下载的整个流程,可以确定,每一次生成二维码的过程,会有变化的,只是输入的网址,和保存至本地的图片的文件名。
这样一来,只需要在上面的代码外面套一层循环,每次在循环体中传入网址和图片文件名这两个变量,就可以满足自己的需求了。而且因为需要批量生成的文件名是很规则的,所以这个实现起来也很简单。
未完待续……