Chromeless 簡介
Chrome 瀏覽器有一種模式叫做 Chrome Headless,在這種模式下躲舌,允許你正常運行 Chrome 瀏覽器,但是沒有界面;想要調(diào)試這種模式下打開的網(wǎng)站绍绘,可以通過它提供的接口來實現(xiàn),而 Chromeless 就是把這層接口做了封裝迟赃,讓你使用接口更方便陪拘。通過它,可以控制瀏覽器行為纤壁,如打開網(wǎng)站左刽、點擊按鈕、填充表單酌媒、獲取 DOM 元素...
可以用來做什么
1 . 可以獲取網(wǎng)頁截圖
2 . 根據(jù)頁面 document 文檔生成 PDF 文件
3 . 編寫測試代碼欠痴,自動化測試網(wǎng)頁
4 . 基于真實的瀏覽器環(huán)境,可以編寫爬蟲程序
安裝
首先要安裝支持 Chrome Headless 模式的瀏覽器秒咨。目前喇辽,Mac 上 Chrome 59 beta
版本與 Linux 上的 Chrome 57+ 已經(jīng)開始支持 headless 特性。Windows 上 Chrome 暫時不支持拭荤,可以使用 Chrome Canary 60 進(jìn)行開發(fā)茵臭。
下面是 Ubuntu17.10 的無界面瀏覽器啟動命令示例:
google-chrome \
--remote-debugging-port=9222 \
--disable-gpu \
--no-sandbox \
--headless
Chromeless 用 NodeJS 編寫,要求 NodeJS 版本8.2+舅世,安裝:
npm install chromeless
如何用于網(wǎng)頁測試
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 斷言庫旦委,代碼實現(xiàn)的功能是驗證 W3school 網(wǎng)站首頁,左側(cè)第一個 ul 列表的內(nèi)容是否包含以下內(nèi)容: JS雏亚、HTML5缨硝、XHTML、CSS罢低、CSS3查辩、TCP/IP"。
命令詳解:
goto:打開加載網(wǎng)站 http://www.w3school.com.cn
网持;
wait:等待指定的元素 #navsecond
(這是 CSS selector) 渲染成功之后才往下執(zhí)行宜岛;
evaluate:會將里面的 JS 代碼送到 瀏覽器中執(zhí)行,并獲取返回結(jié)果功舀;
運行結(jié)果:
如何爬取 Google Search Result
下面代碼實現(xiàn)的功能是使用 Google Search 搜索關(guān)鍵字,并返回結(jié)果的 Title 和鏈接地址辟汰。
查看執(zhí)行結(jié)果
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 搜索框)內(nèi)輸入’云縱‘兩個字;
.press(13)
:按下鍵盤的 Enter 鍵戴而,就是在搜索框輸入文字后執(zhí)行查詢凑术;
.scrollTo(200,400)
:滾動到距離頁面左側(cè)200px,右側(cè)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)
:參數(shù)是數(shù)字時姻檀,表示等待時間命满,參數(shù)是 CSS 選擇器時表示等待元素渲染完成;
Async 與 Await
開發(fā)時要注意這兩個關(guān)鍵字 async 和 await绣版,使用 Chromeless胶台,會用到他們,這是 ES7 實現(xiàn)的異步方案诈唬,需要了解以下內(nèi)容:
1 . function 前面的 async 標(biāo)識符表示函數(shù)內(nèi)有異步操作;
2 . await 強(qiáng)調(diào)后面的異步操作執(zhí)行完成后才能繼續(xù)铸磅;
3 . await 只能用在async標(biāo)識的函數(shù)內(nèi)杭朱;
4 . await 后寫非異步操作也可以阅仔,會直接執(zhí)行