1、什么是puppeteer?

puppeteer是google开源的一套利用nodejs实现的自动爬取网站,实现自动化操作的工具包,使用puppeteer可以实现网站数据爬取、UI自动化测试、学习puppeteer对于前端人员来说成本最低。puppeteer全都是使用的async/await语法,利用了大量的异步编程的思想。

2、本文大概需求概况

需求大概是这几天我那惹人喜欢的婆娘因为之前离职了导致某些原因必须要在国家社会保险公共服务平台上来提交关系转接,然后总是猴急猴撩的想知道转接进度,一天登录个好几次网站去看,我就突发奇想想着为什么不利用个puppeteer来做个小Demo练习一下呢,于是就有了今天的文章。说的不对的地方还请各位大佬多多指教!哈哈,代码中使用的包主要有如下几个:
puppeteer(google爬虫所需要的包)、nodemailer(发送邮件的包)、node-schedule(定时任务)、pm2(运行项目,自动重启项目)、tencentcloud-sdk-nodejs(腾讯云ocr识别所需要的包)
(1)首先我们必须安装puppeteer,执行命令

//1、创建一个文件夹 demo
npm init -y
cd demo
//2、安装puppeteer
cnpm i puppeteer --save    //没有安装cnpm的使用npm

puppeteer会自动下载google的Chromium可以理解成chrome浏览器的阉割版,后面的自动化操作都是利用该软件自动执行的。puppeteer的语法API大家可以上puppeteer中文网查看,里面讲的很清晰,需要用什么东西就直接拿就是了。
(2)首先分析一下国家社会保险公共服务平台这个网站,点击进入后看到的界面是这样的
首先我们需要进入该页面,然后大家可以hover一下登录按钮会出现个人登录、企业登录两个选项,我们当然要得是个人登录。那么我们要做几个事情:
1)打开一个浏览器,创建一个page页面,进入网址。
2)hover登录按钮,点击个人登录。
就这两个事情,那我们的代码基本是下面这样。
创建getReport.js文件如下:

     const puppeteer = require("puppeteer");console.log("初始化执行");//启动浏览器,接受一个对象设置browser对象的参数const browser = await puppeteer.launch({ headless:false, //无头部 ,运行时会自动打开Chromium浏览器,设置为true不会打开浏览器。slowMo:100, //降低执行速度,方便观察devtools:true //打开开发者工具})const page = await browser.newPage();  //等待browser对象创建pagepage.setViewport({width:1200,height:900}) //设置视窗的大小await page.goto("http://si.12333.gov.cn");  //page对象跳转至指定页面await page.waitForSelector("#login_button")  //等待登录按钮出现const closeDiv = await page.$(".closeDiv"); //获取右下角 烦人的弹窗对象closeDiv && await closeDiv.click()  //如果获取成功,则执行click方法关闭await page.hover("#login_button");  //hover登录按钮const userLoginBtn = await page.$("a.usercenter");  //找到个人登录按钮await userLoginBtn.click();  //点击个人登录按钮await page.waitForNavigation(); //等待页面跳转

上面代码中主要用到的是async/await来异步执行代码,其中page.$(“selector”)跟css选择器是一样一样的。

(3)很好,如果上面不出意外的话会自动跳转至登录页面

首先打开开发者工具找到账号、密码的id选择器,#userName,#pwd,编写代码,没有账号密码的可以自己注册一个。

await page.type("#userName","你的账号"); //输入账号
await page.type("#pwd","你的密码");  //输入密码

page.type方法接受两个参数,第一个是css选择器,第二个是输入的内容,同时page.type每个字符输入后都会触发 keydown, keypress/input 和 keyup 事件。这个算个小知识点。
输入账号密码之后,就到了验证码了,然后我发现wtf,这是个什么鬼,图形验证码,然后就一顿百度找各种图像识别ocr库,找到了一个比较热门的 tesseract,github上有如何使用tesseract,评星很高,但是我使用了一下发现非常的不友好,首先2.0版本的按照官方的指引文档根本不会执行,这个大家可以去了解一下,issues找了很久发现是需要指定几个文件路径,后续执行成功后发现wtf,这个库识别率低的可怕,图形验证码的准确率基本0%,这个库牛逼的地方在于可以自己使用训练模型,训练程序,提交准确率。该库训练后会自动生成eng.traineddata文件,至于如何训练大家可以自行百度一下,文章讲的很多,首先需要一些样本图片来让程序识别,然后再做后续操作。
一看到需要样本图片,算了,不用了,因为我用的别人的网站根本没有那些样本图片,后续想到了使用云能力解决这个问题,毕竟大佬还是大佬嘛,有白嫖的东西为啥不用,傻吗?
这里使用的腾讯云的OCR工具

首先登陆腾讯云控制台,找到左侧英文识别选项,点击右上角接口文档
点击调试按钮

