用Electron写个带界面的nodejs爬虫的实现方法


Posted in NodeJs onJanuary 29, 2019

什么是Electron

使用 JavaScript, HTML 和 CSS 构建跨平台的桌面应用

[官网](https://electronjs.org/)

实质就是一个精简的Webkit浏览器显示html页面,通过electron做中间层可以和系统交流。给web项目套上一个node环境的壳。

前言

公司买的推广居然没有后台的api,没有api又不想死板手动操作。那就做个爬虫吧。但是又是给小白用的,自然最好带个界面,本来用C#拖出来就好了,看到vs那么大,下载都要半天。干脆就用Electron做一个,顺便学习一下Electron。

准备工作

安装nodejs

npm安装electron(最好换成cnpm,不然可能失败)

hello world

官方提供了快速开始的手脚架,怎么方便怎么来。

https://github.com/atom/electron-quick-start

clone下来

用Electron写个带界面的nodejs爬虫的实现方法

git那些不是我们需要的,就删掉。

安装相关的依赖,推荐用yarn。

yarn https://yarn.bootcss.com/

cd 到 目录下

cnpm install yarn
yarn

等待依赖安装完成。

npm run start

顺利的话就可以看到程序启动。

用Electron写个带界面的nodejs爬虫的实现方法

界面编写

准备完毕,开始进入正题。

用vscode打开文件夹,顺带一提,vscode也是基于electron。vscode不愧是巨硬出品,越来越好用了。

整理一下

用Electron写个带界面的nodejs爬虫的实现方法

这里就不累赘了。

后台有多个小号要登录,就写个登录页面。

编辑一下index.html

<html>
<head>
 <meta charset="utf-8">
 <link href="http://apps.bdimg.com/libs/bootstrap/3.3.4/css/bootstrap.min.css" rel="external nofollow" rel="stylesheet">
</head>
<body>
 <div class="panel panel-default" style="margin: 10px">
  <div class="panel-body">
   <div class="form-horizontal" role="form">
    <div class="form-group">
     <label for="input_name" class="col-sm-2 control-label">登录帐号</label>
     <div class="col-sm-10">
      <input type="text" class="form-control" id="input_name" placeholder="请输入用户名">
     </div>
    </div>
    <div class="form-group">
     <label for="input_pass" class="col-sm-2 control-label">登录密码</label>
     <div class="col-sm-10">
      <input type="password" class="form-control" id="input_pass" placeholder="请输入登录密码">
     </div>
    </div>
    <div class="form-group">
     <label for="input_check" class="col-sm-2 control-label">验证码:</label>
     <div class="col-sm-6">
      <input type="text" class="form-control" id="input_check" placeholder="请输入验证码">
     </div>
     <div class="col-sm-2">
      <img id="img_code" src="code.png" />
     </div>
    </div>
    <div class="form-group">
     <div class="col-sm-offset-2 col-sm-2">
      <button id="btn_submit" class="btn btn-default">登录</button>
     </div>
     <div class="col-sm-2">
      <button id="btn_refresh" class="btn btn-default">刷新验证码</button>
     </div>
    </div>
   </div>
  </div>
 </div>
 <script>
  require('./index.js');
 </script>
</body>
</html>

都是些 很简单的html代码,为了好看的就用了bootstrap

electron可以调用bootstap、jquery ,方便开发。调用jq有个小小的坑,注意一下。

不过我这里不需要什么效果,就简单点。

在vscode 快捷键 Ctrl + ` 调出 CMD 运行一下

npm run start

就可以看到我们刚刚写的网页了

用Electron写个带界面的nodejs爬虫的实现方法

下载验证码

登录是需要验证码的,我们把验证码下载下来。

流程是 请求验证码网站,下载验证码保存到本地显示,验证码的cookie保存下来,后面登录时候需要用到cookie

安装需要的依赖 superagent , fs-extra

编辑 main.js

const electron = require('electron')
const app = electron.app
const BrowserWindow = electron.BrowserWindow

const path = require('path')
const url = require('url')
// 爬虫
const superagent = require('superagent');
// 操作文件
const fs = require('fs-extra');

let mainWindow
// 验证码的cookie
var codeCookie
// 验证码网址
const codeUrl = '验证码地址';
// 头信息
const browserMsg = {
 'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36',
 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
};

function createWindow() {
 mainWindow = new BrowserWindow({
  width: 800,
  height: 600
 })
 superagent
  .get(codeUrl)
  .set(browserMsg)
  .end((err, res) => {
   codeCookie = res.header['set-cookie']
   console.log('codeCookie: ' + codeCookie)
   // 验证码图片保存到本地
   fs.outputFile(path.join(__dirname) + '/code.png', res.body, function (err) {})
  })
 mainWindow.loadURL(url.format({
  pathname: path.join(__dirname, 'index.html'),
  protocol: 'file:',
  slashes: true
 }))
 // 打开调试控制台
 mainWindow.webContents.openDevTools()

 mainWindow.on('closed', function () {
  mainWindow = null
 })

}
app.on('ready', createWindow)

app.on('window-all-closed', function () {
 if (process.platform !== 'darwin') {
  app.quit()
 }
})

app.on('activate', function () {
 if (mainWindow === null) {
  createWindow()
 }
})

说一下安装的依赖

fs-extra fs-extra模块是系统fs模块的扩展,提供了更多便利的 API,并继承了fs模块的 API。

主角就是 superagent https://www.npmjs.com/package/superagent

运行一下

用Electron写个带界面的nodejs爬虫的实现方法

用Electron写个带界面的nodejs爬虫的实现方法

很好,我们要的验证码和cookie 都有了。

分析登录流程

用 Fiddler 抓包工具和浏览器的调试控制台分析一下后台的登录。

这里不是重点就略过了。

Electron 通信

渲染进程(就是网页) 登录 需要 主进程保存的codeCookie ,这就要两者进行通信。

Electron之间的通信是用ipc

主进程的是 ipcMain 也可以用 mainWindow.webContents来发送

渲染进程的是 ipcRenderer

这里演示一下 主进程发送cookie 给 渲染进程

main.js 文件

const ipcMain = electron.ipcMain;
ipcMain.on('notice', (e, msg) => {
 switch (msg) {
  case 'getcodeCookie':
   mainWindow.webContents.send('codeCookie',codeCookie)
   break
  default:
   break
 }
})

打开调试控制台 可以看到输出

index.js

const electron = require('electron');
const ipcRenderer = electron.ipcRenderer;
// 获取控件
let btn_submit = document.getElementById("btn_submit");
btn_submit.addEventListener('click', (e) => {
  ipcRenderer.send('notice', 'getcodeCookie');
});
// 监听 codeCookie
ipcRenderer.on('codeCookie', (e, msg) => {
  codeCookie = msg;
  console.log('接受主进程发送的codeCookie: '+codeCookie);
});

运行一下, 点击登录按钮

就可以在调试控制台看到 codeCookie

用Electron写个带界面的nodejs爬虫的实现方法

模拟登录

我们需要登录后台,获取登录后cookie这样才方便我们操作。

编辑index.js

const electron = require('electron');
const ipcRenderer = electron.ipcRenderer;
const path = require('path');
const superagent = require('superagent');

// 链接
const urls = {
  loginUrl: "登录的地址",
  codeUrl: "验证码地址",
  targetUrl: "后台的地址"
};

// 头信息
const browserMsg = {
  "User-Agent": "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36",
  'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
};
// 验证码cookie
var codeCookie;
// 登录后的cookie
var tokenCookie;

// 获取控件
const btn_submit = document.getElementById("btn_submit");
const btn_refresh = document.getElementById("btn_refresh");
const input_name = document.getElementById("input_name");
const input_pass = document.getElementById("input_pass");
const input_code = document.getElementById("input_code");

// 登录按钮 点击事件
btn_submit.addEventListener('click', (e) => {
  ipcRenderer.send('notice', 'getcodeCookie');
  // 获取输入文本
  var name = input_name.value;
  var pass = input_pass.value;
  var code = input_check.value;
  // 校验输入
  if (name == "" || pass == "" || code == "") {
    alert("请输入");
  } else {
    // 校验通过 开始进行登录操作
    superagent
      .post(urls.loginUrl)
      .set('Cookie', codeCookie)
      .set(browserMsg)
      // 避免登录后的302重定向
      .redirects(0)
      .send({
        'LoginForm[username]': name
      }).send({
        'LoginForm[password]': pass
      }).send({
        logincode: code
      }).send({
        jz: '0'
      }).end((err, res) => {
        // 登录成功 获取tokenCookie
        // 获取tokenCookie
        tokenCookie = res.header['set-cookie'];
        superagent
          .get(urls.targetUrl)
          .set('Cookie',tokenCookie)
          .set(browserMsg)
          .end((err,res)=>{
            // 成功进入后台
            console.log(res.text);
          })
      });
  }

});

btn_refresh.addEventListener('click', (e) => {

});

ipcRenderer.on('codeCookie', (e, msg) => {
  codeCookie = msg;
  console.log('接受主进程发送的codeCookie: ' + codeCookie);
});

这里只是演示一下代码怎么写,具体不一定那么顺利的拿到tokenCookie。具体情况具体分析。

关键是请求要带上cookie

数据抓取

成功进入到后台了,就要抓取需要的数据了。这里就需要 cheerio 工具了

cheerio https://cheerio.js.org/

可以理解是node上jq , 操作基本跟jq是一样的。

操作还是很简单的。

要注意的是nodejs是异步的,就连for也是异步的。

有时候要等待请求完成的话,就要用上 async 了。

导出xlsx表格

爬虫基本完成了,怎么导出数据就随意了。

如果是要生成xls表格,一般是用excel-export 简单够用

我这里推荐 另一个 better-xlsx 。

这里演示一下 , 怎么调用系统的保存对话框,保存文件。

编辑 index.js

const remote = electron.remote;

btn_refresh.addEventListener('click', (e) => {

  const filepath =remote.dialog.showSaveDialog(remote.getCurrentWindow(), {
    // 过滤文件类型
    filters: [{
      name: "xls Files",
      extensions: ['xlsx']
    },
    {
      name: 'All Files',
      extensions: ['*']
    }
  ]
  });
  console.log(filepath);
});

运行一下,点击刷新验证码按钮就可以看到熟悉的系统对话框

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

NodeJs 相关文章推荐
Nodejs进程管理模块forever详解
Jun 01 NodeJs
14款NodeJS Web框架推荐
Jul 11 NodeJs
Nodejs学习笔记之测试驱动
Apr 16 NodeJs
Nodejs+Socket.io实现通讯实例代码
Feb 13 NodeJs
用nodeJS搭建本地文件服务器的几种方法小结
Mar 16 NodeJs
nodejs基于express实现文件上传的方法
Mar 19 NodeJs
nodejs之koa2请求示例(GET,POST)
Aug 07 NodeJs
NodeJs 模仿SIP话机注册的方法
Jun 21 NodeJs
Sublime Text3 配置 NodeJs 环境的方法
May 20 NodeJs
Nodejs实现微信分账的示例代码
Jan 19 NodeJs
使用 Koa + TS + ESLlint 搭建node服务器的过程详解
May 30 NodeJs
NVM安装nodejs的方法实用步骤
Jan 16 #NodeJs
nodeJS进程管理器pm2的使用
Jan 09 #NodeJs
NodeJS模块与ES6模块系统语法及注意点详解
Jan 04 #NodeJs
nodejs 使用http进行post或get请求的实例(携带cookie)
Jan 03 #NodeJs
详解nodejs 配置文件处理方案
Jan 02 #NodeJs
nodejs基础之多进程实例详解
Dec 27 #NodeJs
nodejs基础之常用工具模块util用法分析
Dec 26 #NodeJs
You might like
PHP实现微信公众平台音乐点播
2014/03/20 PHP
php计算两个日期时间差(返回年、月、日)
2014/06/19 PHP
vs2003 js文件编码问题的解决方法
2010/03/20 Javascript
JQuery UI DatePicker中z-index默认为1的解决办法
2010/09/28 Javascript
javascript实现图片切换的幻灯片效果源代码
2012/12/12 Javascript
jQuery div层的放大与缩小简单实现代码
2013/03/28 Javascript
JS获取url链接字符串 location.href
2013/12/23 Javascript
JavaScript匿名函数之模仿块级作用域
2015/12/12 Javascript
关于jQuery EasyUI 中刷新Tab选项卡后一个页面变形的解决方法
2017/03/02 Javascript
JS+HTML5实现上传图片预览效果完整实例【测试可用】
2017/04/20 Javascript
jQuery实现模糊搜索功能的方法分析
2018/06/29 jQuery
在vue项目中正确使用iconfont的方法
2018/09/28 Javascript
对angularJs中ng-style动态改变样式的实例讲解
2018/09/30 Javascript
Javascript之高级数组API的使用实例
2019/03/08 Javascript
使用原生js编写一个简单的框选功能方法
2019/05/13 Javascript
在Vue环境下利用worker运行interval计时器的步骤
2019/08/01 Javascript
微信小程序批量监听输入框对按钮样式进行控制的实现代码
2019/10/12 Javascript
javascript实现图片轮换动作方法
2020/08/07 Javascript
在vue项目中封装echarts的步骤
2020/12/25 Vue.js
Python threading多线程编程实例
2014/09/18 Python
编写Python的web框架中的Model的教程
2015/04/29 Python
Python实现完整的事务操作示例
2017/06/20 Python
flask-socketio实现WebSocket的方法
2018/07/31 Python
Python实现压缩文件夹与解压缩zip文件的方法
2018/09/01 Python
django的聚合函数和aggregate、annotate方法使用详解
2019/07/23 Python
如何通过python的fabric包完成代码上传部署
2019/07/29 Python
python爬虫中的url下载器用法详解
2020/11/30 Python
HTML5通用接口详解
2016/06/12 HTML / CSS
英国领先的NHS批准的在线药店:Pharmacy2U
2017/01/06 全球购物
英国电动工具购买网站:Anglia Tool Centre
2017/04/25 全球购物
奥地利购买珠宝和手表网站:ELLA JUWELEN
2019/09/03 全球购物
如何提高SQL Server的安全性
2016/07/25 面试题
中专生毕业个人鉴定
2014/02/26 职场文书
2014年服务行业工作总结
2014/11/18 职场文书
2015年新学期寄语
2015/02/26 职场文书
vue使用v-model进行跨组件绑定的基本实现方法
2021/04/28 Vue.js