爬虫的终极形态:nightmare

6,670 阅读2分钟
原文链接: www.moye.me

nightmare 是一个基于 electron 的自动化库(意思是说它自带浏览器),用于实现爬虫或自动化测试。相较于传统的爬虫框架(scrapy/pyspider),或者dom操作库(cheerio/jsdom),或者基于浏览器的自动化框架(selenium/phantomjs),他的优势在于提供了一个简洁有效 的编程模型。

来看官网给出的一个对比场景:

同样是实现一个向yahoo自动提交关键词并搜索的功能

1. PhantomJS实现

phantom.create(function (ph) {
  ph.createPage(function (page) {
    page.open('http://yahoo.com', function (status) {
      page.evaluate(function () {
        var el =
          document.querySelector('input[title="Search"]');
        el.value = 'github nightmare';
      }, function (result) {
        page.evaluate(function () {
          var el = document.querySelector('.searchsubmit');
          var event = document.createEvent('MouseEvent');
          event.initEvent('click', true, false);
          el.dispatchEvent(event);
        }, function (result) {
          ph.exit();
        });
      });
    });
  });
});

2. nightmare实现

yield Nightmare()
  .goto('http://yahoo.com')
  .type('input[title="Search"]', 'github nightmare')
  .click('.searchsubmit');

怎么玩

yarn add nightmare

安装

涉及到下载并编译一个electron,你国网速下需耐心等待

配置 options

openDevTools: { 
      mode: 'bottom',       // 开发者工具位置:right, bottom, undocked, detach
},
show: true,                 // 要不要显示浏览器
dock: true,                 // 要不要在Dock上显示图标
waitTimeout: 60000,         // .wait() 方法超时时长,单位:ms
executionTimeout: 86400000, // .evaluate() 方法超时时长,单位:ms

实例化

import Nightmare from 'nightmare'
const nightmare = new Nightmare(options)

交互用的API

简单介绍几个:

  • .goto(url[, headers]) 跳转到url
  • .viewport(width, height) 浏览器窗口大小
  • .wait(selector) 等待某个dom元素出现
  • .click(selector) 点击某个dom元素
  • .type(selector[, text]) 在某个dom元素中输入
  • .inject(type, file) 在页面上挂载 js/css 文件内容
  • .evaluate(fn[, arg1, arg2,...]) 在客户端注入JS脚本并执行,从而实现electron模型下的C/S互动及数据交换
  • ……

自动登录并轮询的例子

有那么一个网站(比如叫chagang.site),在我登录进去后,会不定时的查岗,需要点击一个按钮以证明没有离线,怎么用nightmare实现自动挂机呢?

大概分这么几步走:

  1. 先跳转到该网站
  2. 模拟输入帐号信息后点击submit
  3. 登录后等待主界面加载出现
  4. 在客户端起一个定时器,2秒一次轮询那个查岗按钮,发现就模拟点击
nightmare
  .goto('http://chagang.site/')
  .viewport(1024, 768)
  .cookies.clearAll()
  .type('#username', '用户名')
  .type('#password', '密码')
  .click('input[type=submit]')
  .wait('#mainContent')
  .evaluate(() => {
    /* eslint-disable */
    function handle() {
	  // 一个叫inspector的button
      var inspector = document.querySelector('#inspector');
      if (inspector && inspector.style.visibility === 'visible') {
        inspector.click();
      }
    }
 
    setInterval(handle, 2000);
    /* eslint-enable */
  })
  .evaluate(() => document.title)
  .then(title => printWithTime(`${title} => 加载完成`))
  .catch(err => error(err))

小结

本文只涉及到API中的一小部分,宝贝都在文档里。用 nightmare,就是希望借助这种简洁模型的梳理,相对愉悦的实现自动化交互(至于标题,终归是意义过剩的产物,请忽略他的招摇。