其中左侧的个人秘钥必填项,如果没有秘钥,点击查看秘钥自己申请一个,然后再secretID及secretKey中输入。
下面的输入参数,其种图片ImageBase64,及图片路径ImageUrl二选一,如果传递了url则以url为准。
由于我的思想是将二维码截图保存在本地然后利用腾讯云识别所有的代码都在自己本地跑,没有放在服务器,所以这里选择的是ImageBase64模式,将图片转成base64之后传递。
腾讯云的代码复制过来依赖于tencentcloud-sdk-nodejs包,所以大家需要先安装一下

cnpm i tencentcloud-sdk-nodejs --save

然后创建ocr.js文件如下:

const tencentcloud = require("tencentcloud-sdk-nodejs");
class Ocr{constructor(){}imgToBase64(imgPath){const fs = require("fs");const path = require("path")const data = fs.readFileSync(imgPath); //不要再设置编码return data;}async init(imgPath){              const OcrClient = tencentcloud.ocr.v20181119.Client;const models = tencentcloud.ocr.v20181119.Models;const Credential = tencentcloud.common.Credential;const ClientProfile = tencentcloud.common.ClientProfile;const HttpProfile = tencentcloud.common.HttpProfile;//注意这里改成你自己的secretId及secretKey值let cred = new Credential("你的secretId", "你的secret秘钥");let httpProfile = new HttpProfile();httpProfile.endpoint = "ocr.tencentcloudapi.com";let clientProfile = new ClientProfile();clientProfile.httpProfile = httpProfile;let client = new OcrClient(cred, "ap-guangzhou", clientProfile);let req = new models.EnglishOCRRequest();let params = `{"ImageBase64":"${this.imgToBase64(imgPath)}"}`;// console.log(params)req.from_json_string(params);// console.log(req)return new Promise((resolve,reject)=>{client.EnglishOCR(req, function(errMsg, response) {if (errMsg) {reject(errMsg);return;}resolve(response.to_json_string())// console.log(response.to_json_string());});})}
}
module.exports = {Ocr}

这个ocr类主要是接受传进来的base64图片文件,然后将base64图片发给腾讯云识别,接受返回的数据。
我们修改getReport.js文件如下

const puppeteer = require("puppeteer");
const {TimeoutError} = puppeteer.errors;
// const {createWorker} = require("tesseract.js"); //引入图形识别
const path = require("path");
const {Ocr} = require("./ocr");
const {NodeMailer} = require("./nodemail");
const schedule = require("node-schedule"); //定时任务
class Report {constructor(){}schedule(){ //每天早上十点半自动发送var j = schedule.scheduleJob('30 30 10 * * *', ()=>{this.init();});}async init(){console.log("初始化执行");const browser = await puppeteer.launch({headless:false, //无头部slowMo:100,devtools:true})const page = await browser.newPage();page.setViewport({width:1200,height:900})await page.goto("http://si.12333.gov.cn");await page.waitForSelector("#login_button")const closeDiv = await page.$(".closeDiv"); //关闭弹窗closeDiv && await closeDiv.click()await page.hover("#login_button");const userLoginBtn = await page.$("a.usercenter");  //个人登录按钮await userLoginBtn.click();await page.waitForNavigation();// console.log("已跳转至登录页面")await page.type("#userName","你的登录账号"); //输入账号await page.type("#pwd","你的密码");  //输入密码const yzmFunction = async ()=>{try{  //处理ocr异常bugconst userNameValue = await page.$eval("#userName",el=>el.value)!userNameValue && await page.type("#userName","你的账号") const pwdVal = await page.$eval("#pwd",el=>el.value);!pwdVal &&  await page.type("#pwd","你的密码");  //输入密码//防止验证码无法出现的bug由于网站会经常出现无法出现验证码的情况,所以这里click两次,降低异常情况const imgReload = await page.$("#img_captcha")await imgReload.click()await imgReload.click()const yzm_el = await page.$(".yzm");//调用screenShot将元素截图保存在本地,并且以base64的形式存储起来await yzm_el.screenshot({  //将图片已base64的形式存储起来path:"./img/1.png", encoding:"base64"})//定义正则至匹配数字 + 英文。let reg = /^[a-zA-Z0-9]{4}$/g;//执行ocr识别 出现异常自动重新执行let  result = await new Ocr().init(path.join(__dirname,"./img/1.png"));   //接受ocr识别返回的验证码result = JSON.parse(result)const yzm = result.TextDetections[0].DetectedText.trim()  //获取识别的文本内容//这里做一层处理,过滤掉不是4位数的验证码,及非数字及英文的。if(yzm.length == 4 && reg.test(yzm)){ //正则匹配必须4位数为数字及英文console.log("验证码为:" + yzm)await page.type("#yz_text",yzm);  //输入验证码const login_btn = await page.$("#gr_login")  //登录按钮await login_btn.click()  //点击登录按钮await page.waitForNavigation({waitUntil:["networkidle0"]});//等待没有请求page.on("response", async response =>{// console.log(response.url())let url = response.url();if(url.indexOf("/cas/siLogin") > -1){ //由于登录时采用form表单提交的方式,在官网上并没有找到form表单提交返回的数据,还请各位大佬多指教,这里我只是将form返回的response捕获。console.log( response.status())                        }})await page.goto("http://si.12333.gov.cn/1989594.jhtml"); //直接跳转至详情页并执行截图//等待list列表的dom 元素出现await page.waitForSelector(".slick-viewport",{timeout:20000})  //waitfroSelector与goto一起使用console.log("开始截图") //执行截图const p = path.join(__dirname,"./img" + getDate()  + ".png");await page.screenshot({  //每日生成图片path:p})console.log("截图已完成")await browser.close();  //关闭浏览器new NodeMailer(p).sendMail(); //发送邮件}else{console.log("识别的验证码为" + yzm)//验证码验证不通过自动重新执行验证码过程yzmFunction(); //如果没有则继续重试}}catch(e){if(e instanceof TimeoutError){  //如果是waitfor方法超时则自动重试console.log("连接超时,重试")yzmFunction()return;// return;}console.log("发生异常错误!重新识别验证码")yzmFunction()}}yzmFunction()function getDate(){ //获取日期var d = new Date();var year = d.getFullYear()var month = d.getMonth() + 1;var day = d.getDate() ;year = year >= 10 ? year : '0' +year;month = month >= 10 ? month : '0' +month;day = day >= 10 ? day : '0' +day;return year + month + day;}console.log("执行完毕")}
}new Report().schedule(); //立即执行

上面代码大概指的是,封装了验证码的函数,

