Chromeless Demo

Chromeless 简介

Chrome 浏览器有一种模式叫做 Chrome Headless,在这种模式下,允许你正常运行 Chrome 浏览器,但是没有界面;想要调试这种模式下打开的网站,可以通过它提供的接口来实现,而 Chromeless 就是把这层接口做了封装,让你使用接口更方便。通过它,可以控制浏览器行为,如打开网站、点击按钮、填充表单、获取 DOM 元素...

可以用来做什么

1 . 可以获取网页截图
2 . 根据页面 document 文档生成 PDF 文件
3 . 编写测试代码,自动化测试网页
4 . 基于真实的浏览器环境,可以编写爬虫程序

安装

首先要安装支持 Chrome Headless 模式的浏览器。目前,Mac 上 Chrome 59 beta
版本与 Linux 上的 Chrome 57+ 已经开始支持 headless 特性。Windows 上 Chrome 暂时不支持,可以使用 Chrome Canary 60 进行开发。
下面是 Ubuntu17.10 的无界面浏览器启动命令示例:

google-chrome \
--remote-debugging-port=9222 \
--disable-gpu \
--no-sandbox  \
--headless

Chromeless 用 NodeJS 编写,要求 NodeJS 版本8.2+,安装:
npm install chromeless

如何用于网页测试

const { Chromeless } = require('chromeless')
const { expect }  = require('chai')

async function run(){
    const chromeless = new Chromeless()
    const firstPage= await chromeless
        .goto('http://www.w3school.com.cn')
        .wait('#navsecond')
        .evaluate(() => {
              // this will be executed in Chrome
              const links = [].map.call(
                document.querySelectorAll('#navsecond ul:nth-child(2) li'),
                a => (a.innerText.trim())
              )
              return links
            })
expect(firstPage).to.have.members(["JS","HTML5","XHTML","CSS","CSS3","TCP/IP"])
await chromeless.end()
}
# 运行并捕捉错误
run().catch(console.error.bind(console))

这里用到了 Chai 断言库,代码实现的功能是验证 W3school 网站首页,左侧第一个 ul 列表的内容是否包含以下内容: JS、HTML5、XHTML、CSS、CSS3、TCP/IP"。
命令详解:
goto:打开加载网站 http://www.w3school.com.cn
wait:等待指定的元素 #navsecond(这是 CSS selector) 渲染成功之后才往下执行;
evaluate:会将里面的 JS 代码送到 浏览器中执行,并获取返回结果;
运行结果:

图一:网页测试Demo结果

如何爬取 Google Search Result

下面代码实现的功能是使用 Google Search 搜索关键字,并返回结果的 Title 和链接地址。
查看执行结果

const { Chromeless } = require('chromeless');

async function run(){
    const chromeless = new Chromeless()
    const firstPage= await chromeless
        .goto('https://www.google.com')
        .wait('input[name="q"]')
        .type('云纵', 'input[name="q"]')
        .press(13) // press enter
        .wait('#foot')
        
    let hasNextPage = true
    let page = 1
    let result = null
    while (hasNextPage){
        if(page===1){
            result = await chromeless
                .evaluate(() => {
                  // this will be executed in Chrome
                  const links = [].map.call(
                    document.querySelectorAll('.g h3 a'),
                    a => ({title: a.innerText, href: a.href})
                  )
                  return links
                })
        }else{
            result = await chromeless
                .scrollTo(200,400)
                .scrollToElement('#foot td:nth-last-child(1)')
                .click('#foot td:nth-last-child(1)')
                .wait(2000)
                .wait('#foot')
                .evaluate(() => {
                              // this will be executed in Chrome
                              const links = [].map.call(
                                document.querySelectorAll('.g h3 a'),
                                a => ({title: a.innerText, href: a.href})
                              )
                              return links
                            })
        }
        console.log(result)
        hasNextPage = await chromeless.evaluate(() => {
              let nextPage =  document.querySelector('#foot td:nth-last-child(1)').innerText
              //return (nextPage==='Next')
              return nextPage.length
            })
        console.log(`第${page}页`)
        console.log(hasNextPage ? '存在下一页' : '不存在下一页')
        page++
    }
    await chromeless.end()
}

run().catch(console.error.bind(console))

命令解释:
.type('云纵', 'input[name="q"]'): 在 CSS 选择器 input[name="q"] 选中的元素(实际上就是 Google Search 搜索框)内输入’云纵‘两个字;
.press(13) :按下键盘的 Enter 键,就是在搜索框输入文字后执行查询;
.scrollTo(200,400) :滚动到距离页面左侧200px,右侧400px的位置;
.scrollToElement('#foot td:nth-last-child(1)'):滚动到 CSS 选择器 #foot td:nth-last-child(1) 选中元素(实际是 Google Search Result 的下一页链接),使元素可见;
.click('#foot td:nth-last-child(1)'):点击元素;
.wait(2000):参数是数字时,表示等待时间,参数是 CSS 选择器时表示等待元素渲染完成;

Async 与 Await

开发时要注意这两个关键字 async 和 await,使用 Chromeless,会用到他们,这是 ES7 实现的异步方案,需要了解以下内容:
1 . function 前面的 async 标识符表示函数内有异步操作;
2 . await 强调后面的异步操作执行完成后才能继续;
3 . await 只能用在async标识的函数内;
4 . await 后写非异步操作也可以,会直接执行

参考资料

1 . 体验异步的终极解决方案-ES7的Async/Await
2 . 初探 Headless Chrome

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335