Node爬取大批量文件的方法示例
有個(gè)朋友在搞留學(xué)工作室,經(jīng)常訪問(wèn)的一個(gè)網(wǎng)站叫留學(xué)者指南,然而每次都要去訪問(wèn)該網(wǎng)站,顯得極其不專業(yè)。于是托俺幫忙寫(xiě)腳本去爬他家的東西,我觀察了下,這次,我們要爬的東西就有點(diǎn)多了——大概就20多萬(wàn)個(gè)文件吧~~~
在20多萬(wàn)個(gè)文件中,下載極有可能會(huì)被中斷,因此需要做下載進(jìn)度備份,下載進(jìn)度恢復(fù)。
那么針對(duì)這樣子的需求,我們開(kāi)始吧!
上代碼!
const cheerio = require("cheerio"); //用于處于HTML文檔流,用法類似jQuery const http = require("http"); //用于發(fā)起請(qǐng)求 const fs = require("fs"); //用于檢測(cè)、寫(xiě)入文件等其他文件操作 // "http://www.compassedu.hk/sitemap1.txt", // "http://www.compassedu.hk/sitemap2.txt", //重復(fù)的鏈接 var source = [ //Robots.txt顯示的數(shù)據(jù)源 "http://m.compassedu.hk/sitemap3.txt", "http://m.compassedu.hk/sitemap4.txt", "http://m.compassedu.hk/sitemap6.txt", "http://m.compassedu.hk/sitemap7.txt", "http://m.compassedu.hk/sitemap8.txt" ] var s = 0; //控制源的序號(hào) var arr = []; //合并的下載地址數(shù)組 var sou = []; //源下載地址數(shù)組 var i = 0; //當(dāng)前下載地址數(shù)組序號(hào) fs.exists(__dirname+"/compassedu", function(flag){ //下載路徑檢測(cè) if(!flag) fs.mkdirSync(__dirname+"/compassedu"); //創(chuàng)建下載存放目錄 }) fs.exists(__dirname+"/logs", function(flag){ //日志路徑檢測(cè) if(!flag) fs.mkdirSync(__dirname+"/logs"); //創(chuàng)建日志存放目錄 }) if(fs.existsSync(__dirname+"/logs/compassedu_backup")){ //是否存在斷連恢復(fù)下載的控制文檔 let obj = JSON.parse(fs.readFileSync(__dirname+"/logs/compassedu_backup")); //存在則讀取上次下載的位置 i = obj.index; init(); //開(kāi)始初始化程序 } else{ init(); //開(kāi)始初始化程序 } function init(){ //初始化 http.get(source[s], function(res){ //獲取源下載地址文件 //將源文件的內(nèi)容存儲(chǔ)到數(shù)組 let rawData = ""; res.setEncoding("utf8"); res.on("data", function(chunk){ //監(jiān)聽(tīng)數(shù)據(jù)流 rawData += chunk; }); res.on("end", function(){ //監(jiān)聽(tīng)結(jié)束 sou[s] = rawData.split("\n"); console.log("源", s+1, ": ", sou[s].length, "條數(shù)據(jù)"); s++; if(s<source.length){ //源文件還未讀取完 init(); } else{ //源文件讀取完畢 for(let m=0;m<sou.length;m++){ //將所有的地址數(shù)組合并 arr = arr.concat(sou[m]); } arr = [...new Set(arr)]; //去重 console.log("總計(jì): ", arr.length, "條數(shù)據(jù)", "\n爬蟲(chóng)配置完畢!!\n開(kāi)始爬取 >>"); start(); //開(kāi)始爬取程序 } }); }) } function start(){ //開(kāi)始 let url = arr[i]; console.log(url); //打印當(dāng)前爬取的URL http.get(url,function(res){ //發(fā)起請(qǐng)求 let obj = { //將當(dāng)前的信息存儲(chǔ)到對(duì)象中 "time": new Date().toLocaleTimeString(), "index": i, "url": url, "status": res.statusCode }; fs.appendFileSync("./logs/download.log", JSON.stringify(obj), "utf8"); //寫(xiě)入日志文件 fs.writeFileSync("./logs/compassedu_backup", JSON.stringify({"index": i}), "utf8"); //將當(dāng)前的下載URL序號(hào)寫(xiě)入斷連恢復(fù)文件 //讀取請(qǐng)求到的數(shù)據(jù)流 let rawData = ""; res.setEncoding("utf8"); res.on("data", function(chunk){ //監(jiān)聽(tīng)數(shù)據(jù)流事件 rawData += chunk; }); res.on("end", function(){ //監(jiān)聽(tīng)結(jié)束事件 $ = cheerio.load(rawData); //啟用類jQuery插件 title = $(".container-public h1").text().replace(/\s/,"").trim(); //讀取數(shù)據(jù)流部分的標(biāo)題 body = $(".container-public").html(); //讀取數(shù)據(jù)流部分的內(nèi)容 body = body.split("visible-xs")[0]; //剝離多于的數(shù)據(jù)或其他處理,準(zhǔn)備寫(xiě)入文件 i++; //序號(hào)+1 fs.writeFile(__dirname+"/compassedu/"+title+".html", body, "utf8", function(err){ //將處理好的數(shù)據(jù)寫(xiě)入文件 if(!err) console.log(title, "寫(xiě)入成功"); else{ console.log(err); } }); if(i>=arr.length) { //若序號(hào)達(dá)到數(shù)組的最后,結(jié)束程序 console.log("爬取結(jié)束"); fs.unlinkSync("./logs/compassedu_backup");//爬取結(jié)束,銷毀斷連恢復(fù)文件 return; } else { //否則遞歸運(yùn)行 start(); } }); res.on("error", function(err){ //監(jiān)聽(tīng)其他錯(cuò)誤 console.log(err); }); }) }
至此,就結(jié)束了,是否有bug還不清楚,數(shù)據(jù)還沒(méi)爬完呢~
有bug的話,我后續(xù)補(bǔ)充修復(fù)~
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
nodejs中使用HTTP分塊響應(yīng)和定時(shí)器示例代碼
本文通過(guò)示例將要?jiǎng)?chuàng)建一個(gè)輸出純文本的HTTP服務(wù)器,輸出的純文本每隔一秒會(huì)新增100個(gè)用換行符分隔的時(shí)間戳。實(shí)例代碼非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下2017-03-03node.js中的path.basename方法使用說(shuō)明
這篇文章主要介紹了node.js中的path.basename方法使用說(shuō)明,本文介紹了path.basename的方法說(shuō)明、語(yǔ)法、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下2014-12-12Nodejs實(shí)現(xiàn)的一個(gè)靜態(tài)服務(wù)器實(shí)例
這篇文章主要介紹了Nodejs實(shí)現(xiàn)的一個(gè)靜態(tài)服務(wù)器實(shí)例,本文實(shí)現(xiàn)的靜態(tài)服務(wù)器實(shí)例包含cache功能、壓縮功能等,需要的朋友可以參考下2014-12-12利用Node.js和MySQL實(shí)現(xiàn)創(chuàng)建API服務(wù)器
這篇文章主要為大家詳細(xì)介紹了如何使用Node.js和MySQL創(chuàng)建API服務(wù)器的步驟,這也是從前端邁向全棧的一個(gè)開(kāi)始,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下2024-01-0130分鐘用Node.js構(gòu)建一個(gè)API服務(wù)器的步驟詳解
這篇文章主要介紹了30分鐘用Node.js構(gòu)建一個(gè)API服務(wù)器的步驟詳解,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-05-05Node.js中Process.nextTick()和Process.setImmediate()的區(qū)別
這篇文章介紹了Node.js中Process.nextTick()和Process.setImmediate()的區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07Node.js應(yīng)用設(shè)置安全的沙箱環(huán)境
這篇文章主要介紹了Node.js應(yīng)用設(shè)置安全的沙箱環(huán)境的方法以及注意事項(xiàng),對(duì)此有需要的朋友可以參考學(xué)習(xí)下。2018-04-04