1. 程式人生 > >Node.js:request+cheerio爬蟲爬取免費代理

Node.js:request+cheerio爬蟲爬取免費代理

背景

在極客學院的微信公眾號上看到了一個Node.js寫爬蟲的入門教程——用nodejs去爬一下A站老司機的文章,感覺挺有意思,於是動手寫一下。

依賴庫

  • request:發請求,下載網頁用的,類似於python的urllib2
  • cheerio:解析網頁用的,語法和jquery相似,類似於python的beautifulsoup。

直接用npm下載即可

npm install request --save
npm install cheerio --save

程式功能

從百度到的提供免費代理的網站上爬取免費代理,並通過訪問靜態資源的方式檢測代理是否可用,將可用的代理存放到本地檔案當中。

與python的不同

用python寫過不少爬蟲,本來以為這次會非常輕鬆的完成,不過失算了。

由於nodejs的單執行緒非同步特性,如果不加思考直接像python那麼寫,那麼request請求還沒回來的時候下面的程式碼就已經執行完了,儲存的總是空的。

所以得做一些小手腳讓它等待非同步函式執行完畢再執行剩下的程式碼。我採用的辦法是用標誌量。

更好的解決辦法應該是用promise,不過我比較愚魯,看了半天愣是沒看懂,希望不久的將來能領悟吧……

程式碼

var request = require("request");
var cheerio = require("cheerio"
); var fs = require("fs"); var proxys = []; //儲存從網站上獲取到的代理 var useful = []; //儲存檢查過有效性的代理 /** * 獲取www.xicidaili.com提供的免費代理 */ function getXici() { url = "http://www.xicidaili.com/nn"; // 國內高匿代理 request ({ url: url, method: "GET", headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36'
} //給個瀏覽器頭,不然網站拒絕訪問 }, function(error, response, body) { if(!error) { var $ = cheerio.load(body); var trs = $("#ip_list tr"); for(var i=1;i<trs.length;i++) { var proxy = {}; tr = trs.eq(i); 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) { //用速度和連線時間篩選一輪 proxys.push(proxy); } } } check(); }); } /** * 過濾無效的代理 */ function check() { //嘗試請求百度的靜態資源公共庫中的jquery檔案 var url = "http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"; var flag = proxys.length; //檢查是否所有非同步函式都執行完的標誌量 for(var i=0;i<proxys.length;i++) { var proxy = proxys[i]; request({ url: url, proxy: "http://" + proxy['ip'] + ":" + proxy['port'], method: 'GET', timeout: 20000 //20s沒有返回則視為代理不行 }, function (error, response, body) { if(!error) { if (response.statusCode == 200) { //這裡因為nodejs的非同步特性,不能push(proxy),那樣會存的都是最後一個 useful.push(response.request['proxy']['href']); console.log(response.request['proxy']['href'], "useful!"); } else { console.log(response.request['proxy']['href'], "failed!"); } } else { //console.log("One proxy failed!"); } flag--; if (flag == 0) { saveProxys(); } }); } } /** * 把獲取到的有用的代理儲存成json檔案,以便在別處使用 */ function saveProxys() { fs.writeFileSync("proxys.json", JSON.stringify(useful)); console.log("Save finished!"); } getXici(); //啟動這個爬蟲