  1. 由于当验证码不正确,点击登录按钮会自动清空账号密码,所以这里做了一层判断当用户账号密码为空时才进行输入。
  2. 验证码由于网络原因经常会显示不出来,这里使用imgReload.click()执行了两次点击,降低验证码显示异常的几率。
    3)await yzm_el.screenshot({ //将图片已base64的形式存储起来
    path:"./img/1.png",
    encoding:“base64”
    })
    将验证码图片截图,并保存为base64形式,存放在本地。
    4)正则过滤掉不是4位验证码及非数字或英文的验证码。
    5)输入验证码,当验证码不通过时waitForNavigation会出现TimeoutError异常,我们在try/catch中重新执行yzmFunction方法,重新执行后续过程。当验证码通过时,跳转至详情页,并截图保存。

好了,这个大致思想就是这样,项目中我还用到了nodeMailer包将截图发送邮件通知。使用了node-schedule来创建了定时任务来定时执行登录,并发送邮件。
创建一个nodemail.js文件如下:

const nodemailer = require('nodemailer');class NodeMailer {constructor(imgPath){this.imgPath = imgPath;}sendMail(){let transporter = nodemailer.createTransport({// host: 'smtp.ethereal.email',service: 'qq', // 使用了内置传输发送邮件 查看支持列表:https://nodemailer.com/smtp/well-known/port: 465, // SMTP 端口secureConnection: true, // 使用了 SSLauth: {user: '你的邮箱',// 这里密码不是qq密码,是你设置的smtp授权码pass: '你的smtp授权码',}});let mailOptions = {from: '你的邮箱', // sender addressto:["接收人的邮箱'],subject: '来自老公的邮件', // Subject line// 发送text或者html格式// text: 'Hello world?', // plain text bodyhtml: '<b>社会关系转移邮件!</b>', // html bodyattachments:[{filename:"审核状态图片.png",path:this.imgPath}]};// send mail with defined transport objecttransporter.sendMail(mailOptions, (error, info) => {if (error) {return console.log(error);}console.log('Message sent: %s', info.messageId);});}
}module.exports = {NodeMailer}

大家可以自己去百度查一下如何使用这些包。记得将发件人邮箱及接收人邮箱改过来。
最后代码写好了之后使用 pm2包将文件运行起来

cnpm i pm2 -g  //全局安装

执行命令 pm2 start getReport.js
第一次写博客请大家多多指教。后续将公共的东西抽取出来方便修改,并发布到github上,大佬们多多评论指教。

node.js+puppeteer创建定时任务自动登录网站截取图片相关推荐

  1. Node.js毕业设计——基于Node.js+JavaScript+MongoDB的供求信息网站设计与实现(毕业论文+程序源码)——供求信息网站

    基于Node.js+JavaScript+MongoDB的供求信息网站设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于Node.js+JavaScript+MongoDB的供求信息网站设计 ...

  2. ENSP如何开启服务器的http_如何使用HTTP模块在Node.js中创建Web服务器(上)

    当你在浏览器中查看网页时,其实是在向互联网上的另一台计算机发出请求,然后它会将网页提供给你作为响应.你通过互联网与之交谈的那台计算机就是Web服务器,Web服务器从客户端(例如你的浏览器)接收HTTP ...

  3. node.js学习笔记14—微型社交网站

    node.js学习笔记14-微型社交网站 1.功能分析 微博是以用户为中心,因此需要有注册和登录功能. 微博最核心的功能是信息的发表,这个功能包括许多方面,包括:数据库访问,前端显示等. 一个完整的微 ...

  4. 间隔一段时间重复自动登录网站、定时自动登录网站的软件 —— 定时执行专家,无需复杂编程,简单配置即可使用

    常常有网友在网上发帖求助,想找一个定时自动登录网页(网站)的软件,大致需求如下: 1.打开网页 → 2.填写用户名.密码 → 3.点击登录按钮 → 4.登录成功 另外,还有诸如:要求间隔是每3个小时自 ...

  5. Java 扫描微信公众号二维码,关注并自动登录网站

    场景:用户扫描微信公众号的二维码,关注后自动登录网站,若已关注则直接登录. 逻辑: 1.系统生成带参数(此参数自定义为唯一值)的临时二维码(微信公众平台有提供该接口,可查看一下开发文档): 2.用户使 ...

  6. Node.js连接数据库 实现注册、登录、判断注册

    Node.js连接数据库实现注册,登录,在登录时检测账号是否进行注册. 准备工作 创建文件夹 此创建文件夹可以不是使用Vue-cli进行创建,只是简单创建文件夹便可. 使用npm进行mysql插件的安 ...

  7. HttpWebRequest自动登录网站并获取网站内容(不包含验证码的网站)

    HttpWebRequest自动登录网站并获取网站内容(不包含验证码的网站) 可以使用 Visual Sniffer(百度搜索) 来捕捉提交的数据信息: 1. 访问你需要站外提交的页面,比如 CSDN ...

  8. python网站自动答题_python自动登录网站答题-女性时尚流行美容健康娱乐mv-ida网...

    女性时尚流行美容健康娱乐mv-ida网 mvida时尚娱乐网 首页 美容 护肤 化妆技巧 发型 服饰 健康 情感 美体 美食 娱乐 明星八卦 首页  > 高级搜索 cookie实现 自动 登录 ...

  9. android js shell,使用adb shell+node.js实现抖音自动抢红包

    这次给大家带来使用adb shell+node.js实现抖音自动抢红包,使用adb shell+node.js实现抖音自动抢红包的注意事项有哪些,下面就是实战案例,一起来看一下. 逻辑很简单,在抖音视 ...

最新文章

  1. 检查点重做检查点队列简单总结Strut2教程-java教程
  2. android免root兼容所有版本ui调试工具
  3. 【数据挖掘】神经网络 后向传播算法( 向后传播误差 | 输出层误差公式 | 隐藏层误差公式 | 单元连接权值更新公式 | 单元偏置更新公式 | 反向传播 | 损失函数 | 误差平方和 | 交叉熵 )
  4. 【light 1341Aladdin and the Flying Carpet】
  5. Linux上常用命令整理(二)—— paste
  6. dumpstack_Java Thread类的静态void dumpStack()方法(带示例)
  7. Python的subprocess模块(一)
  8. Android 软键盘盖住输入框的问题
  9. NO.76 禅道使用分享第八期:创维软件开发团队畅谈专业版
  10. Linux 下载工具推荐: Motrix qbittorrent
  11. 微信小程序开发教程+工具插件
  12. linux如何回到下一级,linux如何返回上一级目录
  13. nodejs 读取写入 plist 文件,使用 plist npm 包
  14. 学java进美团_美团实习Java岗面经,已拿offer
  15. 使用MediaPlayer的一些常见报错及解决方法-1
  16. Perl 最佳实践(节选) --- 12
  17. C++多线程匿名聊天室(控制台)
  18. 一步步认识jdk 我们的朋友 之Arrays
  19. 数学:定积分和数列和互相转化
  20. ios开发之音频视频开发

热门文章

  1. 腾讯即时通信 tim-sdk.js功能扩展
  2. 免费专属 | 100行Python代码实现一款高精度OCR工具
  3. 能够关闭并退出计算机程序的是,电脑强制关闭程序按哪三个键 可按Alt+F4关闭当前页面...
  4. jira是干什么_JIRA的使用介绍(一)- 概念篇
  5. Hadoop分布式集群配置
  6. I-Deas TMG 培训资料 (1)
  7. uni-app学习 组件---a链接的跳转(四)
  8. 关于虚拟机中安装Ubuntu时界面显示不全的解决办法
  9. opencv小游戏(05):小车的运动
  10. 为何要开办《微积分阅览室》?