現(xiàn)在是大數(shù)據(jù)的時(shí)代,網(wǎng)絡(luò)上現(xiàn)成的數(shù)據(jù)都在那里,就看你怎么樣來(lái)利用,網(wǎng)絡(luò)爬蟲(chóng),最適合來(lái)抓取我們需要的數(shù)據(jù)。
那用nodejs來(lái)完成整個(gè)爬蟲(chóng)我們需要哪些模塊和技術(shù)呢
1.request模塊
request是一個(gè)用來(lái)簡(jiǎn)化HTTP請(qǐng)求操作的模塊,其功能強(qiáng)大而且使用方法簡(jiǎn)單
具體用法可以參考
http://blog.youlunshidai.com/post?id=91
2.cheerio模塊
相信用nodejs做過(guò)網(wǎng)絡(luò)爬蟲(chóng)的小伙伴們都知道cheerio這個(gè)模塊,是的,他是jQuery Core的子集,其實(shí)現(xiàn)了jQuery Core中瀏覽器無(wú)關(guān)的DOM操作API,一般通過(guò)cheerio.load方法來(lái)加載我們通過(guò)http請(qǐng)求到的網(wǎng)頁(yè)內(nèi)容,然后進(jìn)行DOM操作,抓取我們需要的數(shù)據(jù)。
需要注意的是,cheerio并不支持所有jQuery的查詢語(yǔ)法
比如$('a:first')會(huì)報(bào)錯(cuò),只能寫成$('a').first()
具體的使用方法可以通過(guò)cheerio模塊的主頁(yè)來(lái)獲取詳細(xì)的使用說(shuō)明
https://npmjs.org/package/cheerio
3.async模塊
async是一個(gè)使用比較廣泛的JS異步流程控制模塊,除了可以在Nodejs上運(yùn)行,也可以在瀏覽器端運(yùn)行,主要用于處理Nodejs過(guò)程中各種復(fù)雜的回調(diào)
在這里我們主要用到了async的eachSeries方法,他主要用來(lái)異步操作里的串行操作,當(dāng)我們希望在異步流程里上一個(gè)操作執(zhí)行完之后再執(zhí)行下一個(gè),而不是同時(shí)執(zhí)行的時(shí)候,我們就可以通過(guò)eachSeries來(lái)循環(huán)執(zhí)行所有的異步操作。
具體的使用方法可以通過(guò)async模塊的主頁(yè)來(lái)獲取詳細(xì)的使用說(shuō)明
https://npmjs.org/package/async
4.mysql模塊
mysql是Nodejs下比較有名的一個(gè)MySQL操作模塊,我們需要用到這個(gè)模塊來(lái)把我們抓取到的數(shù)據(jù)集存儲(chǔ)到mysql數(shù)據(jù)庫(kù)中
具體的使用方法可以通過(guò)mysql模塊的主頁(yè)來(lái)獲取詳細(xì)的使用說(shuō)明
https://github.com/felixge/node-mysql
最后分享一個(gè)我項(xiàng)目中的一個(gè)完整的網(wǎng)絡(luò)爬蟲(chóng)源碼
var request = require('request');
var cheerio = require('cheerio');
var async = require('async');
var mysql = require('../models/db');
exports.get = function() {
var domino = 'http://www.tapotiexie.com';
var url = 'http://www.tapotiexie.com/Line/index/name/yt/p/';
var txtSource = '踏破鐵鞋';
var url_list = [];
var list = [];
for (var i = 1; i < 25; i++) {
url_list.push(url + i + '.html');
}
async.eachSeries(url_list, function(arr_url, callback) {
console.log('正在抓取' + arr_url + '的數(shù)據(jù)...');
request(arr_url, function(err, data) {
if (err) return console.error(err);
var $ = cheerio.load(data.body.toString());
$('.tptx_ytgt .tptx_ytgt_4').each(function() {
var $me = $(this);
//解析船公司和船字段
var arr1 = analyStr($me.find('.tptx_ytgt_2b a').text());
//解析晚和天
var arr2 = analyDay($me.find('.tptx_jcyj_2ab_1 span').text());
var item = {
txtCompany: arr1[0],
txtCruise: arr1[1],
txtLine: analyLine($me.find('.tptx_jcyj_2ab_1 ul li:first-child').text()),
txtStartDate: analyStart($me.find('.tptx_jcyj_2ab_1 li').eq(1).text(), $me.find('.tptx_jcyj_2ab_1 span').text()),
numDay: Number(arr2[1]),
numNight: Number(arr2[0]),
numPrice: analyPrice($me.find('.tptx_jcyj_2ac .tptx_jcyj_2ac_1').text()),
txtUrl: domino + $me.find('.tptx_ytgt_2b a').attr('href')
};
list.push(item);
});
callback(err, list);
});
}, function(err) {
if (err) return console.error(err.stack);
/*寫入數(shù)據(jù)庫(kù)*/
async.eachSeries(list, function(record, callback) {
console.log('正在寫入' + record.txtStartDate + '的數(shù)據(jù)...');
var sql = 'insert into ...';
mysql.query(sql, function(err, info) {
if (err) return console.error(err.stack);
if (info.affectedRows == 1) {
callback(err);
}
});
}, function(err) {
if (err) return console.error(err.stack);
console.log('處理成功!');
});
});
}
/*按中間的空格解析字符串*/
function analyStr(str) {
var _newStr = '';
for (i in str) {
if (str[i].trim() == '') {
_newStr += '|';
} else {
_newStr += str[i];
}
}
return _newStr.split('||');
}
/*解析航線*/
function analyLine(str) {
var arr1 = str.split("郵輪線路:");
return arr1[1];
}
/*解析價(jià)格*/
function analyPrice(str) {
if (str == '售罄') {
return 0;
} else {
var arr1 = str.split("¥");
var arr2 = arr1[1].split("起");
return Number(arr2[0]);
}
}
/*解析晚和天*/
function analyDay(str) {
var arr1 = str.split('晚');
var arr2 = arr1[1].split('天');
var _newStr = arr1[0] + '|' + arr2[0];
return _newStr.split('|');
}
/*解析出發(fā)日期*/
function analyStart(str1, str2) {
var arr1 = str1.split(str2);
var arr2 = arr1[0].split('出發(fā)時(shí)間:');
return arr2[1];
}
一般的網(wǎng)頁(yè)文章列表都會(huì)采用翻頁(yè)來(lái)實(shí)現(xiàn),那么怎么樣獲取所有的每一頁(yè)的數(shù)據(jù)呢
一般可以通過(guò)抓取“下一頁(yè)”按鈕來(lái)判斷是不是有下一頁(yè)數(shù)據(jù),如果有的話,直接讀取下一頁(yè)鏈接的href屬性