生活中遇到了問題,想去成都買個房,那哪個區(qū)域性價比高肯定要考慮一番了,最粗暴直接的就是看租售比,遂打算去鏈家網(wǎng)爬上各個小區(qū)的賣房單價和租房單價比上一比,python寫爬蟲無疑是最流行的了,但最近在研究node,感覺寫個爬蟲強化一下node姿勢水平還是挺不錯的。開整。

首先http請求工具和dom解析工具是必不可少的,嚴謹?shù)恼f是對于像我這樣的菜鳥是必不可少的,http請求工具我選了 request,主流的還有 superagent 可選,dom解析 cheerio 應(yīng)該是不二選擇了,接口和 jquery 一樣一樣的。如果沒接觸過請先自行了解這兩個庫。

基本環(huán)境先搭建好,這個不在討論范圍,


github:https://github.com/huanqingli/node-web-spider

1. 第一步我們先看抓一個網(wǎng)頁是啥樣的:

移動開發(fā)培訓(xùn),Android培訓(xùn),安卓培訓(xùn),手機開發(fā)培訓(xùn),手機維修培訓(xùn),手機軟件培訓(xùn)

router.get('/sell_price', (req, res, next) => {                         request({                             url:'http://cd.lianjia.com/ershoufang/pg1ng1nb1l1/',                             headers: {                             'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.3 (KHTML, like Gecko) Chrome/55.0.2883.9 Safari/537.3'                         } }, function (error, response, body) {                             if (!error && response.statusCode == 200) {                                 res.send(body)                                                       }                         })   }

移動開發(fā)培訓(xùn),Android培訓(xùn),安卓培訓(xùn),手機開發(fā)培訓(xùn),手機維修培訓(xùn),手機軟件培訓(xùn)


不出所料的話,你訪問本機 sell_price 路由的時候鏈家對應(yīng)的頁面就會出現(xiàn)了。

其中有兩點要說明,有的時候你不帶User-Agent,網(wǎng)站會拒絕你的請求,chrome開發(fā)工具network里面隨便找個連接,把里面User-Agent給考出來貼上,有備無患。

鏈家是UTF-8編碼,就沒啥說法了,但是比如網(wǎng)易,它是gb2312編碼的,你拿過來就成火星文了,此時需要搞個解碼工具,iconv-lite應(yīng)該是主流了。我去爬了下網(wǎng)易,要注意一點,request拿過來的時候已經(jīng)幫你按UTF-8解碼了,你需要request時候設(shè)置encoding: null(與設(shè)置url同級),拿回來個buffer,再用iconv-lite解碼。

2. 第二步我們抓過來是要獲取里面的有用信息的,具體到我的目的,就是房價,面積,小區(qū)名等,

var $=cheerio.load(body); 后,你用 jQuery 怎么取值就用 cheerio 怎么取值,比如說:

$('.sellListContentlia.img').eq(i).attr('href') 一個頁面有30個房源,這就拿到了第 i 個房源的連接,進而去獲取房源詳細信息。我抓了兩次試了試,發(fā)現(xiàn) IP 被攔住了,說我訪問太頻繁,要認證。其實大多數(shù)網(wǎng)站都有這個限制,這就不爽了,怎么辦,找代理。

3. 代理是這么爬下來的:

移動開發(fā)培訓(xùn),Android培訓(xùn),安卓培訓(xùn),手機開發(fā)培訓(xùn),手機維修培訓(xùn),手機軟件培訓(xùn)

    router.get('/proxy',(req, res, next) => {         function checkProxy(proxy) {             return request({                 url: 'http://cd.lianjia.com/ershoufang/pg1ng1nb1l1/',                 proxy: "http://" + proxy['ip'] + ":" + proxy['port'],                 timeout: 5000,  //2s沒有返回則視為代理不行                 headers: {                     'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.3 (KHTML, like Gecko) Chrome/55.0.2883.9 Safari/537.3'                 }             }, function (error, response, body) {                 if(!error) {                     if (response.statusCode == 200) {                         console.log(response.request['proxy']['href'], "useful!");                         Ip.create({proxyIp:response.request['proxy']['href']}); //Ip是mogoose建立的數(shù)據(jù)庫模塊儲存Ip                     } else {                         console.log(response.request['proxy']['href'], "failed!");                     }                 }             });         }              request({             url:'http://www.xicidaili.com/nn/1',             headers: {                 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.3 (KHTML, like Gecko) Chrome/55.0.2883.9 Safari/537.3'             } }, function (error, response, body) {             if (!error && response.statusCode == 200) {                 var $ = cheerio.load(body);                 var trs = $("#ip_list tr");                 for(var i=1;i<trs.length;i++){                     var proxy = {};                     var tr = trs.eq(i);                     var tds = tr.children("td");                     proxy['ip'] = tds.eq(1).text();                     proxy['port'] = tds.eq(2).text();                     var speed = tds.eq(6).children("div").attr("title");                     speed = speed.substring(0, speed.length-1);                     var connectTime = tds.eq(7).children("div").attr("title");                     connectTime = connectTime.substring(0, connectTime.length-1);                     if(speed <= 5 && connectTime <= 1) { //用速度和連接時間篩選一輪                         checkProxy(proxy);                     }                 }             }         })          });

移動開發(fā)培訓(xùn),Android培訓(xùn),安卓培訓(xùn),手機開發(fā)培訓(xùn),手機維修培訓(xùn),手機軟件培訓(xùn)


還是熟悉的配方還是同樣的味道,從http://www.xicidaili.com/nn爬下代理信息,篩選一遍存到數(shù)據(jù)庫。

4. 存了二十幾個能用的代理,開始爬售房連接吧:

移動開發(fā)培訓(xùn),Android培訓(xùn),安卓培訓(xùn),手機開發(fā)培訓(xùn),手機維修培訓(xùn),手機軟件培訓(xùn)

    var urlsArray = [];     const pageNum = 100;     const housePerPage = 30;     router.get('/sell_price', (req, res, next) => {         function saveUrl(url) {             return Url.create({sellUrl:url})  //Url是mogoose建立的數(shù)據(jù)庫模塊儲存售房連接         }         var allHouseUrls = async function (ips) {             var j=1 ;             while(j<= pageNum){                 await new Promise(                     function (resolve, reject) {                         request({                             url:'http://cd.lianjia.com/ershoufang/pg'+j+'ng1nb1l1/',                             proxy: ips[Math.floor(Math.random()*ips.length)].proxyIp,                             timeout: 5000,                             headers: {                             'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.3 (KHTML, like Gecko) Chrome/55.0.2883.9 Safari/537.3'                         } }, function (error, response, body) {                             if (!error && response.statusCode == 200) {                                 console.log(ips[Math.floor(Math.random()*ips.length)].proxyIp);                                 var $ = cheerio.load(body);                                 for(let i=0;i<housePerPage;i++){                                     if($('.sellListContent li a.img').eq(i).attr('href')&&urlsArray.indexOf($('.sellListContent li a.img').eq(i).attr('href'))==-1){                                         urlsArray.push($('.sellListContent li a.img').eq(i).attr('href'));                                         saveUrl($('.sellListContent li a.img').eq(i).attr('href'));                                     }                                 }                                 j+=1;                                 resolve()                             } else{                                 console.log(error);                                 resolve()                             }                         })                     }                 )             }         };         Ip.find({},function (err, ips) {             allHouseUrls(ips);         })     });          module.exports = router;

移動開發(fā)培訓(xùn),Android培訓(xùn),安卓培訓(xùn),手機開發(fā)培訓(xùn),手機維修培訓(xùn),手機軟件培訓(xùn)


要說明的就兩點,取到代理 ip 后,每次請求隨機一個ip,如果請求失?。ㄊ聦嵶C明失敗率很高免費代理并不是很可靠),本次循環(huán)作廢,重新隨機一個 ip 再請求。如此進行下去,n長時間后終于成功發(fā)送了100個請求,請求了3000個連接??上攵晒Πl(fā)送3000個請求將是一場災(zāi)難。轉(zhuǎn)戰(zhàn)安居客,每個頁面可以獲得50條房屋數(shù)據(jù)而且不限ip

async,awaite,Promise用作異步控制,node7.6以上默認支持,還有其他好多異步方案可選,怎么著都行。

5. 爬安居客代碼如下:

移動開發(fā)培訓(xùn),Android培訓(xùn),安卓培訓(xùn),手機開發(fā)培訓(xùn),手機維修培訓(xùn),手機軟件培訓(xùn)

    router.get('/sell_price', (req, res, next) => {         var allHouseInfo = async function (area) {             var j=1 ;             while(j<= pageNum){                 await new Promise(                     function (resolve, reject) {                         request({                             url:'http://chengdu.anjuke.com/sale/'+area+'/b54-p'+j,                             // proxy: ips[Math.floor(Math.random()*ips.length)].proxyIp,                             // timeout: 2000,                             headers: {                             'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.3 (KHTML, like Gecko) Chrome/55.0.2883.9 Safari/537.3'                         } }, function (error, response, body) {                             if (!error && response.statusCode == 200) {                                 var $ = cheerio.load(body);                                 var lists = $('#houselist-mod li');                                 for(var i=0;i<lists.length;i++){                                     var info = {                                         area:lists.eq(i).find('.details-item').children('span').eq(0).text().slice(0,-2),                                         price:lists.eq(i).find('.details-item').children('span').eq(2).text().slice(0,-4),                                         year:lists.eq(i).find('.details-item').children('span').eq(4).text().slice(0,4),                                         garden:lists.eq(i).find('.details-item').eq(1).children('span').attr('title').split('[')[0].slice(0,-2)                                     };                                     Sell.create(info);                                     // console.log(info)                                 }                                 j+=1;                                 resolve()                             } else{                                 console.log(error);                                 resolve()                             }                         })                     }                 )             }         };         allHouseInfo("gaoxin");         allHouseInfo("wuhou");         allHouseInfo("qingyang");         allHouseInfo("jinjiang");         allHouseInfo("chenghua");         allHouseInfo("jinniu");         allHouseInfo("longquanyi"); });

移動開發(fā)培訓(xùn),Android培訓(xùn),安卓培訓(xùn),手機開發(fā)培訓(xùn),手機維修培訓(xùn),手機軟件培訓(xùn)

沒有新東西,爬了12000條房屋數(shù)據(jù),也就是2秒鐘的事,存在數(shù)據(jù)庫里備用。就寫到這,后面的事兒就是數(shù)據(jù)分析啦。

http://www.cnblogs.com/lihuanqing/p/6555049.html