Nodejs -- 使用koa2搭建數據爬蟲

當前爬蟲項目開發所需中間件:

  1. cheerio: 則可以對請求結果進行解析,解析方式和jquery的解析方式幾乎徹底相同 cheerio中文文檔 開發參考node - cheerio模塊css

  2. superagent: 可以實現主動發起get/post/delete等請求html

  3. superagent-charset: 解決爬蟲數據中文亂碼問題,早期版本單獨使用,現配合superagent使用node

  4. koa2: 搭建服務器環境等等mysql

  5. koa-router: koa路由,用於根據路由訪問對應代碼塊,邏輯編寫等做用(把他理解爲像平常API接口就好)jquery

  6. knex: 操做數據庫,支持多種數據庫,這裏使用mysql,須要mysql中間件 開發參考knex筆記git

搭建開發環境

  • 在項目根目錄下npm init一路回車,初始化項目環境,出現package.json文件,而後執行如下命令安裝項目依賴
npm i --save cheerio superagent superagent-charset koa-router koa knex mysql
  • 在項目根目錄下建立app.js文件,編寫coding
const Koa = require('koa'),
    Router = require('koa-router'),
    cheerio = require('cheerio'),
    charset = require('superagent-charset'),
    superagent = charset(require('superagent')),
    app = new Koa(),
    router = new Router();
  • 而後編寫路由和搭建服務器環境
router.get('/', function(ctx, next) {
    ctx.body = "搭建好了,開始吧";
});

app
    .use(router.routes())
    .use(router.allowedMethods());

app.listen(3010, () => {
    console.log('[服務已開啓,訪問地址爲:] http://127.0.0.1:3010/');
});
  • 啓動服務 node app.js , 打開瀏覽器http://127.0.0.1:3010/ 就能夠訪問了 如看到 搭建好了,開始吧 就意味着搭建環境success

爬蟲-目標網站

博雅特產網github

const Koa = require('koa'),
  Router = require('koa-router'),
  cheerio = require('cheerio'),
  charset = require('superagent-charset'),
  superagent = charset(require('superagent')),
  app = new Koa(),
  router = new Router();
let arr;

router.get('/', (ctx, next) => {
  url = 'http://shop.bytravel.cn/produce/index226.html'; //target地址
  superagent.get(url)
    .charset('gbk')  // 當前頁面編碼格式
    .buffer(true)
    .end((err, data) => { //頁面獲取到的數據
      if (err) {
        // return next(err); 
        console.log('頁面不存在', err)
      }
      let html = data.text,
        $ = cheerio.load(html, {
          decodeEntities: false,
          ignoreWhitespace: false,
          xmlMode: false,
          lowerCaseTags: false
        }), //用cheerio解析頁面數據
      obj = {};
      arr = [];
      // cheerio的使用相似jquery的操做
      $("table tbody").each((index, element) => {
        let $element = $(element);
        $element.find('#tctitle').next().find('a').addClass('link').attr('class', 'link').text('')
        arr.push({
          'title': $element.find('a.blue14b').text(),
          'image': $element.find('#bright img').attr('src'),
          'summary': $element.find('#tctitle').next().text(),
          'is_cgiia': $element.find('#tctitle font').attr('color') === 'green' ? 1 : 0
        })
      })
    })
  ctx.body = arr
  // console.log(arr)
})

app
  .use(router.routes())
  .use(router.allowedMethods());

app.listen(3010, () => {
  console.log('[服務已開啓,訪問地址爲:] http://127.0.0.1:3010/');
});

命令行重啓服務node app.js, 頁面出現一個數組,有大量數據,success;sql

  • 注: 若是出現亂碼,可能就是代碼的編碼格式和要抓取目標頁面的編碼格式不同致使的,須要留心下;

  • 當前目標網站編碼-- gb2312
  • 若是是utf-8 能夠 不使用 superagent-charset
superagent.get(url)
    .charset('gbk')  // 當前頁面編碼格式
    .buffer(true)
    .end(async (err, data) => { //頁面獲取到的數據
     ······
    })

分析頁面數據

  • 經過cheerio在服務器端須要對DOM進行操做解析頁面獲取數據
$("table tbody").each((index, element) => {
        let $element = $(element);
        $element.find('#tctitle').next().find('a').addClass('link').attr('class', 'link').text(''); //去掉簡介中連接接【詳情】
        arr.push({
            'title': $element.find('a.blue14b').text(),
            'image': $element.find('#bright img').attr('src'),
            'summary': $element.find('#tctitle').next().text(),
            'is_cgiia': $element.find('#tctitle font').attr('color') === 'green' ? 1 : 0
        })
    })

數據庫

sudo npm install knex mysql --save

當前自定義數據庫native_symbol,數據表 products數據庫

CREATE DATABASE native_symbol;
CREATE TABLE `products` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '編號',
  `title` varchar(100) NOT NULL COMMENT '名稱',
  `image` varchar(100) NOT NULL COMMENT '圖片',
  `summary` varchar(1000) NOT NULL COMMENT '簡介',
  `tags` varchar(100) DEFAULT NULL COMMENT '標籤',
  `is_cgiia` tinyint(1) DEFAULT NULL COMMENT '是不是地標特產',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8;
  • 初始化(knex配置鏈接)
