nodeJs小練習之爬取北京昌平區房價

  以前幾年總以爲北京房價再高也和我沒有什麼關係,總認爲租房子也不錯。其實仍是本身太年輕,無家無室的,too young too simple。今年交了女友,人很好,我很是喜歡她。可是逐漸的我發現,我喜歡她並不夠,至少在北京給她個穩定的住所都給不了。以前歷來沒有關注過北京的房價,這陣子在鏈家,安居客等網站上簡單瞭解了下,比我想象中的還貴。雖然這房價太離譜,簡直就是個笑話,現代人努力一生僅僅爲了一個能遮風避雨普普統統的住宅,但既然遊戲規則就如此,只能要麼忍,要麼滾。最終我決定忍,剩下的只能本身加倍努力,加倍付出,來爲咱們親愛的祖國添磚加瓦,爲實現現代化而奮鬥終生,呵呵。。。html

  迴歸正題,最近在學習nodeJs,想正好用學到的東西爬下北京昌平區的房價,而後導出到excel中來有個比較可視化的數據。node

  數據來源於安居客網站,準確性我就無從知道了,僅做參考。npm

 

  首先,運行此程序須要nodeJs開發環境,用npm安裝所需的依賴包,package.json文件以下:json

{
"name": "houseprice",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"cheerio": "^0.22.0",
"excel-export": "^0.5.1",
"node-schedule": "^1.2.0",
"nodemailer": "^2.6.4",
"request": "^2.75.0"
}
}

 

安裝好相關依賴後,新建入口文件index.js函數

首先定義變量,引入相關文件:學習

/**
* 北京昌平區實時二手房房價信息彙總
*/
var http=require('http')
var fs=require('fs')
var cheerio=require('cheerio')
var request=require('request')
var excelPort = require('excel-export');
var nodemailer=require('nodemailer')
var schedule=require('node-schedule')

var fileName='北京昌平區實時房價列表-'+new Date().toLocaleString().split(' ')[0];
var conf={};
var rows=[];
var marginPrice=320;


  其中,cheerio模塊用來將爬到的數據篩選出來,至關於node端的jQuery;excelPort模塊用來將數據導出到excel中,網上隨便找的一個,輕便,可是功能有限;
nodemailer模塊用來發郵件,我想程序跑完後主動給我發封郵件通知本身;schedule模塊是定時器功能,我想要天天定時跑一次任務,而後經過郵件通知我。
  安居客網站數據是分頁,大概60頁左右,每頁50條數據,總數據大約3000條左右。獲取每頁的url的函數以下:
  
/**
* 獲取指定頁面的url地址
* @param index
* @returns {string}
*/
function getPageUrl(index){
return 'http://beijing.anjuke.com/sale/changping/p'+index+'/#filtersort';
}



接下來是主要功能函數,從第一頁開始經過遞歸的方式循環調用,直到最後一頁。爬完數據以後導出到excel,而後發郵件。代碼以下:測試

/**
* 爬去內容並導出excel和發郵件通知
* @param url
* @param curPage
* @param marginPrice
*/
function fetch(url,curPage){
if(!marginPrice){
marginPrice=9999;
}
http.get(url, function (res) {
var html='';
res.setEncoding('utf-8');
res.on('data', function (chunk) {
html+=chunk;
})
res.on('end', function () {
var $=cheerio.load(html);
var list=$('#houselist-mod .list-item');

list.each(function (i,item) {
item=$(item);
var name=item.find('.houseListTitle').text();
var area=item.find('.details-item').eq(0).find('span').eq(0).text();
var totalPrice=item.find('.pro-price .price-det strong').text()*1;
var category=item.find('.details-item').eq(0).find('span').eq(1).text();
var price=item.find('.details-item').eq(0).find('span').eq(2).text();
var floor=item.find('.details-item').eq(0).find('span').eq(3).text();
var year=item.find('.details-item').eq(0).find('span').eq(4).text();
var position=item.find('.details-item').eq(1).find('span').text();

if(marginPrice>=totalPrice){
rows.push([name,area,totalPrice,category,price,floor,year,position])
}

});


if(curPage==1){
conf.cols=[
{caption:'名稱',type:'string',width:50},
{caption:'面積',type:'string',width:15},
{caption:'總價',type:'number',width:15},
{caption:'類別',type:'string',width:15},
{caption:'單價',type:'string',width:15},
{caption:'樓層',type:'string',width:15},
{caption:'年份',type:'string',width:15},
{caption:'位置',type:'string',width:60}
]
}
//下一頁
if($('.aNxt').attr('href')){
var nextPage=curPage+1;
fetch(getPageUrl(nextPage),nextPage,marginPrice);
}else{
conf.rows=rows;
var result=excelPort.execute(conf);
var genFilePath='result/'+fileName+".xlsx";
fs.writeFile(genFilePath,result,'binary', function (err) {
if(err){
console.log(err)
}else{
console.log('done!');
//發送郵件
sendMail();
}
})
}



})
})
}


