Nodejs爬虫——机票查询学习笔记(1).md

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. 爬虫相关的模块介绍

爬虫相关模块
express框架详细总结

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请求
    参考链接

总结:

  1. 通过一周学习,再次理解了上一学期中易混淆未理解“异步调用”、“get/post请求”的概念
    异步调用,依次开启多个函数入口,并发执行,返回结果顺序与入口执行顺序不一定相同,可以使用async模块包来控制异步与同步的切换。
    get请求:通过添加url中的查询参数,向当前网页发出get请求,获得返回数据,不具备保密性。相当于得到了带参数的url完整路径,就得到了这个网页的数据。
    post请求:通过某个动作(如搜索按钮、提交按钮)向服务器后台发送数据包,再收获相应数据,将得到的数据渲染到当前页面,动态生成页面,如果截取到当前网页的url只能获得一个空的HTML页面框架,不具备有价值的数据。
  1. 对于新工具的学习使用能力还要加强
    这一次的模拟请求,用到了postman这个软件+插件进行网页抓包(抓取客户端向服务器发出的请求数据包,与服务器返回的响应数据包)与发包测试(模拟发出get/post请求),在初次使用中因不太理解请求原理+不熟悉使用方法,导致在盲目测试请求时浪费了很多时间。
  2. 爬虫与反爬虫:通过对反爬虫的了解,反面思考爬虫的设计思路
    现在主流的几个反爬虫技术:
    1、限制查询频率,超过一定频率,采取封号、封IP
    2、通过图片验证码等技术识别机器查询与手工查询
    3、设计cookie值与cookie值的随机有效domain
    4、加密查询的参数
    因此通过爬去携程与去哪儿,了解了两家的反爬虫核心:
    携程
    通过post请求传输参数,发送的参数中包含"transNo"与"searchKey"两个参数,均为随机加密参数,每次查询均独立加密
携程的查询参数

去哪儿
通过get请求传输参数,查询参数中包含"es"随机参数,每一次查询都会产生不同的es参数。
因为缺少这两个加密参数,无法获取加密规则,使得我们无法爬取携程与去哪儿两大门户的机票信息,嗨呀真是太气了……

Nodejs爬虫——机票查询学习笔记(1).md_第1张图片
去哪儿的查询参数

你可能感兴趣的:(Nodejs爬虫——机票查询学习笔记(1).md)