這兩天看了好幾篇不錯的文章,有的時候想把好的文章 down 下來放到 kindle 上看,便寫了個爬蟲腳本,因為最近都在搞 node,所以就很自然的選擇 node 來爬咯~
本文地址:http://www.cnblogs.com/hustskyking/p/spider-with-node.html,轉載請注明源地址。
所謂爬蟲,可以簡單理解為利用程序操作文件,只是這些文件不在本地,需要我們拉取過來。
一. 爬蟲代碼解析
1. 拿到目標頁碼源碼
Node 提供了很多接口來獲取遠程地址代碼,就拿 AlloyTeam 的頁面舉例吧,把他首頁幾篇文章的信息爬取過來。因為 AlloyTeam 使用的協議是 http:// ,本文就不介紹 Node 中 https:// 的使用了。
var http = require("http"); var url = "http://www.alloyteam.com/"; var data = ""; // 創建一個請求 var req = http.request(url, function(res){ // 設置顯示編碼 res.setEncoding("utf8"); // 數據是 chunked 發送,意思就是一段一段發送過來的 // 我們使用 data 給他們串接起來 res.on('data', function(chunk){ data += chunk; }); // 響應完畢時間出發,輸出 data res.on('end', function(){ // dealData(data); console.log(data); }); }); // 發送請求 req.end();
上面短短七八行代碼,就拿到了 AlloyTeam 首頁的代碼,真的十分簡單,如果是 https:// 就得引用 https 模塊咯,都是差不多的。
2. 正則提取目標內容
先看下我們要抓取的內容:
由於沒有使用其他庫,我們沒辦法像操作 DOM 一樣獲取目標內容,不過寫正則也挺簡單的,比如我們要 獲取標題/文章鏈接/摘要 這些內容,正則表達式為:
// function dealData var reg = /<ul\s+class="articlemenu">\s+<li>\s+<a[^>]*>.*?<\/a>\s+<a href="(.*?)"[^>]*>(.*?)<\/a>[\s\S]*?<div\s+class="text">([\s\S]*?)<\/div>/g; var res = []; while(match = reg.exec(data)) { res.push({ "url": match[1], "title": match[2], "excerpt": match[3] }); }
這里的正則看起來有點晦澀,不過呢,正則在編程中十分基礎的東西,如果沒有太多的了解,建議先去搞清楚,這里就不細說啦。這里要強調的一點是:
reg.exec(data);
如果只寫上面這句話,只會拿到第一個匹配結果,所以需要使用 while 循環來處理,沒處理一次,正則匹配的位置就會往后推一下。其實上面這條語句執行后返回的是一個對象,其中包含一個 index 屬性,具體可以查閱 JavaScript 正則的內容。
這里返回(res)的數據格式是:
[{ "url: url, "title": title, "excerpt" excerpt }];
3. 數據的過濾
上面雖然拿到了內容,不過我們需要的是純文本,其他標簽什么的得過濾掉,excerpt 中包含了一些標簽:
var excerpt = excerpt.replace(/(<.*?>)((.*?)(<.*?>))?/g, "$3");
雖說文章中有很多代碼,有些標簽是不應該刪除的,不過這里是摘要內容,這些內容的標簽都刪除掉,方便我們儲存。然后把長度處理下:
excerpt = excerpt.slice(0, 120);
4. 存到數據庫(或者文件)
我這里是把文件儲存到文件之中,存放格式為:
[title](url)
> excerpt
哈哈,很熟熟悉吧,markdown 語法,看起來也比較清晰。
var str = ""; for(var i = 0, len = data.length; i < len; i++){ str += "[" + data[i].title + "](" + data[i].url + ")\n" + data[i].excerpt.replace("\n\s*\n?", ">\n") + "\n\n"; }
先拼接數據,然后寫入到文件:
fs.writeFile('index.md', str, function (err) { if (err) throw err; console.log('數據已保存~'); });
大功告成,過程其實是很簡單的。拿到的內容(Linux 下,字體真丑!):
二. 源碼與小結
如果對正則不太熟悉,上面的工作是不太好完成的,很多開發者為 Node 提供了工具庫,使用 npm 可以安裝,如果不習慣正則,使用一些工具包輔助處理,可以把拿到的數據當作 DOM 來解析。
我了解到的有一個叫做 node-jquery 的庫貌似還不錯,具體請讀者自己去網上搜吧,應該挺多的。
上面的代碼都是隨手寫的,沒有做什么容錯的機制,也只爬取了首頁的內容,不過思路都是一樣的,拿到 URL 之后再寫個循環,其他頁面的內容也就到手了。
源碼沒幾行:

var http = require("http"); var fs = require("fs"); var url = "http://www.alloyteam.com/"; var data = ""; var req = http.request(url, function(res){ res.setEncoding("utf8"); res.on('data', function(chunk){ data += chunk; }); res.on('end', function(){ dealData(data); }); }); req.on('error', function(e){ throw e; }); req.end(); console.log("數據下載中..."); function dealData(data){ var reg = /<ul\s+class="articlemenu">\s+<li>\s+<a[^>]*>.*?<\/a>\s+<a href="(.*?)"[^>]*>(.*?)<\/a>[\s\S]*?<div\s+class="text">([\s\S]*?)<\/div>/g; var res = []; while(match = reg.exec(data)) { res.push({ "url": match[1], "title": match[2], "excerpt": match[3].replace(/(<.*?>)((.*?)(<.*?>))?/g, "$3").slice(0,120) }); } writeFile(res) } function writeFile(data){ var str = ""; for(var i = 0, len = data.length; i < len; i++){ str += "[" + data[i].title + "](" + data[i].url + ")\n>" + data[i].excerpt.replace(/\n\s*\n?/g, "\n>") + "\n\n"; } fs.writeFile('index.md', str, function (err) { if (err) throw err; console.log('數據已保存~'); }); }
在 node 環境中:
node spider.js
就可以在同級目錄下看到 index.md 文件了。至於如何放到 kindle 中,先了解下 OPF 格式,然后使用 Amazon 的 KindleGen 工具打包就行啦。
三. 參考資料