网络爬虫是一种自动获取网页内容的程序,功能如下
一个简单的HTTP请求工具,用来获取网页内容
var request = require('request');
var iconv = require('iconv-lite');
request({url: 'http://top.baidu.com/category?c=10&fr=topindex'
, encoding: null},function(err,response,body){
if(err)
console.error(err);
body = iconv.decode(body, 'gbk').toString();
var regex = /<a href=".\/buzz\?b=\d+&c=\d+">.+<\/a>/g;
console.log(body.match(regex));
})
在服务器端实现了jQuery中的DOM操作API
var cheerio = require('cheerio');
var $ = cheerio.load('<ul>
<li><a href="./buzz?b=353&c=10">玄幻奇幻</a></li>
\<li><a href="./buzz?b=354&c=10">爱情</a></li></ul>');
$('ul li a').each(function () {
var $me = $(this);
var item = {
name: $me.text().trim(),
url: $me.attr('href').slice(2)
}
var result = item.url.match(/buzz\?b=(\d+)/);
if (Array.isArray(result)) {
item.id = result[1];
}
console.log(item);
});
用来周期性的执行某种任务或等待处理某些事件的一个守护进程
符号 | 含义 |
---|---|
星号(*) | 代表所有可能的值 |
逗号(,) | 可以用逗号隔开的值指定一个列表范围,例如,“1,2,5,7,8,9” |
中杠(-) | 可以用整数之间的中杠表示一个整数范围,例如“2-6”表示“2,3,4,5,6” |
正斜线(/) | 可以用正斜线指定时间的间隔频率,*/10,如果用在minute字段,表示每十分钟执行一次 |
var cronJob = require('cron').CronJob;
var job1 = new cronJob("* * * * * *",function(){
console.log('每秒');
});
job1.start();
根据环境变量的有选择向控制台输出调试信息
var debug = require('debug')('crawler:main');
//windows set DEBUG=crawler:*
//linux export DEBUG=crawler:*
debug('welcome to zhufengpeixun');
child_process即子进程可以创建一个系统子进程并执行shell命令
spawn语法
child_process.spawn(cmd, args=[], [options])
示例
var url = require('url');
var fs = require('fs');
var DOWNLOAD_DIR = './';
var spawn = require('child_process').spawn;
// 使用curl下载文件的函数
var download_file_curl = function(file_url) {
// 提取文件名
var file_name = url.parse(file_url).pathname.split('/').pop();
// 创建一个可写流的实例
var file = fs.createWriteStream(DOWNLOAD_DIR + file_name);
// 使用spawn运行curl
var curl = spawn('curl', [file_url]);
// 为spawn实例添加了一个data事件
curl.stdout.on('data', function(data) { file.write(data); });
// 添加一个end监听器来关闭文件流
curl.stdout.on('end', function(data) {
file.end();
console.log(file_name + ' downloaded to ' + DOWNLOAD_DIR);
});
// 当子进程退出时,检查是否有错误,同时关闭文件流
curl.on('exit', function(code) {
if (code != 0) {
console.log('Failed: ' + code);
}
});
};
download_file_curl('http://pic.baidu.jpg');
exec语法
child_process.exec(cmd, [options], callback)
示例
var url = require('url');
var fs = require('fs');
var DOWNLOAD_DIR = './';
var exec = require('child_process').exec;
var download_file_curl = function(file_url) {
// 提取文件名
var file_name = url.parse(file_url).pathname.split('/').pop();
// 使用exec执行curl命令
var child = exec('curl '+file_url+' -o '
+DOWNLOAD_DIR+file_name, function(err, stdout, stderr) {
if (err) throw err;
else console.log(file_name + ' downloaded to ' + DOWNLOAD_DIR);
});
};
download_file_curl('http://pic.baidu.jpg');
async是一个流程控制库,为我们带来了丰富的嵌套解决方案
串行执行一个函数数组中的每个函数 series(tasks,callback);
async.series([function(callback){
callback(null, "tv is over");
},function(callback){
callback(null, 'homework is down');
}],function(err, results) {
console.log(results);
});
parallel函数是并行执行多个函数,每个函数都是立即执行,不需要等待其它函数先执行 parallel(tasks, [callback])
console.time('start');
async.parallel([
function (callback) {
setTimeout(function () {
callback(null, 'one');
},2000);
},
function (callback) {
setTimeout(function () {
callback(null, 'two');
},3000);
}
],
function (err, results) {
console.log(results);
console.timeEnd('start');
});
waterfall和series函数有很多相似之处,都是按顺序依次执行一组函数,不同之处是waterfall每个函数产生的值,都将传给下一个函数 waterfall(tasks, [callback])
async.waterfall([function(callback){
callback(null, "水");
},function(data,callback){
callback(null, data+'+咖啡');
},function(data,callback){
callback(null, data+'+牛奶');
}],function(err, results) {
console.log(results);//水+咖啡+牛奶
});
用来处理有依赖关系的多个任务的执行 auto(tasks, [callback]);
async.auto({
getWater: function(callback){
callback(null, 'Water');
},
getFlour: function(callback){
callback(null, 'Flour');
},
mixFlour: ['getWater', 'getFlour', function(callback, results){
callback(null, results['getWater']+','
+results['getFlour']+','+'mixFlour');
}],
steam: ['mixFlour', function(callback, results){
callback(null, results['mixFlour']+',steam');
}]
}, function(err, results) {
console.log('err = ', err);
console.log('results = ', results);
});
注意callback, results的顺序在不同的
async
版本中不一样
所有的任务都迭代完成后才执行后续任务
var arr = [{name:'zfpx1'},{name:'zfpx2'}];
async.forEach(arr, function(item, callback) {
console.log('1.1 enter: ' + item.name);
setTimeout(function(){
console.log('1.1 handle: ' + item.name);
callback(null);
}, 1000);
}, function(err,result) {
console.log('1.1 err: ' + err);
});
tasks/read.js
var request = require('request');
var cheerio = require('cheerio');
var debug = require('debug')('crawler:read');
var iconv = require('iconv-lite');
module.exports = function (url, callback) {
var items = [];
debug('读取电影列表');
request({url:url,encoding:null},function(err,response,body){
body = iconv.decode(body,'gbk');
var $ = cheerio.load(body);
$('.keyword .list-title').each(function(){
var that = $(this);
var item = {
name:that.text().trim(),
url:that.attr('href')
}
items.push(item);
debug('读取电影 ',item);
});
callback(null,items);
debug('读取电影列表完毕');
});
}
model/index.js
var mongoose = require('mongoose');
mongoose.connect('mongodb://123.57.143.189/zhufengcrawl');
exports.Movie = mongoose.model('Movie',new mongoose.Schema({
name:String,
url:String
}));
tasks/save.js
var async = require('async');
var Movie = require('../model').Movie;
var debug = require('debug')('crawler:save');
module.exports = function(items,callback){
async.series([
function(callback){
debug('清空电影列表');
Movie.remove({},callback);
},
function(callback){
debug('保存电影列表');
async.forEach(items, function (item, next) {
debug('保存电影 ',item);
Movie.create(item,next);
}, callback);
}
],function(err,result){
debug('保存电影完毕');
callback();
})
}
tasks/main.js
var read = require('./read');
var save = require('./save');
var async = require('async');
var debug = require('debug')('crawler:main');
debug('开始计划任务');
var movies = [];
async.series([
function(done){
read('http://top.baidu.com/buzz?b=26&c=1&fr=topcategory_c1',function(err,items){
movies = items;
done();
});
},
function(done){
save(movies,done);
}
],function(err,result){
debug('结束计划任务');
process.exit(0);
})
app.js
var spawn = require('child_process').spawn;
var cronJob = require('cron').CronJob;
var path = require('path');
var job = new cronJob('* * * * * ',function(){
var update = spawn(process.execPath,[path.join(__dirname,'tasks/main.js')]);
update.stdout.pipe(process.stdout);
update.stderr.pipe(process.stderr);
update.on('close',function(code){
console.log(code);
});
update.on('error',function(code){
console.log(code);
});
});
job.start();
app.js
var express = require('express');
var app = express();
app.set('view engine', 'jade');
app.set('views', 'views');
var Movie = require('./model').Movie;
app.get('/', function (req, res) {
Movie.find({}, function (err, movies) {
res.render('index', {
movies: movies
});
});
});
app.listen(9090);
var spawn = require('child_process').spawn;
var cronJob = require('cron').CronJob;
var path = require('path');
var job = new cronJob('* * * * * ',function(){
var update = spawn(process.execPath,[path.join(__dirname,'tasks/main.js')]);
update.stdout.pipe(process.stdout);
update.stderr.pipe(process.stderr);
update.on('close',function(code){
console.log(code);
});
update.on('error',function(code){
console.log(code);
});
});
job.start();
doctype html
html
head
title 电影风云榜
link(rel="stylesheet",href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css")
body
.container
.panel.panel-primary
.panel-heading.text-center 电影风云榜
.panel-body
ul.list-group
each movie in movies
li.list-group-item
a(href="#{movie.url}")=movie.name