2017.3.6 - 3.17
学习内容:
- 学习nodejs数据挖掘基本想法
- 熟悉superagent模块的基本接口
- 熟悉cheerio模块的基本接口
- 学习范例挖掘Cnode首页信息
- eventproxy模块学习
- async模块学习
- js变量提升
- 模拟post请求与get请求
详细笔记:
1. 基本想法
nodejs项目通过superagent模块包向网站发起有(或无)参数的get/post请求,获取目标网页的html源代码
——>使用cherrio模块包分析前一步抓取的html,使用方法类似Jquery
——>保存json数据到文本
2. superagent模块接口整理
参考文献:superagent文档 整理稿
一个用于发起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 里面存储着网页的 html 内容,将它传给 cheerio.load 之后
// 就可以得到一个实现了 jquery 接口的变量,我们习惯性地将它命名为 `$`
var $ = cherrio.load(sres.text);
var items = [];
$('#topic_list .topic_title').each(function (idx, element) {
// 对每一个查找到的结果(idx, element),存入items数组Ø中
var $element = $(element);
items.push({
title: $element.attr('title'),
href: $element.attr('href'),
});
});
res.send(items);
})
});
5. js变量声明提升,赋值不提升
参考博客:js中的变量提升hoisting
总结:
JavaScript是函数级作用域(function-level scope)。只有在函数中才会创建新的作用域(适用局部变量)。
// 初始代码
var v='Hello World';
(function(){
alert(v);
var v='I love you';
})()
// 运行结果 : 弹出 undefined
// 实际运行过程
var v='Hello World';
(function(){
var v; //变量声明被提升
alert(v);
v='I love you'; //变量赋值未提升
})()
//对于函数级作用域的理解
function foo() {
var x = 1;
if (x) {
(function () {
var x = 2;
// some other code
}());
}
// x is still 1. //匿名函数中的作用域与foo函数的作用域无关
}
6. eventproxy模块包API整理
一个用于监听多个函数并发执行的模块报(类似于计数器,等所有监听事件都冒泡表示完成,在执行callback)
eventproxy——API
注册单个异步并发:
ep.all('tpl', 'data', function (tpl, data) {})
所有监听事件完成后触发callback注册重复异步并发:
ep.after('got_file', files.length, function (list) {})
监听事件重复指定次数后,触发callback注册持续异步并发:
ep.tail('tpl', 'data', function (tpl, data) {})
在所有监听事件执行后,触发callback;监听事件再次更新,仍然触发callback
7. async模块API整理
流程控制:简化十种常见流程的处理
--- series(tasks, [callback]) (多个函数依次执行,之间没有数据交换)
有多个异步函数需要依次调用,一个完成之后才能执行下一个。各函数之间没有数据的交换,仅仅需要保证其执行顺序。
--- parallel(tasks, [callback]) (多个函数并行执行)
并行执行多个函数,每个函数都是立即执行,不需要等待其它函数先执行。传给最终callback的数组中的数据按照tasks中声明的顺序,而不是执行完成的顺序。
如果某个函数出错,则立刻将err和已经执行完的函数的结果值传给parallel最终的callback。其它未执行完的函数的值不会传到最终数据,但要占个位置。
--- waterfall(tasks, [callback]) (多个函数依次执行,且前一个的输出为后一个的输入)
与seires相似,按顺序依次执行多个函数。不同之处,每一个函数产生的值,都将传给下一个函数。如果中途出错,后面的函数将不会被执行。错误信息以及之前产生的结果,将传给waterfall最终的callback。集合处理:如何使用异步操作处理集合中的数据
forEach:对集合中每个元素进行异步操作
map:对集合中的每个元素通过异步操作得到另一个值,得到新的集合
filter:对集合中元素使用异步操作进行筛选,得到符合条件的集合
reject:与filter相似,只是判断条件时正好相反,得到剩下的元素的集合
reduce:使用一个初始值同集合中每一个元素进行异步操作,最后得到一个唯一的结果
detect:得到集合中满足条件的第一个数据
sortBy:对集合中的数据进行异步操作,再根据值从小到大排序
some/any:集合中是否有至少一个元素满足条件
every/all:集合中是否每个元素都满足条件
concat:对集合中的元素进行异步操作,将结果集合并成一个数组工具类:几个常用的工具类
8. 定时抓取补充
https://github.com/node-schedule/node-schedule
9. jqury的API
$.map() 遍历
$.trim() 字符串去首位的空格
10. 爬虫相关的模块介绍
11. 模拟post请求与get请求 规范整理
//模拟发起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要去除查询参数(?search/departCity=XXXXX等)
2. 在.query中发送查询参数,对应的post方法则是用send(data)的方式发送参数
//模拟发起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中找准发起请求的URL,并且保证req的参数完整且正确
2. 在发送数据前通过.type('form') 声明发送数据的格式
3. 注意返回数据的解析
json文件读写
sublime中的json格式化插件—— pretty json
遇到的问题
Q1:一个json数组在debug时,可以取到其中的值,但是在实际运行中,出现Cannot read property 'href' of undefined
A1:- id重复,找不到dom或找错dom
- 数组越界,导致在debug时的前几轮循环中可取,在整体运行中出错。
- 重点理解nodejs的异步机制!需要在回调函数中操作dom,以防还未捕捉到dom
—————————
Q2:get请求抓取信息的数据结构失败
A2:出于安全保密,携程网页的数据信息由post请求得到,因此仅仅通过get请求无法得到目标html,需要在当前网页发起post请求
参考链接
总结:
- 通过一周学习,再次理解了上一学期中易混淆未理解“异步调用”、“get/post请求”的概念
异步调用,依次开启多个函数入口,并发执行,返回结果顺序与入口执行顺序不一定相同,可以使用async模块包来控制异步与同步的切换。
get请求:通过添加url中的查询参数,向当前网页发出get请求,获得返回数据,不具备保密性。相当于得到了带参数的url完整路径,就得到了这个网页的数据。
post请求:通过某个动作(如搜索按钮、提交按钮)向服务器后台发送数据包,再收获相应数据,将得到的数据渲染到当前页面,动态生成页面,如果截取到当前网页的url只能获得一个空的HTML页面框架,不具备有价值的数据。
- 对于新工具的学习使用能力还要加强
这一次的模拟请求,用到了postman这个软件+插件进行网页抓包(抓取客户端向服务器发出的请求数据包,与服务器返回的响应数据包)与发包测试(模拟发出get/post请求),在初次使用中因不太理解请求原理+不熟悉使用方法,导致在盲目测试请求时浪费了很多时间。 - 爬虫与反爬虫:通过对反爬虫的了解,反面思考爬虫的设计思路
现在主流的几个反爬虫技术:
1、限制查询频率,超过一定频率,采取封号、封IP
2、通过图片验证码等技术识别机器查询与手工查询
3、设计cookie值与cookie值的随机有效domain
4、加密查询的参数
因此通过爬去携程与去哪儿,了解了两家的反爬虫核心:
携程:
通过post请求传输参数,发送的参数中包含"transNo"与"searchKey"两个参数,均为随机加密参数,每次查询均独立加密
去哪儿:
通过get请求传输参数,查询参数中包含"es"随机参数,每一次查询都会产生不同的es参数。
因为缺少这两个加密参数,无法获取加密规则,使得我们无法爬取携程与去哪儿两大门户的机票信息,嗨呀真是太气了……