摘要:可以通過傳入待刪除數(shù)組元素組成的數(shù)組進(jìn)行一次性刪除。如果后臺(tái)返回的為表示登錄的已失效,需要重新執(zhí)行。等所有的異步執(zhí)行完畢后,再執(zhí)行回調(diào)函數(shù)。回調(diào)函數(shù)的參數(shù)是每個(gè)函數(shù)返回?cái)?shù)據(jù)組成的數(shù)組。
其實(shí)在早之前,就做過立馬理財(cái)?shù)匿N售額統(tǒng)計(jì),只不過是用前端js寫的,需要在首頁的console調(diào)試面板里粘貼一段代碼執(zhí)行,點(diǎn)擊這里。主要是通過定時(shí)爬取https://www.lmlc.com/s/web/home/user_buying異步接口來獲取數(shù)據(jù)。然后通過一定的排重算法來獲取最終的數(shù)據(jù)。但是這樣做有以下缺點(diǎn):
代碼只能在瀏覽器窗口下運(yùn)行,關(guān)閉瀏覽器或者電腦就失效了
只能爬取一個(gè)頁面的數(shù)據(jù),不能整合其他頁面的數(shù)據(jù)
爬取的數(shù)據(jù)無法存儲(chǔ)到本地
上面的異步接口數(shù)據(jù)會(huì)部分過濾,導(dǎo)致我們的排重算法失效
由于最近學(xué)習(xí)了node爬蟲相關(guān)知識(shí),我們可以在后臺(tái)自己模擬請(qǐng)求,爬取頁面數(shù)據(jù)。并且我開通了阿里云服務(wù)器,可以把代碼放到云端跑。這樣,1、2、3都可以解決。4是因?yàn)橹安恢肋@個(gè)ajax接口是每三分鐘更新一次,這樣我們可以根據(jù)這個(gè)來排重,確保數(shù)據(jù)不會(huì)重復(fù)。說到爬蟲,大家想到的比較多的還是python,確實(shí)python有Scrapy等成熟的框架,可以實(shí)現(xiàn)很強(qiáng)大的爬取功能。但是node也有自身的優(yōu)點(diǎn),憑借強(qiáng)大的異步特性,可以很輕松的實(shí)現(xiàn)高效的異步并發(fā)請(qǐng)求,節(jié)省cpu的開銷。其實(shí)node爬蟲還是比較簡(jiǎn)單的,下面我們就來分析整個(gè)爬蟲爬取的流程和最終如何展示數(shù)據(jù)的。
線上地址
一、爬蟲流程我們最終的目標(biāo)是實(shí)現(xiàn)爬取立馬理財(cái)每日的銷售額,并知道賣了哪些產(chǎn)品,每個(gè)產(chǎn)品又被哪些用戶在什么時(shí)間點(diǎn)買的。首先,介紹下爬蟲爬取的主要步驟:
1. 結(jié)構(gòu)分析我們要爬取頁面的數(shù)據(jù),第一步當(dāng)然是要先分析清楚頁面結(jié)構(gòu),要爬哪些頁面,頁面的結(jié)構(gòu)是怎樣的,需不需要登錄;有沒有ajax接口,返回什么樣的數(shù)據(jù)等。
2. 數(shù)據(jù)抓取分析清楚要爬取哪些頁面和ajax,就要去抓取數(shù)據(jù)了。如今的網(wǎng)頁的數(shù)據(jù),大體分為同步頁面和ajax接口。同步頁面數(shù)據(jù)的抓取就需要我們先分析網(wǎng)頁的結(jié)構(gòu),python抓取數(shù)據(jù)一般是通過正則表達(dá)式匹配來獲取需要的數(shù)據(jù);node有一個(gè)cheerio的工具,可以將獲取的頁面內(nèi)容轉(zhuǎn)換成jquery對(duì)象,然后就可以用jquery強(qiáng)大的dom API來獲取節(jié)點(diǎn)相關(guān)數(shù)據(jù), 其實(shí)大家看源碼,這些API本質(zhì)也就是正則匹配。ajax接口數(shù)據(jù)一般都是json格式的,處理起來還是比較簡(jiǎn)單的。
3. 數(shù)據(jù)存儲(chǔ)抓取的數(shù)據(jù)后,會(huì)做簡(jiǎn)單的篩選,然后將需要的數(shù)據(jù)先保存起來,以便后續(xù)的分析處理。當(dāng)然我們可以用MySQL和Mongodb等數(shù)據(jù)庫存儲(chǔ)數(shù)據(jù)。這里,我們?yōu)榱朔奖?,直接采用文件存?chǔ)。
4. 數(shù)據(jù)分析因?yàn)槲覀冏罱K是要展示數(shù)據(jù)的,所以我們要將原始的數(shù)據(jù)按照一定維度去處理分析,然后返回給客戶端。這個(gè)過程可以在存儲(chǔ)的時(shí)候去處理,也可以在展示的時(shí)候,前端發(fā)送請(qǐng)求,后臺(tái)取出存儲(chǔ)的數(shù)據(jù)再處理。這個(gè)看我們要怎么展示數(shù)據(jù)了。
5. 結(jié)果展示做了這么多工作,一點(diǎn)展示輸出都沒有,怎么甘心呢?這又回到了我們的老本行,前端展示頁面大家應(yīng)該都很熟悉了。將數(shù)據(jù)展示出來才更直觀,方便我們分析統(tǒng)計(jì)。
二、爬蟲常用庫介紹 1. SuperagentSuperagent是個(gè)輕量的的http方面的庫,是nodejs里一個(gè)非常方便的客戶端請(qǐng)求代理模塊,當(dāng)我們需要進(jìn)行g(shù)et、post、head等網(wǎng)絡(luò)請(qǐng)求時(shí),嘗試下它吧。
2. CheerioCheerio大家可以理解成一個(gè) Node.js 版的 jquery,用來從網(wǎng)頁中以 css selector 取數(shù)據(jù),使用方式跟 jquery 一模一樣。
3. AsyncAsync是一個(gè)流程控制工具包,提供了直接而強(qiáng)大的異步功能mapLimit(arr, limit, iterator, callback),我們主要用到這個(gè)方法,大家可以去看看官網(wǎng)的API。
4. arr-delarr-del是我自己寫的一個(gè)刪除數(shù)組元素方法的工具。可以通過傳入待刪除數(shù)組元素index組成的數(shù)組進(jìn)行一次性刪除。
5. arr-sortarr-sort是我自己寫的一個(gè)數(shù)組排序方法的工具。可以根據(jù)一個(gè)或者多個(gè)屬性進(jìn)行排序,支持嵌套的屬性。而且可以再每個(gè)條件中指定排序的方向,并支持傳入比較函數(shù)。
三、頁面結(jié)構(gòu)分析先屢一下我們爬取的思路。立馬理財(cái)線上的產(chǎn)品主要是定期和立馬金庫(最新上線的光大銀行理財(cái)產(chǎn)品因?yàn)槭掷m(xù)比較麻煩,而且起投金額高,基本沒人買,這里不統(tǒng)計(jì))。定期我們可以爬取理財(cái)頁的ajax接口:https://www.lmlc.com/web/product/product_list?pageSize=10&pageNo=1&type=0。(update: 定期近期沒貨,可能看不到數(shù)據(jù),可以看1月19號(hào)以前的)數(shù)據(jù)如下圖所示:
這里包含了所有線上正在銷售的定期產(chǎn)品,ajax數(shù)據(jù)只有產(chǎn)品本身相關(guān)的信息,比如產(chǎn)品id、籌集金額、當(dāng)前銷售額、年化收益率、投資天數(shù)等,并沒有產(chǎn)品被哪些用戶購買的信息。所以我們需要帶著id參數(shù)去它的產(chǎn)品詳情頁爬取,比如立馬聚財(cái)-12月期HLB01239511。詳情頁有一欄投資記錄,里邊包含了我們需要的信息,如下圖所示:
但是,詳情頁需要我們?cè)诘卿浀臓顟B(tài)下才可以查看,這就需要我們帶著cookie去訪問,而且cookie是有有效期限制的,如何保持我們cookie一直在登錄態(tài)呢?請(qǐng)看后文。
其實(shí)立馬金庫也有類似的ajax接口:https://www.lmlc.com/web/product/product_list?pageSize=10&pageNo=1&type=1,但是里邊的相關(guān)數(shù)據(jù)都是寫死的,沒有意義。而且金庫的詳情頁也沒有投資記錄信息。這就需要我們爬取一開始說的首頁的ajax接口:https://www.lmlc.com/s/web/home/user_buying。但是后來才發(fā)現(xiàn)這個(gè)接口是三分鐘更新一次,就是說后臺(tái)每隔三分鐘向服務(wù)器請(qǐng)求一次數(shù)據(jù)。而一次是10條數(shù)據(jù),所以如果在三分鐘內(nèi),購買產(chǎn)品的記錄數(shù)超過10條,數(shù)據(jù)就會(huì)有遺漏。這是沒有辦法的,所以立馬金庫的統(tǒng)計(jì)數(shù)據(jù)會(huì)比真實(shí)的偏少。
四、爬蟲代碼分析 1. 獲取登錄cookie因?yàn)楫a(chǎn)品詳情頁需要登錄,所以我們要先拿到登錄的cookie才行。getCookie方法如下:
function getCookie() { superagent.post("https://www.lmlc.com/user/s/web/logon") .type("form") .send({ phone: phone, password: password, productCode: "LMLC", origin: "PC" }) .end(function(err, res) { if (err) { handleErr(err.message); return; } cookie = res.header["set-cookie"]; //從response中得到cookie emitter.emit("setCookeie"); }) }
phone和password參數(shù)是從命令行里傳進(jìn)來的,就是立馬理財(cái)用手機(jī)號(hào)登錄的賬號(hào)和密碼。我們用superagent去模擬請(qǐng)求立馬理財(cái)?shù)卿浗涌冢?b>https://www.lmlc.com/user/s/web/logon。傳入相應(yīng)的參數(shù),在回調(diào)中,我們拿到header的set-cookie信息,并發(fā)出一個(gè)setCookeie事件。因?yàn)槲覀冊(cè)O(shè)置了監(jiān)聽事件:emitter.on("setCookie", requestData),所以一旦獲取cookie,我們就會(huì)去執(zhí)行requestData方法。
2. 理財(cái)頁ajax的爬取requestData方法的代碼如下:
function requestData() { superagent.get("https://www.lmlc.com/web/product/product_list?pageSize=100&pageNo=1&type=0") .end(function(err,pres){ // 常規(guī)的錯(cuò)誤處理 if (err) { handleErr(err.message); return; } // 在這里清空數(shù)據(jù),避免一個(gè)文件被同時(shí)寫入 if(clearProd){ fs.writeFileSync("data/prod.json", JSON.stringify([])); clearProd = false; } let addData = JSON.parse(pres.text).data; let formatedAddData = formatData(addData.result); let pageUrls = []; if(addData.totalPage > 1){ handleErr("產(chǎn)品個(gè)數(shù)超過100個(gè)!"); return; } for(let i=0,len=addData.result.length; i代碼很長,getDetailData函數(shù)代碼后面分析。
請(qǐng)求的ajax接口是個(gè)分頁接口,因?yàn)橐话阍谑鄣目偖a(chǎn)品數(shù)不會(huì)超過10條,我們這里設(shè)置參數(shù)pageSize為100,這樣就可以一次性獲取所有產(chǎn)品。
clearProd是全局reset信號(hào),每天0點(diǎn)整的時(shí)候,會(huì)清空prod(定期產(chǎn)品)和user(首頁用戶)數(shù)據(jù)。
因?yàn)橛袝r(shí)候產(chǎn)品較少會(huì)采用搶購的方式,比如每天10點(diǎn),這樣在每天10點(diǎn)的時(shí)候數(shù)據(jù)會(huì)更新很快,我們必須要增加爬取的頻次,以防丟失數(shù)據(jù)。所以針對(duì)預(yù)售產(chǎn)品即buyStartTime大于當(dāng)前時(shí)間,我們要記錄下,并設(shè)定計(jì)時(shí)器,當(dāng)開售時(shí),調(diào)整爬取頻次為1次/秒,見setPreId方法。
如果沒有正在售賣的產(chǎn)品,即pageUrls為空,我們將爬取的頻次設(shè)置為最大32s。
requestData函數(shù)的這部分代碼主要記錄下是否有新產(chǎn)品,如果有的話,新建一個(gè)對(duì)象,記錄產(chǎn)品信息,push到prod數(shù)組里。prod.json數(shù)據(jù)結(jié)構(gòu)如下:
[{ "productName": "立馬聚財(cái)-12月期HLB01230901", "financeTotalAmount": 1000000, "productId": "201801151830PD84123120", "yearReturnRate": 6.4, "investementDays": 364, "interestStartTime": "2018年01月23日", "interestEndTime": "2019年01月22日", "getDataTime": 1516118401299, "alreadyBuyAmount": 875000, "records": [ { "username": "劉**", "buyTime": 1516117093472, "buyAmount": 30000, "uniqueId": "劉**151611709347230,000元" }, { "username": "劉**", "buyTime": 1516116780799, "buyAmount": 50000, "uniqueId": "劉**151611678079950,000元" }] }]是一個(gè)對(duì)象數(shù)組,每個(gè)對(duì)象表示一個(gè)新產(chǎn)品,records屬性記錄著售賣信息。
3. 產(chǎn)品詳情頁的爬取我們?cè)倏聪耮etDetailData的代碼:
function getDetailData(){ // 請(qǐng)求用戶信息接口,來判斷登錄是否還有效,在產(chǎn)品詳情頁判斷麻煩還要造成五次登錄請(qǐng)求 superagent .post("https://www.lmlc.com/s/web/m/user_info") .set("Cookie", cookie) .end(function(err,pres){ // 常規(guī)的錯(cuò)誤處理 if (err) { handleErr(err.message); return; } let retcode = JSON.parse(pres.text).retcode; if(retcode === 410){ handleErr("登陸cookie已失效,嘗試重新登陸..."); getCookie(); return; } var reptileLink = function(url,callback){ // 如果爬取頁面有限制爬取次數(shù),這里可設(shè)置延遲 console.log( "正在爬取產(chǎn)品詳情頁面:" + url); superagent .get(url) .set("Cookie", cookie) .end(function(err,pres){ // 常規(guī)的錯(cuò)誤處理 if (err) { handleErr(err.message); return; } var $ = cheerio.load(pres.text); var records = []; var $table = $(".buy-records table"); if(!$table.length){ $table = $(".tabcontent table"); } var $tr = $table.find("tr").slice(1); $tr.each(function(){ records.push({ username: $("td", $(this)).eq(0).text(), buyTime: parseInt($("td", $(this)).eq(1).attr("data-time").replace(/,/g, "")), buyAmount: parseFloat($("td", $(this)).eq(2).text().replace(/,/g, "")), uniqueId: $("td", $(this)).eq(0).text() + $("td", $(this)).eq(1).attr("data-time").replace(/,/g, "") + $("td", $(this)).eq(2).text() }) }); callback(null, { productId: url.split("?id=")[1], records: records }); }); }; async.mapLimit(pageUrls, 10 ,function (url, callback) { reptileLink(url, callback); }, function (err,result) { let time = (new Date()).format("yyyy-MM-dd hh:mm:ss"); console.log(`所有產(chǎn)品詳情頁爬取完畢,時(shí)間:${time}`.info); let oldRecord = JSON.parse(fs.readFileSync("data/prod.json", "utf-8")); let counts = []; for(let i=0,len=result.length; i=0 && maxNum <= 2){ delay = delay + 1000; } if(maxNum >=8 && maxNum <= 10){ delay = delay/2; } // 每天0點(diǎn),prod數(shù)據(jù)清空,排除這個(gè)情況 if(maxNum == 10 && (time2 - time1 >= 60*1000)){ handleErr("部分?jǐn)?shù)據(jù)可能丟失!"); } if(delay <= 1000){ delay = 1000; } if(delay >= 32*1000){ delay = 32*1000; } return delay } if(oldDelay != delay){ clearInterval(timer); timer = setInterval(function(){ requestData(); }, delay); } fs.writeFileSync("data/prod.json", JSON.stringify(oldRecord)); }) }); } 我們先去請(qǐng)求用戶信息接口,來判斷登錄是否還有效,因?yàn)樵诋a(chǎn)品詳情頁判斷麻煩還要造成五次登錄請(qǐng)求。帶cookie請(qǐng)求很簡(jiǎn)單,在post后面set下我們之前得到的cookie即可:.set("Cookie", cookie)。如果后臺(tái)返回的retcode為410表示登錄的cookie已失效,需要重新執(zhí)行g(shù)etCookie()。這樣就能保證爬蟲一直在登錄狀態(tài)。
async的mapLimit方法,會(huì)將pageUrls進(jìn)行并發(fā)請(qǐng)求,一次并發(fā)量為10。對(duì)于每個(gè)pageUrl會(huì)執(zhí)行reptileLink方法。等所有的異步執(zhí)行完畢后,再執(zhí)行回調(diào)函數(shù)。回調(diào)函數(shù)的result參數(shù)是每個(gè)reptileLink函數(shù)返回?cái)?shù)據(jù)組成的數(shù)組。
reptileLink函數(shù)是獲取產(chǎn)品詳情頁的投資記錄列表信息,uniqueId是由已知的username、buyTime、buyAmount參數(shù)組成的字符串,用來排重的。
async的回調(diào)主要是將最新的投資記錄信息寫入對(duì)應(yīng)的產(chǎn)品對(duì)象里,同時(shí)生成了counts數(shù)組。counts數(shù)組是每個(gè)產(chǎn)品這次爬取新增的售賣記錄個(gè)數(shù)組成的數(shù)組,和delay一起傳入getNewDelay函數(shù)。getNewDelay動(dòng)態(tài)調(diào)節(jié)爬取頻次,counts是調(diào)節(jié)delay的唯一依據(jù)。delay過大可能產(chǎn)生數(shù)據(jù)丟失,過小會(huì)增加服務(wù)器負(fù)擔(dān),可能會(huì)被管理員封ip。這里設(shè)置delay最大值為32,最小值為1。
4. 首頁用戶ajax爬取先上代碼:
function requestData1() { superagent.get(ajaxUrl1) .end(function(err,pres){ // 常規(guī)的錯(cuò)誤處理 if (err) { handleErr(err.message); return; } let newData = JSON.parse(pres.text).data; let formatNewData = formatData1(newData); // 在這里清空數(shù)據(jù),避免一個(gè)文件被同時(shí)寫入 if(clearUser){ fs.writeFileSync("data/user.json", ""); clearUser = false; } let data = fs.readFileSync("data/user.json", "utf-8"); if(!data){ fs.writeFileSync("data/user.json", JSON.stringify(formatNewData)); let time = (new Date()).format("yyyy-MM-dd hh:mm:ss"); console.log((`首頁用戶購買ajax爬取完畢,時(shí)間:${time}`).silly); }else{ let oldData = JSON.parse(data); let addData = []; // 排重算法,如果uniqueId不一樣那肯定是新生成的,否則看時(shí)間差如果是0(三分鐘內(nèi)請(qǐng)求多次)或者三分鐘則是舊數(shù)據(jù) for(let i=0, len=formatNewData.length; iuser.js的爬取和prod.js類似,這里主要想說一下如何排重的。user.json數(shù)據(jù)格式如下:
[ { "payAmount": 5067.31, "productId": "jsfund", "productName": "立馬金庫", "productType": 6, "time": 1548489, "username": "鄭**", "buyTime": 1516118397758, "uniqueId": "5067.31jsfund鄭**" }, { "payAmount": 30000, "productId": "201801151830PD84123120", "productName": "立馬聚財(cái)-12月期HLB01230901", "productType": 0, "time": 1306573, "username": "劉**", "buyTime": 1516117199684, "uniqueId": "30000201801151830PD84123120劉**" }]和產(chǎn)品詳情頁類似,我們也生成一個(gè)uniqueId參數(shù)用來排除,它是payAmount、productId、username參數(shù)的拼成的字符串。如果uniqueId不一樣,那肯定是一條新的記錄。如果相同那一定是一條新記錄嗎?答案是否定的。因?yàn)檫@個(gè)接口數(shù)據(jù)是三分鐘更新一次,而且給出的時(shí)間是相對(duì)時(shí)間,即數(shù)據(jù)更新時(shí)的時(shí)間減去購買的時(shí)間。所以每次更新后,即使是同一條記錄,時(shí)間也會(huì)不一樣。那如何排重呢?其實(shí)很簡(jiǎn)單,如果uniqueId一樣,我們就判斷這個(gè)buyTime,如果buyTime的差正好接近180s,那么幾乎可以肯定是舊數(shù)據(jù)。如果同一個(gè)人正好在三分鐘后購買同一個(gè)產(chǎn)品相同的金額那我也沒轍了,哈哈。
5. 零點(diǎn)整合數(shù)據(jù)每天零點(diǎn)我們需要整理user.json和prod.json數(shù)據(jù),生成最終的數(shù)據(jù)。代碼:
let globalTimer = setInterval(function(){ let nowTime = +new Date(); let nowStr = (new Date()).format("hh:mm:ss"); let max = nowTime; let min = nowTime - 24*60*60*1000; // 每天00:00分的時(shí)候?qū)懭氘?dāng)天的數(shù)據(jù) if(nowStr === "00:00:00"){ // 先保存數(shù)據(jù) let prod = JSON.parse(fs.readFileSync("data/prod.json", "utf-8")); let user = JSON.parse(fs.readFileSync("data/user.json", "utf-8")); let lmlc = JSON.parse(JSON.stringify(prod)); // 清空緩存數(shù)據(jù) clearProd = true; clearUser = true; // 不足一天的不統(tǒng)計(jì) // if(nowTime - initialTime < 24*60*60*1000) return // 篩選prod.records數(shù)據(jù) for(let i=0, len=prod.length; i= max){ delArr1.push(j); } } sort.delArrByIndex(lmlc[i].records, delArr1); } // 刪掉prod.records為空的數(shù)據(jù) let delArr2 = []; for(let i=0, len=lmlc.length; i = min && user[i].buyTime < max){ lmlc[0].records.push({ "username": user[i].username, "buyTime": user[i].buyTime, "buyAmount": user[i].payAmount, }); } } // 刪除無用屬性,按照時(shí)間排序 lmlc[0].records.sort(function(a,b){return a.buyTime - b.buyTime}); for(let i=1, len=lmlc.length; i globalTimer是個(gè)全局定時(shí)器,每隔1s執(zhí)行一次,當(dāng)時(shí)間為00:00:00時(shí),clearProd和clearUser全局參數(shù)為true,這樣在下次爬取過程時(shí)會(huì)清空user.json和prod.json文件。沒有同步清空是因?yàn)榉乐苟嗵幫瑫r(shí)修改同一文件報(bào)錯(cuò)。取出user.json里的所有金庫記錄,獲取當(dāng)天金庫相關(guān)信息,生成一條立馬金庫的prod信息并unshift進(jìn)prod.json里。刪除一些無用屬性,排序數(shù)組最終生成帶有當(dāng)天時(shí)間戳的json文件,如:20180101.json。
五、前端展示 1、整體思路前端總共就兩個(gè)頁面,首頁和詳情頁,首頁主要展示實(shí)時(shí)銷售額、某一時(shí)間段內(nèi)的銷售情況、具體某天的銷售情況。詳情頁展示某天的具體某一產(chǎn)品銷售情況。頁面有兩個(gè)入口,而且比較簡(jiǎn)單,這里我們采用gulp來打包壓縮構(gòu)建前端工程。后臺(tái)用express搭建的,匹配到路由,從data文件夾里取到數(shù)據(jù)再分析處理再返回給前端。
2、前端用到的組件介紹Echarts
Echarts是一個(gè)繪圖利器,百度公司不可多得的良心之作。能方便的繪制各種圖形,官網(wǎng)已經(jīng)更新到4.0了,功能更加強(qiáng)大。我們這里主要用到的是直方圖。
DataTables
Datatables是一款jquery表格插件。它是一個(gè)高度靈活的工具,可以將任何HTML表格添加高級(jí)的交互功能。功能非常強(qiáng)大,有豐富的API,大家可以去官網(wǎng)學(xué)習(xí)。
Datepicker
Datepicker是一款基于jquery的日期選擇器,需要的功能基本都有,主要樣式比較好看,比jqueryUI官網(wǎng)的Datepicker好看太多。
3、gulp配置gulp配置比較簡(jiǎn)單,代碼如下:
var gulp = require("gulp"); var uglify = require("gulp-uglify"); var less = require("gulp-less"); var minifyCss = require("gulp-minify-css"); var livereload = require("gulp-livereload"); var connect = require("gulp-connect"); var minimist = require("minimist"); var babel = require("gulp-babel"); var knownOptions = { string: "env", default: { env: process.env.NODE_ENV || "production" } }; var options = minimist(process.argv.slice(2), knownOptions); // js文件壓縮 gulp.task("minify-js", function() { gulp.src("src/js/*.js") .pipe(babel({ presets: ["es2015"] })) .pipe(uglify()) .pipe(gulp.dest("dist/")); }); // js移動(dòng)文件 gulp.task("move-js", function() { gulp.src("src/js/*.js") .pipe(babel({ presets: ["es2015"] })) .pipe(gulp.dest("dist/")) .pipe(connect.reload()); }); // less編譯 gulp.task("compile-less", function() { gulp.src("src/css/*.less") .pipe(less()) .pipe(gulp.dest("dist/")) .pipe(connect.reload()); }); // less文件編譯壓縮 gulp.task("compile-minify-css", function() { gulp.src("src/css/*.less") .pipe(less()) .pipe(minifyCss()) .pipe(gulp.dest("dist/")); }); // html頁面自動(dòng)刷新 gulp.task("html", function () { gulp.src("views/*.html") .pipe(connect.reload()); }); // 頁面自動(dòng)刷新啟動(dòng) gulp.task("connect", function() { connect.server({ livereload: true }); }); // 監(jiān)測(cè)文件的改動(dòng) gulp.task("watch", function() { gulp.watch("src/css/*.less", ["compile-less"]); gulp.watch("src/js/*.js", ["move-js"]); gulp.watch("views/*.html", ["html"]); }); // 激活瀏覽器livereload友好提示 gulp.task("tip", function() { console.log(" <----- 請(qǐng)用chrome瀏覽器打開 http://localhost:5000 頁面,并激活livereload插件 -----> "); }); if (options.env === "development") { gulp.task("default", ["move-js", "compile-less", "connect", "watch", "tip"]); }else{ gulp.task("default", ["minify-js", "compile-minify-css"]); }開發(fā)和生產(chǎn)環(huán)境都是將文件打包到dist目錄。不同的是:開發(fā)環(huán)境只是編譯es6和less文件;生產(chǎn)環(huán)境會(huì)再壓縮混淆。支持livereload插件,在開發(fā)環(huán)境下,文件改動(dòng)會(huì)自動(dòng)刷新頁面。
后記至此,一個(gè)完整的爬蟲就完成了。其實(shí)我覺得最需要花時(shí)間的是在分析頁面結(jié)構(gòu),處理數(shù)據(jù)還有解決各種問題,比如如何保持一直在登錄狀態(tài)等。
本爬蟲代碼只做研究學(xué)習(xí)用處,禁止用作任何商業(yè)分析。再說,統(tǒng)計(jì)的數(shù)據(jù)也不準(zhǔn)確。
因?yàn)榇a開源,希望大家照著代碼去爬取其他網(wǎng)站,如果都拿立馬理財(cái)來爬,估計(jì)服務(wù)器會(huì)承受不了的額。
歡迎大家star學(xué)習(xí)交流:線上地址 | github地址 | 我的博客
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/107523.html
摘要:剩下的同學(xué),我們繼續(xù)了可以看出,作為一個(gè)完善的電商網(wǎng)站,尚妝網(wǎng)有著普通電商網(wǎng)站所擁有的主要的元素,包括分類,分頁,主題等等。 系列教程 手把手教你寫電商爬蟲-第一課 找個(gè)軟柿子捏捏 如果沒有看過第一課的朋友,請(qǐng)先移步第一課,第一課講了一些基礎(chǔ)性的東西,通過軟柿子切糕王子這個(gè)電商網(wǎng)站好好的練了一次手,相信大家都應(yīng)該對(duì)寫爬蟲的流程有了一個(gè)大概的了解,那么這課咱們就話不多說,正式上戰(zhàn)場(chǎng),對(duì)壘...
摘要:剩下的同學(xué),我們繼續(xù)了可以看出,作為一個(gè)完善的電商網(wǎng)站,尚妝網(wǎng)有著普通電商網(wǎng)站所擁有的主要的元素,包括分類,分頁,主題等等。 系列教程 手把手教你寫電商爬蟲-第一課 找個(gè)軟柿子捏捏 如果沒有看過第一課的朋友,請(qǐng)先移步第一課,第一課講了一些基礎(chǔ)性的東西,通過軟柿子切糕王子這個(gè)電商網(wǎng)站好好的練了一次手,相信大家都應(yīng)該對(duì)寫爬蟲的流程有了一個(gè)大概的了解,那么這課咱們就話不多說,正式上戰(zhàn)場(chǎng),對(duì)壘...
摘要:之前寫了一個(gè)電商爬蟲系列的文章,簡(jiǎn)單的給大家展示了一下爬蟲從入門到進(jìn)階的路徑,但是作為一個(gè)永遠(yuǎn)走在時(shí)代前沿的科技工作者,我們從來都不能停止。金融數(shù)據(jù)實(shí)在是價(jià)值大,維度多,來源廣。由于也是一種,因此通常來說,在中抽取某個(gè)元素是通過來做的。 相關(guān)教程: 手把手教你寫電商爬蟲-第一課 找個(gè)軟柿子捏捏 手把手教你寫電商爬蟲-第二課 實(shí)戰(zhàn)尚妝網(wǎng)分頁商品采集爬蟲 手把手教你寫電商爬蟲-第三課 實(shí)戰(zhàn)...
摘要:之前寫了一個(gè)電商爬蟲系列的文章,簡(jiǎn)單的給大家展示了一下爬蟲從入門到進(jìn)階的路徑,但是作為一個(gè)永遠(yuǎn)走在時(shí)代前沿的科技工作者,我們從來都不能停止。金融數(shù)據(jù)實(shí)在是價(jià)值大,維度多,來源廣。由于也是一種,因此通常來說,在中抽取某個(gè)元素是通過來做的。 相關(guān)教程: 手把手教你寫電商爬蟲-第一課 找個(gè)軟柿子捏捏 手把手教你寫電商爬蟲-第二課 實(shí)戰(zhàn)尚妝網(wǎng)分頁商品采集爬蟲 手把手教你寫電商爬蟲-第三課 實(shí)戰(zhàn)...
摘要:和前面幾節(jié)課類似的分析這節(jié)課就不做了,對(duì)于分頁,請(qǐng)求什么的,大家可以直接參考前面的四節(jié)課,這一刻主要特別的是,我們?cè)诓杉唐返耐瑫r(shí),會(huì)將京東的商品評(píng)價(jià)采集下來。 系列教程: 手把手教你寫電商爬蟲-第一課 找個(gè)軟柿子捏捏 手把手教你寫電商爬蟲-第二課 實(shí)戰(zhàn)尚妝網(wǎng)分頁商品采集爬蟲 手把手教你寫電商爬蟲-第三課 實(shí)戰(zhàn)尚妝網(wǎng)AJAX請(qǐng)求處理和內(nèi)容提取 手把手教你寫電商爬蟲-第四課 淘寶網(wǎng)商品爬...
閱讀 2765·2021-11-24 09:39
閱讀 1657·2021-09-28 09:35
閱讀 1128·2021-09-06 15:02
閱讀 1324·2021-07-25 21:37
閱讀 2737·2019-08-30 15:53
閱讀 3655·2019-08-30 14:07
閱讀 724·2019-08-30 11:07
閱讀 3529·2019-08-29 18:36