前言

我一直觉得,爬虫是许多web开发人员难以回避的点。我们也应该或多或少的去接触这方面,因为可以从爬虫中学习到web开发中应当掌握的一些基本知识。而且,它还很有趣。

所以让我们用nodejs写一个简单的爬虫吧!

robots协议

robots协议也叫做robots.txt, 是一种存放于网站根目录下的文本文件, 它的作用是告诉网络搜索引擎,此网站中的哪些内容不应被搜索引擎的漫游器获取的,哪些是可以被漫游器获取的。

robots协议并不是一个规范,而只是约定俗成的,所以并不能保证网站的隐私。

Robots协议是国际互联网界通行的道德规范,基于以下原则建立:

  1. 搜索技术应服务于人类,同时尊重信息提供者的意愿,并维护其隐私权;

  2. 网站有义务保护其使用者的个人信息和隐私不被侵犯。

基础依赖


yarn add crawler

yarn add cheerio

yarn add node-schedule

核心代码


const Crawler = require('crawler');

const cheerio = require('cheerio');

const config = require('./config');




const startTime = new Date().getTime();

const sleep = ms => {

return new Promise(resolve => setTimeout(resolve, ms));

};



const crawl = new Crawler(

Object.assign(config.option, {

async callback(error, res, done) {

if (error) {

console.log(error);

return;

}



const $ = cheerio.load(res.body, { decodeEntities: false });

const products = $('#result .goods');

const goods = [];



// 过滤数据

products.each(item => {

const cur = {

name: item.name,

};

goods.push(cur);

})



// 获取其他数据整合

const getExtra = async data => {

await Promise.all(

data.map(async item => {

await sleep(Math.floor(10 + Math.random() * (1000 - 10)));

item.extra = await fetchDetail(

platformURL + '/product/' + item.goodsId + '.html',

handleDetail

);

})

);

};



await getExtra(goods);



await goods.forEach(item => {

const keys = [];

const values = [];

for (let key in item) {

keys.push(key);

if (item[key] instanceof Object) {

values.push(JSON.stringify(item[key]));

} else {

values.push(item[key]);

}

}

// 放到需要的地方

insert(keys, [values]);

});



await done();

},

})

);



crawl.on('drain', () => {

console.log(`------------------------------`);

console.log(`任务已完成,耗时:【${new Date().getTime() - startTime}ms】`);

console.log(`------------------------------`);

});



const start = () => {

crawl.queue(config.uri);

};



module.exports = start;

IP 代理池


const request = require('request')

const cheerio = require('cheerio')

const fs = require('fs')



var proxys = []

var useful = []



// 过滤无效的代理

function check () {

// 尝试请求百度的静态资源

const url = 'http://apps.bdimg.com/libs/underscore.js/1.7.0/underscore-min.js'

const flag = proxys.length // 检查是否所有异步函数都执行完的标志量

for (var i = 0; i < proxys.length; i++) {

var proxy = proxys[i]

request({

url: url,

proxy: 'http://' + proxy['ip'] + ':' + proxy['port'],

method: 'GET',

timeout: 2000 // 20s没有返回则视为代理不行

}, function (error, response, body) {

if (!error) {

if (response.statusCode == 200) {

// 这里因为nodejs的异步特性,不能push(proxy),那样会存的都是最后一个

useful.push(response.request['proxy']['href'])

console.log(response.request['proxy']['href'], 'useful!')

} else {

console.log(response.request['proxy']['href'], 'failed!')

}

}

flag--

if (flag == 0) {

saveProxys()

}

})

}

}



function saveProxys () {

console.log('write in ./proxys.json')

fs.writeFileSync('./proxys.json', JSON.stringify(useful))

}



function getProxy () {

// 嗯。。。。你懂的

const url = 'http://www.xicidaili.com/nn'



request({

url: url,

method: 'GET',

headers: {

'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36'

}

}, function (error, response, body) {

if (!error) {

const $ = cheerio.load(body)

var trs = $('#ip_list tr')

for (let i = 1; i < trs.length; i++) {

const proxy = {}

tr = trs.eq(i)

tds = tr.children('td')

proxy['ip'] = tds.eq(1).text()

proxy['port'] = tds.eq(2).text()

const speed = tds.eq(6).children('div').attr('title')

speed = speed.substring(0, speed.length - 1)

const connectTime = tds.eq(7).children('div').attr('title')

connectTime = connectTime.substring(0, connectTime.length - 1)

if (speed <= 5 && connectTime <= 1) {

proxys.push(proxy)

}

}

}

check()

})

}



getProxy()

定时任务


const schedule = require('node-schedule')

const job1 = require('./app1.js')

const job2 = require('./app2.js')



let counter = 1

schedule.scheduleJob('30 35 17 * * *', () => {

console.log('到点爬东西了:' + new Date()+"定时器触发次数:"+counter)

counter++

job1()

})

schedule.scheduleJob('30 8 19 * * *', () => {

console.log('到点爬东西了:' + new Date()+"定时器触发次数:"+counter)

counter++

job1()

})

console.log("定时器已开启")

最后

点赞你我他,幸福靠大家!