使用 Node 抓取指定页面的有效链接,并输出所有非有效链接

1. Node.js 安装配置:

http://www.runoob.com/nodejs/nodejs-install-setup.html

2. 安装需要的相关包

执行命令: npm i cheerio async colors

什么是cheerio、async、colors?

  • cheerio 是nodejs特别为服务端定制的,可以快速灵活的对JQuery核心进行实现。快速抓取页面,它工作于DOM模型上,且解析、操作、呈送都很高效。
    https://www.npmjs.com/package/cheerio
  • Async是一个实用程序模块,它为使用异步JavaScript提供了直观,强大的功能。虽然最初设计用于Node.js并可安装npm install --save async,但也可以直接在浏览器中使用。
    https://www.npmjs.com/package/async

3.开始编码啦

新建xxx.js文件内容:

// 引入需要的包
var
  http = null,
  fs = require('fs'),
  cheerio = require('cheerio'),
  parse = require('url').parse,
  async = require('async')

require('colors')

// 接收抓取页面的URL 如果不指定默认抓取http://jeffjade.com/2016/03/30/104-front-end-tutorial/上的所有链接
var
  TARGET_PATH = process.argv[2] || 'http://jeffjade.com/2016/03/30/104-front-end-tutorial/',
  TIMEOUT_VALUE = 45000 // 指定超时的时间45秒

// 入口方法:调用main方法传入指定页面的地址
mian(TARGET_PATH)
function mian (targetUrl) {
  var info = parse(targetUrl)
  http = info.protocol.indexOf('https') > -1 ? require('https') : require('http')
  console.log('>> Start crawling all links ...'.green)

  // 调用download开始下载targetUrl页面啦, function (data) :指定下载页面成功后的回调
  download(targetUrl, function (data) {
    if (data) {
      console.log('Well done! Grab all the links work has been completed!'.green)
      
      // 引入我们要解析的html
      var $ = cheerio.load(data),
        saveGrabbingLinkArr = []
        
      // 找到所有的a标签
      $('body a').each(function (i, e) {
        var aTagsVal = $(e).attr('href')
        if (!!aTagsVal) {
          let _aTagsVal = ''

          if (aTagsVal.indexOf('http://') === 0 || aTagsVal.indexOf('https://') === 0) {
            _aTagsVal = aTagsVal
          } else if (aTagsVal.indexOf('/') > -1) {
            _aTagsVal = info.protocol + '//' + info.host + aTagsVal
          } else {
            _aTagsVal = ''
          }
          _aTagsVal && !saveGrabbingLinkArr.includes(_aTagsVal) && saveGrabbingLinkArr.push(_aTagsVal)
        }
      })
      console.log('>> Start handle these links(Eg: Duplicate removal,Make the path complete) ...'.cyan)
      console.log('>> Start analyzing the effectiveness of all links ...'.green)
      
      // 过滤链接 参数:页面上的所有链接
      filterInvalidLinks(saveGrabbingLinkArr)
    }
  })
}
// // 根据指定URL下载页面,回调函数返回下载页面(string类型)
function download (url, callback) {
  http.get(url, function (res) {
    var data = ''

    res.on('data', function (chunk) {
      data += chunk
    })

    res.on('end', function () {
      callback(data)
    })
  }).on('error', function (err) {
    console.log('Opps, Download Error Occurred !'.red)
    console.log(err)
  })
}

// 以异步的方式遍历检查传入的所有链接
function filterInvalidLinks (needFilterList) {
  async.map(needFilterList, function(item, _callback) {
   
    // 具体检查实现
    requestUrl(item, _callback)
  }, function(err, results) {
      callback(err, results)
  })
}
// 以GET方法请求URL成功后调用CallBack
function requestUrl (url, callback) {
  var info = parse(url),
    path = info.pathname + (info.search || ''),
    options = {
      host: info.hostname,
      port: info.port || 80,
      path: path,
      method: 'GET'
    },
    req = null,
    request_timeout = null

  request_timeout = setTimeout(function () {
    request_timeout = null
    req.abort()
    callback(new Error('Request timeout'), url)
  }, TIMEOUT_VALUE)

  req = http.request(options, function (res) {
    clearTimeout(request_timeout)
    var chunks = [],
      length = 0
    res.on('data', function (chunk) {
      length += chunk.length
      chunks.push(chunk)
    }).on('end', function () {
      var data = new Buffer(length)
      for (var i = 0, pos = 0, l = chunks.length; i < l; i++) {
        chunks[i].copy(data, pos)
        pos += chunks[i].length
      }
      res.body = data
      callback(null, 'normal-link')
    }).on('error', function (err) {
      callback(err, url)
    })
  }).on('error', function (err) {
    // node0.5.x及以上,调用req.abort()会触发一次“socket hang up” error;
    // 所以需要判断是否超时,如果是超时,则无需再回调异常结果
    if (request_timeout) {
      clearTimeout(request_timeout)
      callback(err, url)
    }
  })
  req.end()
}


function callback (err, errUrlArr) {
  if (errUrlArr.length <= 0) {
    console.log('Nice, All links in the page are accessible. '.green)
    return
  }

  console.log('These are invalid links (Maybe for you to shield):'.yellow)
  
  // 以红色打印非有效链接到控制台
  var invalidUrlList = errUrlArr.filter(item => {
    if (item !== "normal-link") {
      console.log(item.red)
      return item
    }
  })
  
  outPrint(invalidUrlList)
}
// 输出非有效链接到err_url_list.json
function outPrint (resData) {
  var filepath = './err_url_list.json'
  var resJson = JSON.stringify(resData, null, 2)
  fs.writeFile(filepath, resJson, function (e) {
    if (e) throw e
  })
}

4.使用

node xxx.js http://jeffjade.com/

源码地址:https://github.com/nicejade/nice-jade-collecting/blob/master/scripts/crawl_page-invalid_link.js

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容