在node.js中,有了 cheerio 模塊、request 模塊,抓取特定URL頁面的數據已經非常方便。
一個簡單的就如下
var request = require('request'); var cheerio = require('cheerio'); request(url,function(err,res){ if(err) return console.log(err); var $ = cheerio.load(res.body.toString()); //解析頁面內容 });
有了基本的流程,現在找個web地址(url)試試。就以博客園的搜索頁為例。
通過搜索關鍵詞 node.js
得到如下的URL:
http://zzk.cnblogs.com/s?t=b&w=node.js
點擊第二頁,URL如下:
http://zzk.cnblogs.com/s?t=b&w=node.js&p=2
分析URL,發現w= ?為要搜索的關鍵詞 p= ?為頁碼。
借助 request 模塊請求URL
var request = require('request'); var cheerio = require('cheerio'); var key = 'node.js', page = 1; var url = 'http://zzk.cnblogs.com/s?t=b&w='+ key +'&p='+ page; request(url, function(err, res) { if (err) return console.log(err); var $ = cheerio.load(res.body.toString()); var arr = []; //內容解析 });
現在URL有了,分析下URL對應的頁面內容。
頁面還是很有規律的。
標題 摘要 作者 發布時間 推薦次數 評論條數 瀏覽次數 文章鏈接
借助瀏覽器開發工具
發現 <div class="searchItem">...</div> 對應的是每篇文章
點開每一項,有如下內容
class="searchItemTitle" 包含的是文章標題、也包含了文章URL地址
class="searchItemInfo-userName" 包含的是作者
class="searchItemInfo-publishDate" 包含的是發布時間
class="searchItemInfo-views" 包含的是瀏覽次數
借助 cheerio 模塊解析文章,抓取具體的內容

var request = require('request'); var cheerio = require('cheerio'); var key = 'node.js', page = 1; var url = 'http://zzk.cnblogs.com/s?t=b&w='+ key +'&p='+ page; request(url, function(err, res) { if (err) return console.log(err); var $ = cheerio.load(res.body.toString()); var arr = []; //內容解析 $('.searchItem').each(function() { var title = $(this).find('.searchItemTitle'); var author = $(this).find('.searchItemInfo-userName a'); var time = $(this).find('.searchItemInfo-publishDate'); var view = $(this).find('.searchItemInfo-views'); var info = { title: $(title).find('a').text(), href: $(title).find('a').attr('href'), author: $(author).text(), time: $(time).text(), view: $(view).text().replace(/[^0-9]/ig, '') }; arr.push(info); //打印 console.log('============================= 輸出開始 ============================='); console.log(info); console.log('============================= 輸出結束 ============================='); }); });
可以來運行下,看看是否正常抓取了數據。
現在有數據數據,可以保存到數據庫。這里以mysql為例,其實用mongodb更方便。
借助 mysql 模塊保存數據(假設數據庫名為test,表為blog)。

var request = require('request'); var cheerio = require('cheerio'); var mysql = require('mysql'); var db = mysql.createConnection({ host: '127.0.0.1', user: 'root', password: '123456', database: 'test' }); db.connect(); var key = 'node.js', page = 1; var url = 'http://zzk.cnblogs.com/s?t=b&w='+ key +'&p='+ page; request(url, function(err, res) { if (err) return console.log(err); var $ = cheerio.load(res.body.toString()); var arr = []; //內容解析 $('.searchItem').each(function() { var title = $(this).find('.searchItemTitle'); var author = $(this).find('.searchItemInfo-userName a'); var time = $(this).find('.searchItemInfo-publishDate'); var view = $(this).find('.searchItemInfo-views'); var info = { title: $(title).find('a').text(), href: $(title).find('a').attr('href'), author: $(author).text(), time: $(time).text(), view: $(view).text().replace(/[^0-9]/ig, '') }; arr.push(info); //打印 console.log('============================= 輸出開始 ============================='); console.log(info); console.log('============================= 輸出結束 ============================='); //保存數據 db.query('insert into blog set ?', info, function(err,result){ if (err) throw err; if (!!result) { console.log('插入成功'); console.log(result.insertId); } else { console.log('插入失敗'); } }); }); });
運行下,看看數據是否保存到數據庫了。
現在一個基本的抓取、保存都有了。但是呢 只抓取一次,而且只能抓取關鍵詞為node.js 頁碼為1的URL頁面。
改關鍵詞為javascript,頁碼為1,清空blog表,再從新運行一次,看看表里是不是能保存javascript相關的數據。
現在去博客園搜索javascript,看看搜索到的結果和表里的內容能否對應。呵呵,不用看啦,肯定能對應啊~~
只能抓取一個頁面的內容,肯定不能滿足的,能自動抓取其他頁的內容就更好了。
分析搜索頁面,底部都有頁碼、下一頁。
借助瀏覽器開發工具查看
我們發現最后一個a標簽的內容有Next,表示下一頁,看href有p=2,在第二分頁p=3, ... 最后一頁沒有內容有Next的a標簽了。
var nextA = $('.pager a').last(), nextUrl = ''; if ($(nextA).text().indexOf('Next') > -1) { nextUrl = nextA.attr('href'); page = nextUrl.slice(nextUrl.indexOf('p=') + 2); //todo } else { db.end(); console.log('沒有數據了...'); }
這里把程序代碼做一點修改,封裝成一個函數,完整如下:

var request = require('request'); var cheerio = require('cheerio'); var mysql = require('mysql'); var db = mysql.createConnection({ host: '127.0.0.1', user: 'root', password: '123456', database: 'test' }); db.connect(); function fetchData(key, page) { var url = 'http://zzk.cnblogs.com/s?t=b&w=' + key + '&p=' + page; request(url, function(err, res) { if (err) return console.log(err); var $ = cheerio.load(res.body.toString()); var arr = []; //內容解析 $('.searchItem').each(function() { var title = $(this).find('.searchItemTitle'); var author = $(this).find('.searchItemInfo-userName a'); var time = $(this).find('.searchItemInfo-publishDate'); var view = $(this).find('.searchItemInfo-views'); var info = { title: $(title).find('a').text(), href: $(title).find('a').attr('href'), author: $(author).text(), time: $(time).text(), view: $(view).text().replace(/[^0-9]/ig, '') }; arr.push(info); //打印 console.log('============================= 輸出開始 ============================='); console.log(info); console.log('============================= 輸出結束 ============================='); //保存數據 db.query('insert into blog set ?', info, function(err, result) { if (err) throw err; if (!!result) { console.log('插入成功'); console.log(result.insertId); } else { console.log('插入失敗'); } }); }); //下一頁 var nextA = $('.pager a').last(), nextUrl = ''; if ($(nextA).text().indexOf('Next') > -1) { nextUrl = nextA.attr('href'); page = nextUrl.slice(nextUrl.indexOf('p=') + 2); setTimeout(function() { fetchData(key, page); }, 2000); } else { db.end(); console.log('沒有數據了...'); } }); } fetchData('node.js', 1);
運行一下,開始抓數據了... 博客園搜索結果100個分頁,每頁20條數據,供2000條,程序間隔2秒抓取下一頁,抓取一個搜索關鍵詞2000條數據約3分20秒左右。
到此程序實現了抓取、保存數據。
如果是其他URL,需要重新解析頁面內容,頁面編碼不是utf-8編碼,需要轉碼,可以借助 iconv-lite 模塊。
數據庫有了數據,當然可以讀取出來,比如按瀏覽次數多少排序輸出來。
按需求,抓取數據,顯示數據,助技術進步。