Cheukdarsy / Cheukdarsy.github.io

personal blog, about tech, life or work
0 stars 0 forks source link

node爬取公积金_初记 #5

Open Cheukdarsy opened 7 years ago

Cheukdarsy commented 7 years ago

前言

老领导离职前交给我的最后一个任务是,做一个爬取公积金的系统,大概需求就是减少频繁地输入复杂公积金账号及身份证号码,当然还有验证码。在接到这个mission的时候,我脑海产品原型以及技术栈已经大概成型了,用户初次输入账号与身份证后绑定用户名,之后就直接从后台查询,不用再输入。技术栈方面后台node爬虫,koa2做webserver层,vue2/element做前端,redis做个缓存数据库,mogodb做后台数据库,分发爬回来的数据,项目将会慢慢展开,进展都会记录在这里。

爬取数据

分析登录过程

![](){http://opn2763v8.bkt.clouddn.com/chrome.png?e=1494258564&token=ZJ3VPs05J5IvXnEP0k5t1gO-OVA3KnS1CgSNB9_s:Tq2eFUeKa33l3PpEDxBJ80uuhKQ} 打开chrome浏览器的devtools(web开发者的最佳调试帮手),通过登录自己的账号,然后把整个http请求过程分析一遍,这个分析很重要!特别是对cookie以及post请求的content-type及formdata分析决定了是否可以有效获取数据。根据分析,查询通过post请求request的Content-Type是application/x-www-form-urlencoded,所以我们模拟传递的参数需要是querystring模式‘a=123&b=dsdsd’,如果Content-Type是其他格式(application/json)则是直接传类json的对象{a:'123', b:'...'}。同时在分析cookie时候发现JSESSIONID这个属性会随时间改变,其他的cookie都不变(毕竟公积金查询不需要用户登录,此cookie用来做验证码的绑定),也就是决定了验证码验证的关键因素。由于验证码也需要直接识别通过,我们可不想再半自动去做查询,经过提取验证码的地址,发现图片请求地址为code.jsp?yzm=1494233717242,也就是验证码获取地址后面应该要加个timestamp,拿到验证码后,就要开始识别图像啦,有点人工智能的滋味哈,业界比较出名的就是google的ocr,然后为了接近我们的爬虫语言,我选择了它的一个分支tesseract.js来做图片分析,由于公积金网站的验证码都是数字,ocr都不需要训练,基本识别率为99%。在确定了一些关键点之后就可以正式开码啦!

const fs = require('fs');
const path = require('path');
const querystring = require('querystring');
const http = require('http');
const Koa = require('koa');
const axios = require('axios');
const Tesseract = require('tesseract.js');
const parser = require('koa-bodyparser');

const app = new Koa();
const baseDir = path.join(__dirname, 'capcha.png');
const headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36' };
app.use(parser());

function getUrl() {
  const date = new Date().getTime();
  const url = `http://app.szzfgjj.com:7001/pages/code.jsp?yzm=${date}`;
  return url;
}

function downlaodCapcha(dir) {
  return new Promise((resolve, reject) => {
    axios({
      method: 'get',
      url: getUrl(),
      responseType: 'stream',
      headers,
      httpAgent: new http.Agent({ keepAlive: true })
    }).then((response) => {
      console.log(response.headers);
      if (Object.prototype.hasOwnProperty.call(response.headers, 'set-cookie')) {
        Object.assign(headers, { Cookie: `_gscu_1755638563=74963411y91hdo23; _gscbrs_1755638563=1; ${response.headers['set-cookie'][0].split(';')[0]}; Hm_lvt_4afe023cb2729993bb54a75b58c3bd3d=1494145133; Hm_lpvt_4afe023cb2729993bb54a75b58c3bd3d=${(new Date().getTime() - 100).toString().slice(0, 10)}` });
      }
      response.data.pipe(fs.createWriteStream(dir));
      Tesseract.recognize(dir).then((result) => {
        resolve(result.text);
      }).catch((err) => {
        throw new Error(err);
      });
    }).catch((err) => {
      console.log(err);
      reject(err);
    });
  });
}

function getAccountDetail(capcha) {
  const formData = {
    accnum: '20816069804',
    certinum: 440981199211096410,
    qryflag: 1,
    verify: parseInt(capcha, 10)
  };
  return new Promise((resolve, reject) => {
    axios({
      method: 'post',
      url: 'http://app.szzfgjj.com:7001/accountQuery',
      headers,
      data: querystring.stringify(formData),
      httpAgent: new http.Agent({ keepAlive: true })
    }).then((response) => {
      console.log(response);
      resolve(response.data);
    }).catch((err) => {
      reject(err);
    });
  });
}

app.use(async (ctx) => {
  const result = await downlaodCapcha(baseDir);
  const baseaccount = await getAccountDetail(result.slice(0, 5));
  ctx.body = baseaccount;
});

app.listen(3001);
console.log('tesseract img is starting at port 3001');

以上是目前最初写的爬取脚本,已经可以拿到了公积金数据,代码还写的有点混乱,慢慢再优化,好比如图片的识别过程是否可以改为直接数据流提取验证码,然后还有验证码提取错误的reload等等。第一part就到这里了,接下来就是要把获取到的数据通过koa2进行restful封装,再下一步就开始接入前端。当然还有docker的部署,这个可能要在这个过程就完成。 see next!