var knex = require('knex')({
  client: 'mysql', //指明數據庫類型,還能夠是pg,sqlite3等等
  connection: { //指明鏈接參數
    host: '127.0.0.1',
    user: 'root',
    password: 'root',
    database: 'native_symbol'
  },
  debug: true, //指明是否開啓debug模式,默認爲true表示開啓
  pool: { //指明數據庫鏈接池的大小,默認爲{min: 2, max: 10}
    min: 0,
    max: 7,
  },
  acquireConnectionTimeout: 10000, //指明鏈接計時器大小,默認爲60000ms
  migrations: {
    tableName: 'migrations' //數據庫遷移,可選
  }
});

把數據庫類型和鏈接相關的參數配置好以後,才能夠正確的鏈接到數據庫,connection的配置信息一般寫到config文件中。npm

目前node開發服務端最優解決異步回調是 koa2 + es7(async/await)

  • 例如;向users表中寫入數據
// 寫入庫
    knex('users')
        .returning('id')
        .insert({
            name: 'charblus',
            age: 18,
            sex: 1
        })
        .then(res => {
           console.log('success', res)
        })
    }
$("table tbody").each((index, element) => {
        let $element = $(element);
        $element.find('#tctitle').next().find('a').addClass('link').attr('class', 'link').text('')
        arr.push({
        'title': $element.find('a.blue14b').text(),
        'image': $element.find('#bright img').attr('src'),
        'summary': $element.find('#tctitle').next().text(),
        'is_cgiia': $element.find('#tctitle font').attr('color') === 'green' ? 1 : 0
        })
    })

    for (let i of arr) {
        const findRes = await knex('products').select().where('title', i.title)
        if (findRes.length) {
        console.log('數據已存在')
        } else {
        // 寫入庫
        await knex('products')
            .returning('id')
            .insert(i)
            .then(res => {
            console.log('success', res)
            })
        }
    }

這裏讀寫數據庫是異步操做 使用async/await, 如上knex讀寫數據庫時都用了await,須要在當前函數前加async

  • 根據目標網站連接的特性,這裏加了個定時器,修改URL地址,並從新superagent請求數據,cheerio分析數據,knex存入數據

app.js

const Koa = require('koa'),
  Router = require('koa-router'),
  cheerio = require('cheerio'),
  charset = require('superagent-charset'),
  superagent = charset(require('superagent')),
  app = new Koa(),
  router = new Router();
let arr;

var knex = require('knex')({
  client: 'mysql', //指明數據庫類型,還能夠是pg,sqlite3等等
  connection: { //指明鏈接參數
    host: '127.0.0.1',
    user: 'root',
    password: 'root',
    database: 'native_symbol'
  },
  debug: true, //指明是否開啓debug模式,默認爲true表示開啓
  pool: { //指明數據庫鏈接池的大小,默認爲{min: 2, max: 10}
    min: 0,
    max: 7,
  },
  acquireConnectionTimeout: 10000, //指明鏈接計時器大小,默認爲60000ms
  migrations: {
    tableName: 'migrations' //數據庫遷移,可選
  }
});
var idx = 100;

router.get('/', (ctx, next) => {


  var timer = setInterval(() => {
    idx++;
    if (idx > 10000) {
      clearInterval(timer)
      return
    }
    url = `http://shop.bytravel.cn/produce/index${idx}.html`; //爬蟲地址
    timePlay(url)
    console.log('頁面抓包記錄', idx)
  }, 100);
  timePlay = (url) => {
    superagent.get(url)
      .charset('gbk')
      .buffer(true)
      .end(async (err, data) => { //頁面獲取到的數據
        // if (err) {
        //     // return next(err); 
        //     console.log('頁面不存在', err)
        // }
        let html = data.text,
          $ = cheerio.load(html, {
            decodeEntities: false,
            ignoreWhitespace: false,
            xmlMode: false,
            lowerCaseTags: false
          }), //用cheerio解析頁面數據
          obj = {};
        arr = [];

        $("table tbody").each((index, element) => {
          let $element = $(element);
          $element.find('#tctitle').next().find('a').addClass('link').attr('class', 'link').text('')
          arr.push({
            'title': $element.find('a.blue14b').text(),
            'image': $element.find('#bright img').attr('src'),
            'summary': $element.find('#tctitle').next().text(),
            'is_cgiia': $element.find('#tctitle font').attr('color') === 'green' ? 1 : 0
          })
        })

        for (let i of arr) {
          const findRes = await knex('products').select().where('title', i.title)
          if (findRes.length) {
            console.log('數據已存在')
          } else {
            // 寫入庫
            await knex('products')
              .returning('id')
              .insert(i)
              .then(res => {
                console.log('success', res)
              })
          }
        }
      });
  }
  ctx.body = arr;
  // console.log(arr)
});

app
  .use(router.routes())
  .use(router.allowedMethods());

app.listen(3010, () => {
  console.log('[服務已開啓,訪問地址爲:] http://127.0.0.1:3010/');
});
相關文章
相關標籤/搜索