最近在搗鼓一個仿簡書的開源項目,從前端到後臺,一戰擼到底。就須要數據支持,最近mock數據,比較費勁。簡書的不少數據都是後臺渲染的,很難快速抓api請求數據,本人又比較懶,就想到用寫個簡易爬蟲系統。css
安裝nodejs,官網, 中文網。根據本身系統安裝,這裏跳過,表示你已經安裝了nodejs。html
選擇一款順手拉風的編輯器,用來寫代碼。推薦webstorm最近版。前端
webstorm建立一個工程,起一個喜歡的名字。建立一個package.json文件,webstorm快捷建立package.json很是簡單。仍是用命令行建立,打開Terminal,默認當前項目根目錄,npm init,一直下一步。node
能夠看這裏npm經常使用你應該懂的使用技巧jquery
superagent 頁面數據下載git
cheerio 頁面數據解析github
這是2個npm包,咱們先下載在接着繼續,下載須要時間的。web
npm install superagent cheerio --save
接下啦簡單說說這2個是啥東西ajax
superagent是nodejs裏一個很是方便的客戶端請求代碼模塊,superagent是一個輕量級的,漸進式的ajax API,可讀性好,學習曲線低,內部依賴nodejs原生的請求API,適用於nodejs環境下。數據庫
get (默認)
post
put
delete
head
語法:request(RequestType, RequestUrl).end(callback(err, res));
寫法:
request .get('/login') .end(function(err, res){ // code });
application/json (默認)
form
json
png
xml
...
設置方式:
1. request .get('/login') .set('Content-Type', 'application/json'); 2. request .get('/login') .type('application/json'); 3. request .get('/login') .accept('application/json');
以上三種方效果同樣。
query
send
設置請求參數,能夠寫json對象或者字符串形式。
能夠寫多組key,value
request .get('/login') .query({ username: 'jiayi', password: '123456' });
能夠寫多組key=value,須要用&隔開
request .get('/login') .query('username=jiayi&password=123456');
設置請求參數,能夠寫json對象或者字符串形式。
能夠寫多組key,value
request .get('/login') .sned({ username: 'jiayi', password: '123456' });
能夠寫多組key=value,須要用&隔開
request .get('/login') .sned('username=jiayi&password=123456');
上面兩種方式可使用在一塊兒
request .get('/login') .query({ id: '100' }) .sned({ username: 'jiayi', password: '123456' });
Response.text包含未解析前的響應內容,通常只在mime類型可以匹配text/json、x-www-form-urlencoding的狀況下,默認爲nodejs客戶端提供,這是爲了節省內存,由於當響應以文件或者圖片大內容的狀況下影響性能。
Response.header包含解析以後的響應頭數據,鍵值都是node處理成小寫字母形式,好比res.header('content-length')。
Content-Type響應頭字段是一個特列,服務器提供res.type來訪問它,默認res.charset是空的,若是有的化,則自動填充,例如Content-Type值爲text/html;charset=utf8,則res.type爲text/html;res.charset爲utf8。
cheerio是一個node的庫,能夠理解爲一個Node.js版本的jquery,用來從網頁中以 css selector取數據,使用方式和jquery基本相同。
類似的語法:Cheerio 包括了 jQuery 核心的子集。Cheerio 從jQuery庫中去除了全部 DOM不一致性和瀏覽器尷尬的部分,揭示了它真正優雅的API。
閃電般的塊:Cheerio 工做在一個很是簡單,一致的DOM模型之上。解析,操做,呈送都變得難以置信的高效。基礎的端到端的基準測試顯示Cheerio 大約比JSDOM快八倍(8x)。
巨靈活: Cheerio 封裝了兼容的htmlparser。Cheerio 幾乎可以解析任何的 HTML 和 XML document。
須要先loading一個須要加載html文檔,後面就能夠jQuery同樣使用操做頁面了。
const cheerio = require('cheerio'); const $ = cheerio.load('<ul id="fruits">...</ul>'); $('#fruits').addClass('newClass');
基本全部選擇器基本和jQuery同樣,就不一一列舉。具體怎麼使用看官網。
上面已經基本把咱們要用到東西有了基本的瞭解了,咱們用到比較簡單,接下來就開始寫代碼了,爬數據了哦。
根目錄建立一個app.js文件。
引入依賴
定義一個地址
發起請求
頁面數據解析
分析頁面數據
生成數據
const superagent = require('superagent'); const cheerio = require('cheerio');
const reptileUrl = "http://www.jianshu.com/";
superagent.get(reptileUrl).end(function (err, res) { // 拋錯攔截 if(err){ return throw Error(err); } // 等待 code });
這個時候咱們會向簡書首頁發一個請求,只要不拋錯,走if,那麼就能夠繼續往下看了。
superagent.get(reptileUrl).end(function (err, res) { // 拋錯攔截 if(err){ return throw Error(err); } /** * res.text 包含未解析前的響應內容 * 咱們經過cheerio的load方法解析整個文檔,就是html頁面全部內容,能夠經過console.log($.html());在控制檯查看 */ let $ = cheerio.load(res.text); });
註釋已經說明這行代碼的意思,就不在說明了。就下了就比較難了。
你需在瀏覽器打開簡書官網,簡書是後臺渲染部分可見的數據,後續數據是經過ajax請求,使用js填充。咱們爬數據,通常只能爬到後臺渲染的部分,js渲染的是爬不到,若是ajax,你能夠直接去爬api接口,那個往後再說。
言歸正傳,簡書首頁文章列表,默認會加載20條數據,這個已經夠我用了,你每次刷新,若是有更新就會更新,最新的永遠在最上面。
這20條數據存在頁面一個類叫.note-list的ul裏面,每條數據就是一個li,ul父級有一個id叫list-container,學過html的都知道id是惟一,保證不出錯,我選擇id往下查找。
$('#list-container .note-list li')
上面就是cheerio幫咱們獲取到說有須要的文章列表的li,是否是和jq寫同樣。我要獲取li裏面內容就須要遍歷 Element.each(function(i, elem) {})
也是和jq同樣
$('#list-container .note-list li').each(function(i, elem) { // 拿到當前li標籤下全部的內容,開始幹活了 });
以上都比較簡單,複雜的是下面的,數據結構。咱們須要怎麼拼裝數據,我大體看了一下頁面,根據經驗總結了一個結構,還算靠譜。
{ id: 每條文章id slug:每條文章訪問的id (加密的id) title: 標題 abstract: 描述 thumbnails: 縮略圖 (若是文章有圖,就會抓第一張,若是沒有圖就沒有這個字段) collection_tag:文集分類標籤 reads_count: 閱讀計數 comments_count: 評論計數 likes_count:喜歡計數 author: { 做者信息 id:沒有找到 slug: 每一個用戶訪問的id (加密的id) avatar:會員頭像 nickname:會員暱稱(註冊填的那個) sharedTime:發佈日期 } }
基本數據結構有了,先定義一個數組data,來存放拼裝的數據,留給後面使用。
隨便截取一條文章數據
<li id="note-12732916" data-note-id="12732916" class="have-img"> <a class="wrap-img" href="/p/b0ea2ac2d5c4" target="_blank"> <img src="//upload-images.jianshu.io/upload_images/1996705-7e00331b8f3dbc5d.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/375/h/300" alt="300" /> </a> <div class="content"> <div class="author"> <a class="avatar" target="_blank" href="/u/652fbdd1e7b3"> <img src="//upload.jianshu.io/users/upload_avatars/1996705/738ba2908445?imageMogr2/auto-orient/strip|imageView2/1/w/96/h/96" alt="96" /> </a> <div class="name"> <a class="blue-link" target="_blank" href="/u/652fbdd1e7b3">xxx</a> <span class="time" data-shared-at="2017-05-24T08:05:12+08:00"></span> </div> </div> <a class="title" target="_blank" href="/p/b0ea2ac2d5c4">xxxxxxx</a> <p class="abstract"> xxxxxxxxx... </p> <div class="meta"> <a class="collection-tag" target="_blank" href="/c/8c92f845cd4d">xxxx</a> <a target="_blank" href="/p/b0ea2ac2d5c4"> <i class="iconfont ic-list-read"></i> 414 </a> <a target="_blank" href="/p/b0ea2ac2d5c4#comments"> <i class="iconfont ic-list-comments"></i> 2 </a> <span><i class="iconfont ic-list-like"></i> 16</span> <span><i class="iconfont ic-list-money"></i> 1</span> </div> </div> </li>
咱們就拿定義的數據結構和實際的頁面dom去一一比對,去獲取咱們想要的數據。
id: 每條文章id
li上有一個 data-note-id="12732916"這個東西就是文章的id,
怎麼獲取:$(elem).attr('data-note-id'),這樣就完事了
slug:每條文章訪問的id (加密的id)
若是你點文章標題,或者帶縮略圖的位置,都會跳轉一個新頁面 http://www.jianshu.com/p/xxxxxx
這樣的格式。標題是一個a連接,連接上有一個href屬性,裏面有一段 /p/xxxxxx
這樣的 /p/是文章詳情一個標識,xxxxxx是標識哪片文章。而咱們slug就是這個xxxxxx,就須要處理一下。$(elem).find('.title').attr('href').replace(//p//, ""),這樣就能夠獲得xxxxxx了。
title: 標題
這個簡單,$(elem).find('.title').text()就行了。
abstract: 描述
這個簡單,$(elem).find('.abstract').text()就行了。
thumbnails: 縮略圖 (若是文章有圖,就會抓第一張,若是沒有圖就沒有這個字段)
這個存在.wrap-img這a標籤裏面img裏,若是沒有就不顯示,$(elem).find('.wrap-img img').attr('src'),若是取不到就是一個undefined,那正合我意。
下面4個都在.meta的div裏面 (我沒有去打賞的數據,由於我不須要這個數據)
collection_tag:文集分類標籤
有對應的class,$(elem).find('.collection-tag').text()
reads_count: 閱讀計數
這個就比較麻煩了,它的結構是這樣的
<a target="_blank" href="/p/b0ea2ac2d5c4"> <i class="iconfont ic-list-read"></i> 414 </a>
還要有一個字體圖標的class可使用,否則還真很差玩,那須要怎麼獲取了,$(elem).find('.ic-list-read').parent().text(),先去查找這個字體圖標i標籤,而後去找它的父級a標籤,獲取裏面text文本,標籤就不被獲取了,只剩下數字。
接下來2個同樣處理的。
comments_count: 評論計數
$(elem).find('.ic-list-comments').parent().text()
likes_count:喜歡計數
$(elem).find('.ic-list-like').parent().text()
接來就是會員信息,所有都在.author這個div裏面
id:沒有找到
slug: 每一個用戶訪問的id (加密的id)
這個處理方式和文章slug同樣,$(elem).find('.avatar').attr('href').replace(//u//, ""),惟一不一樣的須要吧p換成u。
avatar:會員頭像
$(elem).find('.avatar img').attr('src')
nickname:會員暱稱(註冊填的那個)
暱稱存在一個叫.blue-link標籤裏面,$(elem).find('.blue-link').text()
sharedTime:發佈日期
這個發佈日期,你看到頁面是個性化時間,xx小時前啥的,若是直接取就是一個坑爹的事了,在.time的span上有一個data-shared-at="2017-05-24T08:05:12+08:00"這個纔是正真的時間,你會發現它一上來是空的,是js來格式化的。$(elem).find('.time').attr('data-shared-at')
以上就是全部字段來源的。接下來要說一個坑爹的事,text()獲取出來的,有回車符/n和空格符/s。因此須要寫一個方法把它們去掉。
function replaceText(text){ return text.replace(/\n/g, "").replace(/\s/g, ""); }
組裝起來的數據代碼:
let data = []; // 下面就是和jQuery同樣獲取元素,遍歷,組裝咱們須要數據,添加到數組裏面 $('#list-container .note-list li').each(function(i, elem) { let _this = $(elem); data.push({ id: _this.attr('data-note-id'), slug: _this.find('.title').attr('href').replace(/\/p\//, ""), author: { slug: _this.find('.avatar').attr('href').replace(/\/u\//, ""), avatar: _this.find('.avatar img').attr('src'), nickname: replaceText(_this.find('.blue-link').text()), sharedTime: _this.find('.time').attr('data-shared-at') }, title: replaceText(_this.find('.title').text()), abstract: replaceText(_this.find('.abstract').text()), thumbnails: _this.find('.wrap-img img').attr('src'), collection_tag: replaceText(_this.find('.collection-tag').text()), reads_count: replaceText(_this.find('.ic-list-read').parent().text()) * 1, comments_count: replaceText(_this.find('.ic-list-comments').parent().text()) * 1, likes_count: replaceText(_this.find('.ic-list-like').parent().text()) * 1 }); });
let _this = $(elem); 先把$(elem);存到一個變量裏面,jq寫習慣了。
有幾個*1是吧數字字符串轉成數字,js小技巧,不解釋。
數據已經能夠獲取了,都存在data這個數據裏面,如今是20條數據,咱們理想的數據,那麼放在node裏面,咱們仍是拿不到,怎麼辦,一個存在數據庫(尚未弄到哪裏,我都尚未想好怎麼建數據庫表設計),一個就存在本地json文件。
那就存在本地json文件。nodejs是一個服務端語言,就說能夠訪問本地磁盤,添加文件和訪問文件。須要引入nodejs內置的包fs。
const fs = require('fs');
它的其餘用法不解釋了,只說一個建立一個文件,而且在裏面寫內容
這是寫文件的方法:
fs.writeFile(filename,data,[options],callback); /** * filename, 必選參數,文件名 * data, 寫入的數據,能夠字符或一個Buffer對象 * [options],flag 默認‘2’,mode(權限) 默認‘0o666’,encoding 默認‘utf8’ * callback 回調函數,回調函數只包含錯誤信息參數(err),在寫入失敗時返回。 */
咱們須要這樣來寫了:
// 寫入數據, 文件不存在會自動建立 fs.writeFile(__dirname + '/data/article.json', JSON.stringify({ status: 0, data: data }), function (err) { if (err) throw err; console.log('寫入完成'); });
注意事項
我方便管理數據,放在data文件夾,若是你也是這樣,記得必定先要在根目錄建一個data文件夾否則就會報錯
默認utf-8編碼;
寫json文件必定要JSON.stringify()處理,否則就是[object Object]這貨了。
若是是文件名能夠直接article.json會自動生成到當前項目根目錄裏,若是要放到某個文件裏,例如data,必定要加上__dirname + '/data/article.json'。千萬不能寫成3. 若是是文件名能夠直接article.json會自動生成到當前項目根目錄裏,若是要放到某個文件裏,例如data,必定要加上__dirname + '/data/article.json'。千萬不能寫成'/data/article.json'否則就會拋錯,找不到文件夾,由於文件夾在你所在的項目的盤符裏。例如G:/data/article.json。
以上基本就完成一個列表頁面的抓取。看下完整代碼:
/** * 獲取依賴 * @type {*} */ const superagent = require('superagent'); const cheerio = require('cheerio'); const fs = require('fs'); /** * 定義請求地址 * @type {*} */ const reptileUrl = "http://www.jianshu.com/"; /** * 處理空格和回車 * @param text * @returns {string} */ function replaceText(text) { return text.replace(/\n/g, "").replace(/\s/g, ""); } /** * 核心業務 * 發請求,解析數據,生成數據 */ superagent.get(reptileUrl).end(function (err, res) { // 拋錯攔截 if (err) { return throw Error(err); } // 解析數據 let $ = cheerio.load(res.text); /** * 存放數據容器 * @type {Array} */ let data = []; // 獲取數據 $('#list-container .note-list li').each(function (i, elem) { let _this = $(elem); data.push({ id: _this.attr('data-note-id'), slug: _this.find('.title').attr('href').replace(/\/p\//, ""), author: { slug: _this.find('.avatar').attr('href').replace(/\/u\//, ""), avatar: _this.find('.avatar img').attr('src'), nickname: replaceText(_this.find('.blue-link').text()), sharedTime: _this.find('.time').attr('data-shared-at') }, title: replaceText(_this.find('.title').text()), abstract: replaceText(_this.find('.abstract').text()), thumbnails: _this.find('.wrap-img img').attr('src'), collection_tag: replaceText(_this.find('.collection-tag').text()), reads_count: replaceText(_this.find('.ic-list-read').parent().text()) * 1, comments_count: replaceText(_this.find('.ic-list-comments').parent().text()) * 1, likes_count: replaceText(_this.find('.ic-list-like').parent().text()) * 1 }); }); // 生成數據 // 寫入數據, 文件不存在會自動建立 fs.writeFile(__dirname + '/data/article.json', JSON.stringify({ status: 0, data: data }), function (err) { if (err) throw err; console.log('寫入完成'); }); });
一個簡書首頁文章列表的爬蟲就大工告成了,運行代碼,打開Terminal運行node app.js或者node app都行。或者在package.json的scripts對象下添加一個"dev": "node app.js",而後用webstorm的npm面板運行。
有文章列表就有對應的詳情頁面,後面繼續講解怎麼爬詳情。
有了上面抓取文章列表的經驗,接下來就好辦多了,完事開頭難。
引入依賴
定義一個地址
發起請求
頁面數據解析
分析頁面數據
生成數據
這個就不用引入,在一個文件裏面,由於比較簡單的,代碼很少,懶得分文件寫。導入導出模塊麻煩,人懶就這樣的。
但咱們須要寫一個函數,來處理爬詳情的方法。
function getArticle(item){ // 等待code }
注意這個地址,是有規律的,不是隨便的地址,隨便點開一篇文章就能夠看到地址欄,http://www.jianshu.com/p/xxxxxx
, 咱們定義的reptileUrl = "http://www.jianshu.com/";
那麼就須要拼地址了,還記得xxxxxx咱們存在哪裏嗎,存在slug裏面。請求地址:reptileUrl + 'p/' + item.slug
。
superagent.get(reptileUrl + 'p/' + item.slug).end(function (err, res) { // 拋錯攔截 if(err){ return throw Error(err); } });
你懂的
superagent.get(reptileUrl + 'p/' + item.slug).end(function (err, res) { // 拋錯攔截 if(err){ return throw Error(err); } /** * res.text 包含未解析前的響應內容 * 咱們經過cheerio的load方法解析整個文檔,就是html頁面全部內容,能夠經過console.log($.html());在控制檯查看 */ let $ = cheerio.load(res.text); });
你可能會按上面的方法,打開一個頁面,而後就去獲取標籤上面的class,id。我開始也在這個上面遇到一個坑,頁面上有閱讀 ,評論 ,喜歡 這三個數據,我一開始覺得都是直接load頁面就有數據,在獲取時候,並無數據,是一個空。我就奇怪,而後我就按了幾回f5刷新,發現問題了,這幾個數據的是頁面加載完成之後才顯示出來的,那麼就是說這個有多是js渲染填充的。那就說明的我寫的代碼沒有錯。
有問題要解決呀,若是是js渲染,要麼會有網絡加載,刷新幾回,沒有這個數據,那就只能存在頁面裏,寫的內聯的script標籤裏面了,右鍵查看源碼,ctrl+f搜索,把閱讀 ,評論 ,喜歡的數字,隨便挑一個,找到了最底部data-name="page-data"的script標籤裏面,有一個json對象,裏面有些字段,和我文章列表定義很像,就是這個。有了這個就好辦了,省的我去截取一大堆操做。
解析script數據
let note = JSON.parse($('script[data-name=page-data]').text());
script裏面數據
{"user_signed_in":false,"locale":"zh-CN","os":"windows","read_mode":"day","read_font":"font2","note_show":{"is_author":false,"is_following_author":false,"is_liked_note":false,"uuid":"7219e299-034d-4051-b995-a6a4344038ef"},"note":{"id":12741121,"slug":"b746f17a8d90","user_id":6126137,"notebook_id":12749292,"commentable":true,"likes_count":59,"views_count":2092,"public_wordage":1300,"comments_count":29,"author":{"total_wordage":37289,"followers_count":221,"total_likes_count":639}}}
把script裏面內容都獲取出來,而後用 JSON方法,字符串轉對象。
接下來依舊是要定義數據結構:
article: { 文章信息 id: 文章id slug: 每條文章訪問的id (加密的id) title: 標題 content: 正文(記得要帶html標籤的) publishTime: 更新時間 wordage: 字數 views_count: 閱讀計數 comments_count: 評論計數 likes_count: 喜歡計數 }, author: { id: 用戶id slug: 每一個用戶訪問的id (加密的id) avatar: 會員頭像 nickname: 會員暱稱(註冊填的那個) signature: 會員暱稱簽名 total_wordage: 總字數 followers_count: 總關注計數 total_likes_count: 總喜歡計數 }
還要專題分類和評論列表我沒有累出來,有興趣能夠本身去看看怎麼爬出來。它們是單獨api接口,數據結構就不須要了。
由於有了note 這個對象不少數據都簡單了,仍是一個一個說明來源
article 文章信息
id: 文章id
主要信息都存在note.note裏面,文章id就是note.note.id,
slug: 每條文章訪問的id (加密的id)
note.note.slug
title: 標題
全部的正文都存在.post下的.article裏,那麼獲取title就是$('div.post').find('.article .title').text()
content: 正文(記得要帶html標籤的)
注意正文不是獲取text文本是要獲取html標籤,須要用到html來獲取而不是text,$('div.post').find('.article .show-content').html() 返回都是轉義字符。到時候前端須要處理就會顯示了。雖然咱們看不懂,瀏覽器看得懂就好了。
publishTime: 更新時間
這時間直接顯示出來了,不是個性化時間,直接取就行了$('div.post').find('.article .publish-time').text()
wordage: 字數
這個是一個標籤裏面<字數 1230>這樣的,咱們確定不能要這樣的,須要吧數字提取出來,$('div.post').find('.article .wordage').text().match(/d+/g)[0]*1 用正則獲取數字字符串,而後轉成數字。
views_count: 閱讀計數
note.note.views_count
comments_count: 評論計數
note.note.comments_count
likes_count: 喜歡計數
note.note.likes_count
author 用戶信息
id: 用戶id
前面的文章列表咱們並無拿到用戶id,note.note發現了一個user_id,反正不論是不是先存了再說,別空着,note.note.user_id
slug: 每一個用戶訪問的id (加密的id)
文章列表怎麼獲取,這個就怎麼獲取$('div.post').find('.avatar').attr('href').replace(//u//, "")
avatar: 會員頭像
$('div.post').find('.avatar img').attr('src')
nickname: 會員暱稱(註冊填的那個)
$('div.post').find('.author .name a').text()
signature: 會員暱稱簽名
這個簽名在上面位置了,就在文章正文下面,評論和打賞上面,有個很大關注按鈕那個灰色框框裏面,最早一段文字。$('div.post').find('.signature').text()
total_wordage: 總字數
note.note.author.total_wordage
followers_count: 總關注計數
note.note.author.followers_count
total_likes_count: 總喜歡計數
note.note.author.total_likes_count
有些字段命名就是從note.note這個json對象裏面獲取的,一開始我也不知道取什麼名字。
最終拼接的數據
/** * 存放數據容器 * @type {Array} */ let data = { article: { id: note.note.id, slug: note.note.slug, title: replaceText($post.find('.article .title').text()), content: replaceText($post.find('.article .show-content').html()), publishTime: replaceText($post.find('.article .publish-time').text()), wordage: $post.find('.article .wordage').text().match(/\d+/g)[0]*1, views_count: note.note.views_count, comments_count: note.note.comments_count, likes_count: note.note.likes_count }, author: { id: note.note.user_id, slug: $post.find('.avatar').attr('href').replace(/\/u\//, ""), avatar: $post.find('.avatar img').attr('src'), nickname: replaceText($post.find('.author .name a').text()), signature: replaceText($post.find('.signature').text()), total_wordage: note.note.author.total_wordage, followers_count: note.note.author.followers_count, total_likes_count: note.note.author.total_likes_count } };
和列表生成數據基本同樣,有一個區別。文件須要加一個標識,article_+ item.slug(文章訪問的id)
// 寫入數據, 文件不存在會自動建立 fs.writeFile(__dirname + '/data/article_' + item.slug + '.json', JSON.stringify({ status: 0, data: data }), function (err) { if (err) throw err; console.log('寫入完成'); });
基本就擼完了,看獲取詳情的完整代碼:
function getArticle(item) { // 拼接請求地址 let url = reptileUrl + '/p/' + item.slug; /** * 核心業務 * 發請求,解析數據,生成數據 */ superagent.get(url).end(function (err, res) { // 拋錯攔截 if (err) { return throw Error(err); } // 解析數據 let $ = cheerio.load(res.text); // 獲取容器,存放在變量裏,方便獲取 let $post = $('div.post'); // 獲取script裏的json數據 let note = JSON.parse($('script[data-name=page-data]').text()); /** * 存放數據容器 * @type {Array} */ let data = { article: { id: note.note.id, slug: note.note.slug, title: replaceText($post.find('.article .title').text()), content: replaceText($post.find('.article .show-content').html()), publishTime: replaceText($post.find('.article .publish-time').text()), wordage: $post.find('.article .wordage').text().match(/\d+/g)[0]*1, views_count: note.note.views_count, comments_count: note.note.comments_count, likes_count: note.note.likes_count }, author: { id: note.note.user_id, slug: $post.find('.avatar').attr('href').replace(/\/u\//, ""), avatar: $post.find('.avatar img').attr('src'), nickname: replaceText($post.find('.author .name a').text()), signature: replaceText($post.find('.signature').text()), total_wordage: note.note.author.total_wordage, followers_count: note.note.author.followers_count, total_likes_count: note.note.author.total_likes_count } }; // 生成數據 // 寫入數據, 文件不存在會自動建立 fs.writeFile(__dirname + '/data/article_' + item.slug + '.json', JSON.stringify({ status: 0, data: data }), function (err) { if (err) throw err; console.log('寫入完成'); }); }); }
你確定要問了,在哪裏調用了,
在上面獲取文章列表的請求end裏面底部隨便找個位置加上:
data.forEach(function (item) { getArticle(item); });
運行,你就會在data文件夾裏看到21個json文件。源文件,歡迎指正Bug。