前言
我一直觉得,爬虫是许多web开发人员难以回避的点。我们也应该或多或少的去接触这方面,因为可以从爬虫中学习到web开发中应当掌握的一些基本知识。而且,它还很有趣。
所以让我们用nodejs写一个简单的爬虫吧!
robots协议
robots协议也叫做robots.txt, 是一种存放于网站根目录下的文本文件, 它的作用是告诉网络搜索引擎,此网站中的哪些内容不应被搜索引擎的漫游器获取的,哪些是可以被漫游器获取的。
robots协议并不是一个规范,而只是约定俗成的,所以并不能保证网站的隐私。
Robots协议是国际互联网界通行的道德规范,基于以下原则建立:
搜索技术应服务于人类,同时尊重信息提供者的意愿,并维护其隐私权;
网站有义务保护其使用者的个人信息和隐私不被侵犯。
基础依赖
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("定时器已开启")
最后
点赞你我他,幸福靠大家!
1条评论