前兩天發現知乎收藏夾中的答案正在不斷減小。。看來須要保存一下了,但以前別人的方式是用chrome插件(瀏覽器沒法自動保存本地文件)+wget先後端配合來完成這個工做的,並且還有一些缺點(好比保存的html沒法改名),十分麻煩,因此打算用後端程序來抓一下網頁,最終仍是選擇了Node來實現,是由於想借此機會學習一下Node。javascript
結果:好端端的異步編程被窩寫成了同步編程。。此次就放同步編程的代碼好了,下次再放異步的。。(並且發如今一個月前就有人用python和c#實現了一個足夠好的版本。。一下就沒動力了)html
廢話少說,放代碼。(Node的基本知識不用我說了吧)java
//本身用的小品代碼,因此沒有拆成多個模塊,而是塞到一塊兒了。 var http = require('http'), fs = require('fs'), path = require('path'); //本身封裝的一個建立目錄的工具函數,由於要建立的目錄的父目錄不存在時,不會像shell指令同樣連父目錄一塊建立了,而是會報錯。 //因此用try-catch,出錯的話則把目錄拆成多個部分,一步步檢查。 function myMkdir(dir) { var nowPath = ''; dir = path.normalize(dir.toString()); try{ if (fs.existsSync(dir) !== true) { fs.mkdirSync(dir); } else { console.log('該目錄或文件已存在'); return true; } } catch (e) { dir = path.normalize('/') === '\\'? dir.split('\\'):dir.split('/'); while (dir.length > 0) { nowPath += dir.shift()+'\\'; if (fs.existsSync(nowPath) === true && fs.statSync(nowPath).isDirectory() === false) { console.log(nowPath+'處已有文件存在,請指定其餘目錄。'); return false; } else if (fs.existsSync(nowPath) === true && fs.statSync(nowPath).isDirectory() === true) { continue; }else { try { fs.mkdirSync(nowPath); } catch (e) { console.log('建立目錄'+nowPath+'失敗,多是權限不足,請指定其餘目錄。'); return false; } } } } console.log('建立目錄成功'); return true; } //從module.paths獲取程序運行的當前目錄 function getNowPath () { //不一樣系統的斜槓不一樣,須要這樣處理一下。 var slash = path.normalize('/'); return module.paths[0].split(slash).slice(0,-1).join(slash); } //src,dir //傳入兩個參數,src和dir,src是指定知乎上收藏夾的url,dir是指定保存目錄 //個人作法是,先檢查目錄dir,而後遍歷src的目錄頁得到全部文章的連接,最後再一一下載 function main(argv) { var waitingList = [],nowBuffer, slash = path.normalize('/'), src = argv[0], dir = argv[1], links = [], nowPage = 0, //當前頁碼 totalPage, //總頁碼 downloading = -1, whitespace = "[\\x20\\t\\r\\n\\f]", //node不能用jQuery和DOM的API很蛋疼。。因此正則只能寫長一點。。 rgetTotalPage = new RegExp('<span>'+whitespace+'*<a href="\\?page=(\\d+)">\\1<\\/a>'+whitespace+'*<\\/span>'+whitespace+'*<span>'+ whitespace+'*<a href="\\?page=2">下一頁<\\/a>'+whitespace+'*<\\/span>'+whitespace+'*<\\/div>'); var getAllLinkThisPage = function () { nowPage++; var contentTmp = []; var reg = /href="(\/question\/\d+\/answer\/\d+)">/g, match = true; if (nowPage <= totalPage) { console.log('正在獲取第'+nowPage+'頁的連接'); http.get(src+'?page='+nowPage,function (response) { response.on('data',function (chunk) { contentTmp.push(chunk); }); response.on('end',function () { console.log('第'+nowPage+'頁連接獲取完畢'); contentTmp = Buffer.concat(contentTmp).toString(); while (reg.lastIndex < contentTmp.length && match) { match = reg.exec(contentTmp); if (match) { var tmp = {type:'html',address:'http://www.zhihu.com'+match[1],dir:dir}; links.push(tmp); } } contentTmp = []; getAllLinkThisPage(); }); }); } else { console.log('連接獲取完畢,開始下載頁面,總共有'+links.length+'個頁面'); download(); } }; var download = function () { downloading++; var rgetTitle = /<title>([\s\S]+)<\/title>/, match = true, title = '', fileName = ''; if (downloading < links.length) { http.get(links[downloading].address,function (response) { console.log('開始下載第'+downloading+'個頁面'); console.log(links[downloading].address); var contentTmp = []; response.on('data',function (chunk) { contentTmp.push(chunk); }); response.on('end',function () { contentTmp = Buffer.concat(contentTmp).toString(); title = rgetTitle.exec(contentTmp)[1].replace(/[\\\/:*?"<>|]/g,''); fileName = links[downloading].dir+path.normalize('/')+title +'.'+links[downloading].type; fs.writeFile(fileName,contentTmp,function (err) { if (err) { console.log('建立'+fileName+'文件失敗,緣由:'); console.log(err); } else { console.log('已成功保存'+fileName); } }); contentTmp = []; download(); }); }); } else { console.log(links.length+'個文件已下載完畢'); } }; if (src === undefined || src.indexOf('www.zhihu.com') < 0) { console.log('獲取失敗!請輸入正確的知乎收藏夾網址,http://www.zhihu.com/collection/20026124'); return false; } if (dir === undefined) { dir = getNowPath()+slash+'zhihu'+slash+src.split('/').slice(-1); console.log('沒有指定存放位置,將使用默認位置:'+dir); } //建立目錄 if (fs.existsSync(dir) === false) { console.log('該目錄不存在,開始建立目錄'); if(myMkdir(dir) === false) { console.log('建立目錄出錯,請看錯誤信息,若不能解決,請聯繫做者 Aeolia yiaolia@gmail.com'); return false; } } else if (fs.existsSync(dir) === true && fs.statSync(dir).isDirectory() === false) { console.log('該目錄已被文件佔領,請移除該文件'); return false; } console.log('開始鏈接'+src); http.get(src,function (response) { var content = []; console.log(response.statusCode); response.on('data',function (chunk) { content.push(chunk); }); response.on('end',function () { content = Buffer.concat(content).toString(); console.log('拿到目錄文件'); totalPage = rgetTotalPage.exec(content.toString())[1]; console.log(totalPage); getAllLinkThisPage(); }); }) } main(process.argv.slice(2));