用 Node 抓站(一):怎么写出本身满意的代码

若是只写怎么抓取网页,确定会被吐槽太水,知足不了读者的逼格要求,因此本文会经过不断的审视代码,作到令本身满意(撸码也要不断迸发新想法!html

本文目标:抓取什么值得买网站国内优惠的最新商品,而且做为对象输出出来,方便后续入库等操做git

抓取经常使用到的npm模块

本文就介绍两个:requestcheerio,另外lodash是个工具库,不作介绍,后面篇幅会继续介绍其余用到的npm库。github

  • request:是一个http请求库,封装了不少经常使用的配置,并且也有promise版本(还有next版本。
  • cheerio:是一个相似jQuery的库,能够将html String转成相似jQ的对象,增长jQ的操做方法(实际是htmlparser2

request 示例

var request = require('request');
request('http://www.smzdm.com/youhui/', (err, req)=>{
  if(!err){
    console.log(Object.keys(req))
  }
})复制代码

经过上面的代码就看到req实际是个response对象,包括headersstatusCodebody 等,咱们用body就是网站的html内容npm

cheerio 示例

var request = require('request')
var cheerio = require('cheerio')

cheerio.prototype.removeTagText = function () {
  var html = this.html()
  return html.replace(/<([\w\d]+)\b[^<]+?<\/\1>/g, (m) => {
    return ''
  })
}
request('http://www.smzdm.com/youhui/', (err, req) => {
  if (!err) {
    var body = req.body
    var $ = cheerio.load(body, {
      decodeEntities: false
    })
    $('.list.list_preferential').each((i, item) => {
      var $title = $('.itemName a', item)
      var url = $title.attr('href')
      var title = $title.removeTagText().trim()

      var hl = $title.children().text().trim()
      var img = $('img', item).attr('src')
      var desc = $('.lrInfo', item).html().trim()
      desc = desc.replace(/<a\b.+?>阅读全文<\/a>/g, '')
      var mall = $('.botPart a.mall', item).text().trim()

      console.log({title, hl, url, img, desc, mall})
    })
  }
})复制代码

简单解释下,removeTagText是直接扩展了cheerio的一个方法,目的是去掉相似json

再特价:QuanU 全友 布艺沙发组合<span class="z-highlight">2798元包邮(需定金99元,3.1付尾款)</span>复制代码

里面span以后的文字。执行后获得下面的结果:promise

怎么写出本身满意的代码

从上面需求来看,只须要提取列表页面的商品信息,而取到数据以后,使用cheerio进行了解析,而后经过一些「选择器」对数据进行「提取加工」,获得想要的数据。数据结构

重点是选择器提取加工,若是想要的字段多了,那么代码会越写越多,维护困难,最重要的是「不环保」,今天抓什么值得买,明天抓惠惠网,代码还要copy一份改一改!一来二去,抓的越多,那么代码越乱,想一想哪天不用request了,是否是要挨个修改呢?因此要抓重点,从最后须要的数据结构入手,关注选择器提取加工ide

handlerMap

从最后须要的数据结构入手,关注选择器提取加工。我设计一种对象结构,做为参数传入,这个参数我起名:handlerMap,最后实现一个spider的函数,用法以下:函数

spider(url, callback, handlerMap)复制代码

从目标数据结构出发,最后数据什么样子,那么handlerMap结构就是什么样子,key就是最后输出数据的key,是由selectorhandler两个key组成的对象,相似咱们须要最后产出的数据是:工具

[{
  title: '',
  ht: '',
  url: '',
  img: '',
  mall: '',
  desc: ''
}, {item2..}...]复制代码

那么须要的handlerMap就是:

{
  title: {
    selector: '.itemName a',
    handler: 'removeTagText'
  },
  ht: {
    selector: '.itemName a span',
    handler: 'text'
  },
  url: {
    selector: '.itemName a',
    handler: 'atrr:href'
  },
  img: {
    selector: 'img',
    handler: 'attr:src'
  },
  mall: {
    selector: '.botPart a.mall',
    handler: 'text'
  },
  desc: {
    selector: '.lrInfo',
    handler: function (data){
      return data.replace(/<a\b.+?>阅读全文<\/a>/g, '')
    }
  }
}复制代码

再酷一点,就是简写方法:url:".itemName a!attr:href”,另外再加上若是抓取的是JSON数据,也要一块儿处理的状况。通过分析以后,开始改造代码,代码最后分为了两个模块:

  • spider.js:包装request 模块,负责抓取页面将页面交给parser.js解析出来想要的数据
  • parser.js:负责解析handlerMap,同时支持json和html两种类型的页面进行解析

虽然增长很多代码工做量,可是抽象后的代码在使用的时候就更加方便了,本身仍是别人在使用的时候,不用关心代码实现,只须要关注抓取的页面url、要提取的页面内容和数据获得后的继续操做便可,使用起来要比以前混杂在一块儿的代码更加清晰简洁;而且抓取任意页面都不须要动核心的代码,只须要填写前面提到的handlerMap

总结

其实Node抓取页面很简单,本文只是经过一个简单的抓取任务,不断深刻思考,进行抽象,写出本身满意的代码,以小见大,但愿本文对读者有所启发😄

今天到此结束,完成一个基础抓取的库,有空继续介绍Node抓站的知识,欢迎你们交流讨论

本文的完整代码,在github/ksky521/mpdemo/ 对应文章名文件夹下能够找到

-eof-
@三水清
未经容许,请勿转载,不用打赏,喜欢请转发和关注

感受有用,欢迎关注个人公众号,每周一篇原创技术文章

关注三水清
相关文章
相关标签/搜索