schedule模塊功能挺多,支持cron表達式,能知足大部分需求,我這裏僅僅爲了測試功能是否好用,代碼以下:
/**
* 執行定時任務
*/
function execSchedule(){
var date=new Date(2016,9,23,23,56,1);
schedule.scheduleJob(date, function () {
fetch(getPageUrl(1),1,marginPrice);
})
}

//fetch(getPageUrl(1),1,marginPrice);
//sendMail();
execSchedule();



其中,marginPrice變量是我用來過濾房價,好比說只導出總價小於320萬的就好了。
完整代碼以下:fetch


/**
* 北京昌平區實時二手房房價信息彙總
*/
var http=require('http')
var fs=require('fs')
var cheerio=require('cheerio')
var request=require('request')
var excelPort = require('excel-export');
var nodemailer=require('nodemailer')
var schedule=require('node-schedule')

var fileName='北京昌平區實時房價列表-'+new Date().toLocaleString().split(' ')[0];
var conf={};
var rows=[];
var marginPrice=1;
/**
* 獲取指定頁面的url地址
* @param index
* @returns {string}
*/
function getPageUrl(index){
return 'http://beijing.anjuke.com/sale/changping/p'+index+'/#filtersort';
}
/**
* 爬去內容並導出excel和發郵件通知
* @param url
* @param curPage
* @param marginPrice
*/
function fetch(url,curPage){
if(!marginPrice){
marginPrice=9999;
}
http.get(url, function (res) {
var html='';
res.setEncoding('utf-8');
res.on('data', function (chunk) {
html+=chunk;
})
res.on('end', function () {
var $=cheerio.load(html);
var list=$('#houselist-mod .list-item');

list.each(function (i,item) {
item=$(item);
var name=item.find('.houseListTitle').text();
var area=item.find('.details-item').eq(0).find('span').eq(0).text();
var totalPrice=item.find('.pro-price .price-det strong').text()*1;
var category=item.find('.details-item').eq(0).find('span').eq(1).text();
var price=item.find('.details-item').eq(0).find('span').eq(2).text();
var floor=item.find('.details-item').eq(0).find('span').eq(3).text();
var year=item.find('.details-item').eq(0).find('span').eq(4).text();
var position=item.find('.details-item').eq(1).find('span').text();

if(marginPrice>=totalPrice){
rows.push([name,area,totalPrice,category,price,floor,year,position])
}

});


if(curPage==1){
conf.cols=[
{caption:'名稱',type:'string',width:50},
{caption:'面積',type:'string',width:15},
{caption:'總價',type:'number',width:15},
{caption:'類別',type:'string',width:15},
{caption:'單價',type:'string',width:15},
{caption:'樓層',type:'string',width:15},
{caption:'年份',type:'string',width:15},
{caption:'位置',type:'string',width:60}
]
}
//下一頁
if($('.aNxt').attr('href')){
var nextPage=curPage+1;
fetch(getPageUrl(nextPage),nextPage,marginPrice);
}else{
conf.rows=rows;
var result=excelPort.execute(conf);
var genFilePath='result/'+fileName+".xlsx";
fs.writeFile(genFilePath,result,'binary', function (err) {
if(err){
console.log(err)
}else{
console.log('done!');
//發送郵件
sendMail();
}
})
}


})
})
}
/**
* 發送郵件通知
*/
function sendMail(){
var transporter=nodemailer.createTransport('smtps://447818666%40qq.com:liqianghello@smtp.qq.com')
var opts={
from:'447818666@qq.com',
to:'447818666@qq.com',
subject:'nodeJs郵件系統測試',
text:'純文本',
html:'<h1 style="color:red">html文本h1</h1>'
}
transporter.sendMail(opts, function (err,info) {
if(err){
console.log(err)
}else{
console.log(info.response);
}
})
}
/**
* 執行定時任務
*/
function execSchedule(){
var date=new Date(2016,9,23,23,56,1);
schedule.scheduleJob(date, function () {
fetch(getPageUrl(1),1,marginPrice);
})
}
//fetch(getPageUrl(1),1,marginPrice);
//sendMail();
execSchedule();


本身測試nodeJs爬數據的功能大致就這些,確定有不少不足和不對的地方,歡迎提意見。。。網站

相關文章
相關標籤/搜索