使用node构建爬虫服务

101 阅读2分钟

1.node服务搭建 1)执行npm init,初始化node服务,项目目录结构为

image.png

bin文件夹用于存放打包启动相关配置
lib文件夹用于存放打包好的文件
src/app用于存放应用实例
src/config用于存放基础配置
src/gitConfig用于存放git相关配置
src/router用于存放路由相关配置(接口定义)
src/router_handler用于存放接口处理函数(接口实现)
main为入口文件

所需依赖:

 cheerio:解析器/*chalk 不能使用 4.1 以上版本否则提示必须使用 es6 语法,不支持 commonjs*/
 cors:防跨域
 esbuild:打包器
 express:项目框架
 fs-extra:文件读取器
 superagent:npde爬虫
 superagent-proxy:爬虫代理

项目实现:

//main.js引入配置的端口,引入app应用实例
const { APP_PORT } = require('./config/config.default.js');
const { app } = require('./app/index.js');

// 之后的其他配置都写在这里 ​
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(APP_PORT, function () {
  console.log(`server is running http://localhost:${APP_PORT}`);
});

//app/index.js创建应用实例,引入定义的接口router
const express = require('express');
const cors = require('cors');
const { router } = require('../router/api.js');

// 创建 express 的服务器实例

const app = express();
app.use(cors());
app.use(express.json());

app.use(router);

exports.app = app;
//router/api.js下定义接口getApi,引入接口实现函数apiHandler
const express = require('express');
const router = express.Router();
// 导入用户路由处理函数模块
const { apiHandler } = require('../router_handler/apiHandler.js');

// 处理登录请求的映射关系
router.post('/getApi', apiHandler);

exports.router = router;

//接口实现
//引入superagent用于爬取资源
//引入cheerio用于解析资源
//引入proxySuperagent用于配置代理(如果配置代理,在打包时必须必须手动写入 bridge.js 及 setup-sandbox.js 否则使用 esbuid 打包时不会引入,导致开发模式正常,打包后编译失败)
//抛出处理函数apiHandler
//接口内部最终返回处理好的信息
const superagent = require('superagent');
const cheerio = require('cheerio');
const proxySuperagent = require('superagent-proxy');

proxySuperagent(superagent);
// const weiboURL = "http://wiki.ziroom.com/dologin.action";  // 域名
// const hotSearchURL = weiboURL + "";

let cookie = null;

// 配置代理 /
let proxyConfig = {
  URL: 'http://127.0.0.1:80',
};
// 登录请求的处理函数
exports.apiHandler = async (req, res) => {
  console.log(req.body, 'req');
  const { url, cookie, type, gateway = null } = req.body;
  let apiJson = await getApiJsonFn(url, cookie, type, gateway);
  res.send(apiJson);
};

const getApiJsonFn = (url, cookie, type, gateway) => {
  // 数据处理方法
  return new Promise((resolve, reject) => {
    superagent
      .get(url)
      .set('Content-Type', 'text/html;charset=UTF-8')
      .set('Cookie', cookie)
      .set('host', '127.0.0.1')
      .proxy(proxyConfig.URL)
      .end((err, res) => {
        // console.log(res, '=============================================================');
        if (err) console.error('e请求失败', err);
        const content = res.text;
        let $ = cheerio.load(content);
        const _json = analysisFn($, type, gateway);
        resolve(_json);
      });
  });
};

const analysisFn = ($, type, gateway) => {
  //解析函数,书写自己的解析器内容
  //const content = $('#main');
 
  //返回解析完成的数据
  return result;
 
};

打包及启动配置

    //配置package.json下启动脚本
    "scripts": {
    "start": "node src/main.js",
    "build": "node bin/build.js",
    "serve": "node lib/index.js"
  },
const esbuild = require('esbuild');
const fs = require('fs-extra');
const path = require('path');
const join = (...paths) => path.join(__dirname, ...paths);
// const npm = exec('npm');

const outfile = 'lib/index.js';

(async function () {
  const builded = await esbuild.build({
    outfile,
    platform: 'node',
    entryPoints: ['src/main.js'],
    logLevel: 'silent',
    format: 'cjs',
    bundle: true,
    // minify: true,
    define: {
      'process.env.NODE_ENV': '"production"',
    },
  });
   /*!!!如果配置了代理,必须手动写入这两个文件,这两个文件为依赖包中的同名文件*/
  fs.copyFileSync(join('./bridge.js'), join('../lib', 'bridge.js'));
  fs.copyFileSync(join('./setup-sandbox.js'), join('../lib', 'setup-sandbox.js'));

var lemonadeChange = function(bills) {
    let map=new Map()
    for(let i in bills){
       switch(bills[i]){
           case 5:
            mapAdd(map,5)
            break
           case 10:
            if(map.has(5) && map.get(5)>=0){
                mapAdd(map,10)
                map.set(5,map.get(5)-1)
            }else{
                return false
            }
            break
           case 20:
            if(map.has(10)&&map.get(10)>=0){
                if(map.has(5)&&map.get(5)>=0){
                    map.set(10,map.get(10)-1)
                    map.set(5,map.get(5)-1)
                }else{
                    return false
                }
            }else{
                 if(map.has(5)&&map.get(5)>=2){
                     map.set(5,map.get(5)-3)
                 }else{
                     return false
                 }
            }
            break
            

       }
    }
    return true
};
const mapAdd=(map,i)=>{
    if(map.has(i)){
        map.set(i,map.get(i)+1)
    }else{
       map.set(i,0)
    }
}
====额外维护了一个map对象,可以不维护map,直接统计5美元和10美元数量