摘要:獲取獲取上下文句柄執(zhí)行計算銷毀句柄除此之外,還可以使用意為在瀏覽器環(huán)境執(zhí)行腳本,可傳入第二個參數(shù)作為句柄,而則針對選中的一個元素執(zhí)行操作。
我們?nèi)粘J褂脼g覽器或者說是有頭瀏覽器時的步驟為:啟動瀏覽器、打開一個網(wǎng)頁、進行交互。
無頭瀏覽器指的是我們使用腳本來執(zhí)行以上過程的瀏覽器,能模擬真實的瀏覽器使用場景。
有了無頭瀏覽器,我們就能做包括但不限于以下事情:
對網(wǎng)頁進行截圖保存為圖片或 pdf
抓取單頁應用(SPA)執(zhí)行并渲染(解決傳統(tǒng) HTTP 爬蟲抓取單頁應用難以處理異步請求的問題)
做表單的自動提交、UI的自動化測試、模擬鍵盤輸入等
用瀏覽器自帶的一些調(diào)試工具和性能分析工具幫助我們分析問題
在最新的無頭瀏覽器環(huán)境里做測試、使用最新瀏覽器特性
寫爬蟲做你想做的事情(奸笑
無頭瀏覽器很多,包括但不限于:
PhantomJS, 基于 Webkit
SlimerJS, 基于 Gecko
HtmlUnit, 基于 Rhnio
TrifleJS, 基于 Trident
Splash, 基于 Webkit
這里主要介紹 Google 提供的無頭瀏覽器(headless Chrome), 他基于 Chrome DevTools protocol 提供了不少高度封裝的接口方便我們控制瀏覽器。
簡單的代碼示例啟動/關閉瀏覽器、打開頁面為了能使用 async/await 等新特性,需要使用 v7.6.0 或更高版本的 Node.
// 啟動瀏覽器 const browser = await puppeteer.launch({ // 關閉無頭模式,方便我們看到這個無頭瀏覽器執(zhí)行的過程 // headless: false, timeout: 30000, // 默認超時為30秒,設置為0則表示不設置超時 }); // 打開空白頁面 const page = await browser.newPage(); // 進行交互 // ... // 關閉瀏覽器 // await browser.close();設置頁面視窗大小
// 設置瀏覽器視窗 page.setViewport({ width: 1376, height: 768, });輸入網(wǎng)址
// 地址欄輸入網(wǎng)頁地址 await page.goto("https://google.com/", { // 配置項 // waitUntil: "networkidle", // 等待網(wǎng)絡狀態(tài)為空閑的時候才繼續(xù)執(zhí)行 });保存網(wǎng)頁為圖片
打開一個網(wǎng)頁,然后截圖保存到本地:
await page.screenshot({ path: "path/to/saved.png", });
完整示例代碼
保存網(wǎng)頁為 pdf打開一個網(wǎng)頁,然后保存 pdf 到本地:
await page.pdf({ path: "path/to/saved.pdf", format: "A4", // 保存尺寸 });
完整示例代碼
執(zhí)行腳本要獲取打開的網(wǎng)頁中的宿主環(huán)境,我們可以使用 Page.evaluate 方法:
// 獲取視窗信息 const dimensions = await page.evaluate(() => { return { width: document.documentElement.clientWidth, height: document.documentElement.clientHeight, deviceScaleFactor: window.devicePixelRatio }; }); console.log("視窗信息:", dimensions); // 獲取 html // 獲取上下文句柄 const htmlHandle = await page.$("html"); // 執(zhí)行計算 const html = await page.evaluate(body => body.outerHTML, htmlHandle); // 銷毀句柄 await htmlHandle.dispose(); console.log("html:", html);
Page.$ 可以理解為我們常用的 document.querySelector, 而 Page.$$ 則對應 document.querySelectorAll。
完整示例代碼
自動提交表單打開谷歌首頁,輸入關鍵字,回車進行搜索:
// 地址欄輸入網(wǎng)頁地址 await page.goto("https://google.com/", { waitUntil: "networkidle", // 等待網(wǎng)絡狀態(tài)為空閑的時候才繼續(xù)執(zhí)行 }); // 聚焦搜索框 // await page.click("#lst-ib"); await page.focus("#lst-ib"); // 輸入搜索關鍵字 await page.type("辣子雞", { delay: 1000, // 控制 keypress 也就是每個字母輸入的間隔 }); // 回車 await page.press("Enter");
完整示例代碼
復雜點的代碼示例每一個簡單的動作連接起來,就是一連串復雜的交互,接下來我們看兩個更具體的示例。
抓取單頁應用: 模擬餓了么外賣下單傳統(tǒng)的爬蟲是基于 HTTP 協(xié)議,模擬 UserAgent 發(fā)送 http 請求,獲取到 html 內(nèi)容后使用正則解析出需要抓取的內(nèi)容,這種方式面對服務端渲染直出 html 的網(wǎng)頁時非常便捷。
但遇到單頁應用(SPA)時,或遇到登錄校驗時,這種爬蟲就顯得比較無力。
而使用無頭瀏覽器,抓取網(wǎng)頁時完全使用了人機交互時的操作,所以頁面的初始化完全能使用宿主瀏覽器環(huán)境渲染完備,不再需要關心這個單頁應用在前端初始化時需要涉及哪些 HTTP 請求。
無頭瀏覽器提供的各種點擊、輸入等指令,完全模擬人的點擊、輸入等指令,也就再也不用擔心正則寫不出來了啊哈哈哈
當然,有些場景下,使用傳統(tǒng)的 HTTP 爬蟲(寫正則匹配) 還是比較高效的。
在這里就不再詳細對比這些差異了,以下這個例子僅作為展示模擬一個完整的人機交互:使用移動版餓了么點外賣。
先看下效果:
代碼比較長就不全貼了,關鍵是幾行:
const puppeteer = require("puppeteer"); const devices = require("puppeteer/DeviceDescriptors"); const iPhone6 = devices["iPhone 6"]; console.log("啟動瀏覽器"); const browser = await puppeteer.launch(); console.log("打開頁面"); const page = await browser.newPage(); // 模擬移動端設備 await page.emulate(iPhone6); console.log("地址欄輸入網(wǎng)頁地址"); await page.goto(url); console.log("等待頁面準備好"); await page.waitForSelector(".search-wrapper .search"); console.log("點擊搜索框"); await page.tap(".search-wrapper .search"); await page.type("麥當勞", { delay: 200, // 每個字母之間輸入的間隔 }); console.log("回車開始搜索"); await page.tap("button"); console.log("等待搜素結(jié)果渲染出來"); await page.waitForSelector("[class^="index-container"]"); console.log("找到搜索到的第一家外賣店!"); await page.tap("[class^="index-container"]"); console.log("等待菜單渲染出來"); await page.waitForSelector("[class^="fooddetails-food-panel"]"); console.log("直接選一個菜品吧"); await page.tap("[class^="fooddetails-cart-button"]"); // console.log("===為了看清楚,傲嬌地等兩秒==="); await page.waitFor(2000); await page.tap("[class^=submit-btn-submitbutton]"); // 關閉瀏覽器 await browser.close();
關鍵步驟是:
加載頁面
等待需要點擊的 DOM 渲染出來后點擊
繼續(xù)等待下一步需要點擊的 DOM 渲染出來再點擊
關鍵的幾個指令:
page.tap(或 page.click) 為點擊
page.waitForSelector 意思是等待指定元素出現(xiàn)在網(wǎng)頁中,如果已經(jīng)出現(xiàn)了,則立即繼續(xù)執(zhí)行下去, 后面跟的參數(shù)為 selector 選擇器,與我們常用的 document.querySelector 接收的參數(shù)一致
page.waitFor 后面可以傳入 selector 選擇器、function 函數(shù)或 timeout 毫秒時間,如 page.waitFor(2000) 指等待2秒再繼續(xù)執(zhí)行,例子中用這個函數(shù)暫停操作主要是為了演示
以上幾個指令都可接受一個 selector 選擇器作為參數(shù),這里額外介紹幾個方法:
page.$(selector) 與我們常用的 document.querySelector(selector) 一致,返回的是一個 ElementHandle 元素句柄
page.$$(selector) 與我們常用的 document.querySelectorAll(selector) 一致,返回的是一個數(shù)組
在有頭瀏覽器上下文中,我們選擇一個元素的方法是:
const body = document.querySelector("body"); const bodyInnerHTML = body.innerHTML; console.log("bodyInnerHTML: ", bodyInnerHTML);
而在無頭瀏覽器里,我們首先需要獲取一個句柄,通過句柄獲取到環(huán)境中的信息后,銷毀這個句柄。
// 獲取 html // 獲取上下文句柄 const bodyHandle = await page.$("body"); // 執(zhí)行計算 const bodyInnerHTML = await page.evaluate(dom => dom.innerHTML, bodyHandle); // 銷毀句柄 await bodyHandle.dispose(); console.log("bodyInnerHTML:", bodyInnerHTML);
除此之外,還可以使用 page.$eval:
const bodyInnerHTML = await page.$eval("body", dom => dom.innerHTML); console.log("bodyInnerHTML: ", bodyInnerHTML);
page.evaluate 意為在瀏覽器環(huán)境執(zhí)行腳本,可傳入第二個參數(shù)作為句柄,而 page.$eval 則針對選中的一個 DOM 元素執(zhí)行操作。
完整示例代碼
導出批量網(wǎng)頁:下載圖靈圖書我在 圖靈社區(qū) 上買了不少電子書,以前支持推送到 mobi 格式到 kindle 或推送 pdf 格式到郵箱進行閱讀,不過經(jīng)常會關閉這些推送渠道,只能留在網(wǎng)頁上看書。
對我來說不是很方便,而這些書籍的在線閱讀效果是服務器渲染出來的(帶了大量標簽,無法簡單抽取出好的排版),最好的方式當然是直接在線閱讀并保存為 pdf 或圖片了。
借助瀏覽器的無頭模式,我寫了個簡單的下載已購買書籍為 pdf 到本地的腳本,支持批量下載已購買的書籍。
使用方法,傳入帳號密碼和保存路徑,如:
$ node ./demo/download-ituring-books.js "用戶名" "密碼" "./books"
注意:puppeteer 的 Page.pdf() 目前僅支持在無頭模式中使用,所以要想看有頭狀態(tài)的抓取過程的話,執(zhí)行到 Page.pdf() 這步會先報錯:
所以啟動這個腳本時,需要保持無頭模式:
const browser = await puppeteer.launch({ // 關閉無頭模式,方便我們看到這個無頭瀏覽器執(zhí)行的過程 // 注意若調(diào)用了 Page.pdf 即保存為 pdf,則需要保持為無頭模式 // headless: false, });
看下執(zhí)行效果:
我的書架里有20多本書,下載完后是這樣子:
完整示例代碼
無頭瀏覽器還能做什么?無頭瀏覽器說白了就是能模擬人工在有頭瀏覽器中的各種操作,那自然很多人力活,都能使用無頭瀏覽器來做(比如上面這個下載 pdf 的過程,其實是人力打開每一個文章頁面,然后按 ctrl+p 或 command+p 保存到本地的自動化過程)。
那既然用自動化工具能解決的事情,就不應該浪費重復的人力勞動了,所以我們還可以做:
自動化工具
如自動提交表單,自動下載
自動化 UI 測試
如記錄下正確 DOM 結(jié)構(gòu)或截圖,然后自動執(zhí)行指定操作后,檢查 DOM 結(jié)構(gòu)或截圖是否匹配(UI 斷言)
定時監(jiān)控工具
如定時截圖發(fā)周報,或定時巡查重要業(yè)務路徑下的頁面是否處于可用狀態(tài),配合郵件告警
爬蟲
如傳統(tǒng) HTTP 爬蟲怕不到的地方,就可配合無頭瀏覽器渲染能力來做
etc
感謝閱讀!
(全文完)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/89179.html
摘要:抓取并生成預先呈現(xiàn)的內(nèi)容即。自動表單提交,測試,鍵盤輸入等。創(chuàng)建一個最新的自動化測試環(huán)境。使用最新的的和瀏覽器功能,直接在最新版本的瀏覽器中運行測試。捕獲您網(wǎng)站的時間線跟蹤,以幫助診斷性能問題。 木偶 Puppeteer 更友好的 Headless Chrome Node API木偶也是有心的 (=?ω?=) showImg(https://segmentfault.com/img/b...
摘要:首先介紹是一個庫,他提供了一組用來操縱的默認也就是無的,也可以配置為有有點類似于,但是官方團隊進行維護的,前景更好。使用,相當于同時具有和的能力,應用場景會非常多。 首先介紹Puppeteer Puppeteer是一個node庫,他提供了一組用來操縱Chrome的API(默認headless也就是無UI的chrome,也可以配置為有UI) 有點類似于PhantomJS,但Puppet...
摘要:技術(shù)縱橫調(diào)試指南協(xié)議是新加入的調(diào)試協(xié)議,通過與交互,同時基于瀏覽器的提供了圖形化的調(diào)試界面。使得多業(yè)務線在復雜架構(gòu)情況下能夠獨立開發(fā)測試,互不干擾,并統(tǒng)一調(diào)用接口。技術(shù)周刊由小組出品,匯聚一周好文章,周刊原文。 本期推薦 寫在 2017 的前端數(shù)據(jù)層不完全指北 在前端技術(shù)的發(fā)展中,各個層面演進出不同的技術(shù)方案,如數(shù)據(jù)類型層面的 TypeScript,F(xiàn)low,PropTypes,應用架...
摘要:前端日報精選無頭瀏覽器初探鼠標無限移動簡介譯深入分析變更檢測發(fā)布前必須排查的安全如何開發(fā)中文第期關鍵和減少阻塞渲染的的自動化解決方案譯網(wǎng)頁設計掘金年最受歡迎的個編程挑戰(zhàn)網(wǎng)站簡書系列和深入理解掘金發(fā)布后臺管理系統(tǒng),沒錯,它就是你想 2017-10-18 前端日報 精選 無頭瀏覽器 Puppeteer 初探鼠標無限移動 JS API Pointer Lock簡介[譯] 深入分析 Angul...
閱讀 2953·2023-04-26 01:49
閱讀 2082·2021-10-13 09:39
閱讀 2294·2021-10-11 11:09
閱讀 936·2019-08-30 15:53
閱讀 2825·2019-08-30 15:44
閱讀 932·2019-08-30 11:12
閱讀 2992·2019-08-29 17:17
閱讀 2385·2019-08-29 16:57