2017.3.6 - 3.17
學習內(nèi)容:
- 學習nodejs數(shù)據(jù)挖掘基本想法
- 熟悉superagent模塊的基本接口
- 熟悉cheerio模塊的基本接口
- 學習范例挖掘Cnode首頁信息
- eventproxy模塊學習
- async模塊學習
- js變量提升
- 模擬post請求與get請求
詳細筆記:
1. 基本想法
nodejs項目通過superagent模塊包向網(wǎng)站發(fā)起有(或無)參數(shù)的get/post請求,獲取目標網(wǎng)頁的html源代碼
——>使用cherrio模塊包分析前一步抓取的html,使用方法類似Jquery
——>保存json數(shù)據(jù)到文本
2. superagent模塊接口整理
參考文獻:superagent文檔 整理稿
一個用于發(fā)起get/post請求的https模塊包
3. cherrio模塊接口整理
一個用于抓取當前頁面信息的模塊包
4. 簡單抓取頁面信息的核心代碼 ——————挖掘Cnode首頁
//通過get請求抓取
router.get('/', function(req, res, next) {
superAgent.get('https://cnodejs.org/')
.end(function (err, sres) {
if(err){
return next(err);
}
// sres.text 里面存儲著網(wǎng)頁的 html 內(nèi)容,將它傳給 cheerio.load 之后
// 就可以得到一個實現(xiàn)了 jquery 接口的變量,我們習慣性地將它命名為 `$`
var $ = cherrio.load(sres.text);
var items = [];
$('#topic_list .topic_title').each(function (idx, element) {
// 對每一個查找到的結(jié)果(idx, element),存入items數(shù)組?中
var $element = $(element);
items.push({
title: $element.attr('title'),
href: $element.attr('href'),
});
});
res.send(items);
})
});
5. js變量聲明提升,賦值不提升
參考博客:js中的變量提升hoisting
總結(jié):
JavaScript是函數(shù)級作用域(function-level scope)。只有在函數(shù)中才會創(chuàng)建新的作用域(適用局部變量)。
// 初始代碼
var v='Hello World';
(function(){
alert(v);
var v='I love you';
})()
// 運行結(jié)果 : 彈出 undefined
// 實際運行過程
var v='Hello World';
(function(){
var v; //變量聲明被提升
alert(v);
v='I love you'; //變量賦值未提升
})()
//對于函數(shù)級作用域的理解
function foo() {
var x = 1;
if (x) {
(function () {
var x = 2;
// some other code
}());
}
// x is still 1. //匿名函數(shù)中的作用域與foo函數(shù)的作用域無關(guān)
}
6. eventproxy模塊包API整理
一個用于監(jiān)聽多個函數(shù)并發(fā)執(zhí)行的模塊報(類似于計數(shù)器,等所有監(jiān)聽事件都冒泡表示完成,在執(zhí)行callback)
eventproxy——API
注冊單個異步并發(fā):
ep.all('tpl', 'data', function (tpl, data) {})
所有監(jiān)聽事件完成后觸發(fā)callback注冊重復異步并發(fā):
ep.after('got_file', files.length, function (list) {})
監(jiān)聽事件重復指定次數(shù)后,觸發(fā)callback注冊持續(xù)異步并發(fā):
ep.tail('tpl', 'data', function (tpl, data) {})
在所有監(jiān)聽事件執(zhí)行后,觸發(fā)callback;監(jiān)聽事件再次更新,仍然觸發(fā)callback
7. async模塊API整理
流程控制:簡化十種常見流程的處理
--- series(tasks, [callback]) (多個函數(shù)依次執(zhí)行,之間沒有數(shù)據(jù)交換)
有多個異步函數(shù)需要依次調(diào)用,一個完成之后才能執(zhí)行下一個。各函數(shù)之間沒有數(shù)據(jù)的交換,僅僅需要保證其執(zhí)行順序。
--- parallel(tasks, [callback]) (多個函數(shù)并行執(zhí)行)
并行執(zhí)行多個函數(shù),每個函數(shù)都是立即執(zhí)行,不需要等待其它函數(shù)先執(zhí)行。傳給最終callback的數(shù)組中的數(shù)據(jù)按照tasks中聲明的順序,而不是執(zhí)行完成的順序。
如果某個函數(shù)出錯,則立刻將err和已經(jīng)執(zhí)行完的函數(shù)的結(jié)果值傳給parallel最終的callback。其它未執(zhí)行完的函數(shù)的值不會傳到最終數(shù)據(jù),但要占個位置。
--- waterfall(tasks, [callback]) (多個函數(shù)依次執(zhí)行,且前一個的輸出為后一個的輸入)
與seires相似,按順序依次執(zhí)行多個函數(shù)。不同之處,每一個函數(shù)產(chǎn)生的值,都將傳給下一個函數(shù)。如果中途出錯,后面的函數(shù)將不會被執(zhí)行。錯誤信息以及之前產(chǎn)生的結(jié)果,將傳給waterfall最終的callback。集合處理:如何使用異步操作處理集合中的數(shù)據(jù)
forEach:對集合中每個元素進行異步操作
map:對集合中的每個元素通過異步操作得到另一個值,得到新的集合
filter:對集合中元素使用異步操作進行篩選,得到符合條件的集合
reject:與filter相似,只是判斷條件時正好相反,得到剩下的元素的集合
reduce:使用一個初始值同集合中每一個元素進行異步操作,最后得到一個唯一的結(jié)果
detect:得到集合中滿足條件的第一個數(shù)據(jù)
sortBy:對集合中的數(shù)據(jù)進行異步操作,再根據(jù)值從小到大排序
some/any:集合中是否有至少一個元素滿足條件
every/all:集合中是否每個元素都滿足條件
concat:對集合中的元素進行異步操作,將結(jié)果集合并成一個數(shù)組工具類:幾個常用的工具類
8. 定時抓取補充
https://github.com/node-schedule/node-schedule
9. jqury的API
$.map() 遍歷
$.trim() 字符串去首位的空格
10. 爬蟲相關(guān)的模塊介紹
爬蟲相關(guān)模塊
express框架詳細總結(jié)
11. 模擬post請求與get請求 規(guī)范整理
//模擬發(fā)起get請求
superAgent.get('http://flight.qunar.com/twell/flight/inter/search')
.query(queryString)
.set(headers)
.end(function (err, res) {
if (res.error)
throw new Error(res.error);
console.log(res.body);
});
****在get請求中需要注意的地方:****
1. 在chrome檢查——network——headers中查到看的request URL要去除查詢參數(shù)(?search/departCity=XXXXX等)
2. 在.query中發(fā)送查詢參數(shù),對應的post方法則是用send(data)的方式發(fā)送參數(shù)
//模擬發(fā)起post請求
superAgent.post('http://flights.ctrip.com/international/AjaxRequest/SearchFlights/AsyncSearchHandlerSOAII.ashx')
.set(ctripHeaders)
.type('form')
.send(data)
.end(function (err, res) {
// res是json對象
if (err){
return console.error(err);
}
console.dir(res.body);
})
****在post請求中的注意:****
1. 在chrome檢查——network中找準發(fā)起請求的URL,并且保證req的參數(shù)完整且正確
2. 在發(fā)送數(shù)據(jù)前通過.type('form') 聲明發(fā)送數(shù)據(jù)的格式
3. 注意返回數(shù)據(jù)的解析
json文件讀寫
sublime中的json格式化插件—— pretty json
遇到的問題
Q1:一個json數(shù)組在debug時,可以取到其中的值,但是在實際運行中,出現(xiàn)Cannot read property 'href' of undefined
A1:- id重復,找不到dom或找錯dom
- 數(shù)組越界,導致在debug時的前幾輪循環(huán)中可取,在整體運行中出錯。
- 重點理解nodejs的異步機制!需要在回調(diào)函數(shù)中操作dom,以防還未捕捉到dom
—————————
Q2:get請求抓取信息的數(shù)據(jù)結(jié)構(gòu)失敗
A2:出于安全保密,攜程網(wǎng)頁的數(shù)據(jù)信息由post請求得到,因此僅僅通過get請求無法得到目標html,需要在當前網(wǎng)頁發(fā)起post請求
參考鏈接
總結(jié):
- 通過一周學習,再次理解了上一學期中易混淆未理解“異步調(diào)用”、“get/post請求”的概念
異步調(diào)用,依次開啟多個函數(shù)入口,并發(fā)執(zhí)行,返回結(jié)果順序與入口執(zhí)行順序不一定相同,可以使用async模塊包來控制異步與同步的切換。
get請求:通過添加url中的查詢參數(shù),向當前網(wǎng)頁發(fā)出get請求,獲得返回數(shù)據(jù),不具備保密性。相當于得到了帶參數(shù)的url完整路徑,就得到了這個網(wǎng)頁的數(shù)據(jù)。
post請求:通過某個動作(如搜索按鈕、提交按鈕)向服務器后臺發(fā)送數(shù)據(jù)包,再收獲相應數(shù)據(jù),將得到的數(shù)據(jù)渲染到當前頁面,動態(tài)生成頁面,如果截取到當前網(wǎng)頁的url只能獲得一個空的HTML頁面框架,不具備有價值的數(shù)據(jù)。
- 對于新工具的學習使用能力還要加強
這一次的模擬請求,用到了postman這個軟件+插件進行網(wǎng)頁抓包(抓取客戶端向服務器發(fā)出的請求數(shù)據(jù)包,與服務器返回的響應數(shù)據(jù)包)與發(fā)包測試(模擬發(fā)出get/post請求),在初次使用中因不太理解請求原理+不熟悉使用方法,導致在盲目測試請求時浪費了很多時間。 - 爬蟲與反爬蟲:通過對反爬蟲的了解,反面思考爬蟲的設計思路
現(xiàn)在主流的幾個反爬蟲技術(shù):
1、限制查詢頻率,超過一定頻率,采取封號、封IP
2、通過圖片驗證碼等技術(shù)識別機器查詢與手工查詢
3、設計cookie值與cookie值的隨機有效domain
4、加密查詢的參數(shù)
因此通過爬去攜程與去哪兒,了解了兩家的反爬蟲核心:
攜程:
通過post請求傳輸參數(shù),發(fā)送的參數(shù)中包含"transNo"與"searchKey"兩個參數(shù),均為隨機加密參數(shù),每次查詢均獨立加密

去哪兒:
通過get請求傳輸參數(shù),查詢參數(shù)中包含"es"隨機參數(shù),每一次查詢都會產(chǎn)生不同的es參數(shù)。
因為缺少這兩個加密參數(shù),無法獲取加密規(guī)則,使得我們無法爬取攜程與去哪兒兩大門戶的機票信息,嗨呀真是太氣了……
