這個教程會用詳細的步驟教你在 macOS 上怎麼去獲取和執行 Headless Chrome 和怎麼去使用 Chrome 團隊提供的示例程式碼。
Headless Chrome 解決了什麼樣的問題?
Chrome 的 Headless 模式是一個和網站互動的新方式,但是沒有一個實際的介面顯示在螢幕上。這看起來像是一個微不足道的改進,但對於從 Web 上抓取內容來說是一個巨大的飛躍。現在有一些穩定的非正式的解決方案去抓取,比如 PhantomJS 和 Nightmare(構建在 Electron 之上)。這些方案都還沒有消失(編輯:PhantomJS 僅有的維護者已經辭職),它們仍然是抓取 Web 非常棒的解決方案。如果你已經在自己的系統中運用了這些工具,你可以繼續使它們。
但是也有這樣的說法,一些使用者在使用 PhantomJS 和 Nightmare 的時候遇到了麻煩。這兩個工具都有警告,當它們執行在 shell-only 系統(沒有實際的螢幕或者視窗管理) 時。舉個例子,當你在使用 Nightmare (一個 Electron 應用)時,為了執行這個應用你需要安裝一個虛擬的顯示管理。另外,自從 Nightmare 使用 Electron 構建之後,它與 Chrome 有著不用的安全模式,在生產環境測試的時候可能會無法捕獲一些安全問題。
什麼版本的 Chrome 支援 Headless ?
Headless Chrome 已經在 Chrome 59 中釋出。截止 2017 年 4 月 13 日,Chrome Canary 是唯一包含 Chrome 59 的頻道。這意味著現在,如果你想要使用 Headless 你需要安裝 Chrome Canary。將來 Chrome 的開發團隊會把 Chrome 59 放到主要的 Chrome 版本中,你就不需要安裝 Chrome Canary 了。
安裝 Chrome Canary ,你可以下載或者用 homebrew 安裝:
brew install Caskroom/versions/google-chrome-canary複製程式碼
我怎麼找到 Headless Chrome?
很多例子在使用 Headless Chrome 時,只用了一個簡單的 chrome
命令。這可以在 Linux 下很好的工作,但是在 macOS 上不行,因為命令沒有被安裝在你的 PATH
中。
所以你需要找到 Chrome 的路徑,讓我們啟動我們的終端去找 Chrome Canary 安裝在我們系統的哪裡。
sudo find / -type d -name "*Chrome Canary.app"複製程式碼
你可能會得到一些許可權錯誤,但是你還是會得到一個路徑長得像這樣:
/Applications/Google Chrome Canary.app複製程式碼
我們找到了 Chrome Canary 的路徑,我們可以使用它啟動 Chrome 並使它執行在 Headless 模式。
我怎麼啟動 Headless Chrome?
我們得到 Chrome Canary 的路徑以後,我們需要用一個命令啟動 Chrome 作為一個 Headless 服務。
/Applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary --headless --remote-debugging-port=9222 --disable-gpu https://chromium.org複製程式碼
特別需要注意的是我們轉義了檔名中空格,深入到 Mac .app 檔案中去找到實際的 Chrome 二進位制檔案。然後我們通過啟動引數去啟動了一個 Headless 瀏覽器,給它一個初始化的連結 https://chromium.org
。這個瀏覽器會監聽 9222
埠等待接下來的指令。保持這個標籤和服務執行。 開啟另一個標籤,我們將會連線到這個瀏覽器的這個標籤,然後給一些指令。
我怎麼使用 Headless Chrome 抓取資料?
我將要使用 Node.js 去連線我們執行中的 Chrome Canary 例項。你需要確保你已經安裝了 Node,才可以繼續這個步驟。
讓我們生成一個普通的 Node 專案,只有一個依賴那就是 Chrome Remote Interface 包,它可以幫助我們與 Chrome 溝通。然後我們建立一個空白的 index.js
檔案。
mkdir my-headless-chrome && cd my-headless-chrome
npm init --yes
npm install --save chrome-remote-interface
touch index.js複製程式碼
現在我們將要放一些程式碼到index.js
。這個模板例子是由 Chrome 團隊提供的。它指示這個瀏覽器導航到github.com
,然後通過 client
裡的 Network
屬性捕獲這個頁面上所有的網路請求。
const CDP = require("chrome-remote-interface");
CDP(client => {
// extract domains
const { Network, Page } = client;
// setup handlers
Network.requestWillBeSent(params => {
console.log(params.request.url);
});
Page.loadEventFired(() => {
client.close();
});
// enable events then start!
Promise.all([Network.enable(), Page.enable()])
.then(() => {
return Page.navigate({ url: "https://github.com" });
})
.catch(err => {
console.error(err);
client.close();
});
}).on("error", err => {
// cannot connect to the remote endpoint
console.error(err);
});複製程式碼
最後啟動我們的 Node 應用。
node index.js複製程式碼
我們可以看到 Chrome 發出的所有的網路請求,然而並沒有一個實際的瀏覽器視窗。
https://github.com/
https://assets-cdn.github.com/assets/frameworks-12d63ce1986bd7fdb5a3f4d944c920cfb75982c70bc7f75672f75dc7b0a5d7c3.css
https://assets-cdn.github.com/assets/github-2826bd4c6eb7572d3a3e9774d7efe010d8de09ea7e2a559fa4019baeacf43f83.css
https://assets-cdn.github.com/assets/site-f4fa6ace91e5f0fabb47e8405e5ecf6a9815949cd3958338f6578e626cd443d7.css
https://assets-cdn.github.com/images/modules/site/home-illo-conversation.svg
https://assets-cdn.github.com/images/modules/site/home-illo-chaos.svg
https://assets-cdn.github.com/images/modules/site/home-illo-business.svg
https://assets-cdn.github.com/images/modules/site/integrators/slackhq.png
https://assets-cdn.github.com/images/modules/site/integrators/zenhubio.png
https://assets-cdn.github.com/images/modules/site/integrators/travis-ci.png
https://assets-cdn.github.com/images/modules/site/integrators/atom.png
https://assets-cdn.github.com/images/modules/site/integrators/circleci.png
https://assets-cdn.github.com/images/modules/site/integrators/codeship.png
https://assets-cdn.github.com/images/modules/site/integrators/codeclimate.png
https://assets-cdn.github.com/images/modules/site/integrators/gitterhq.png
https://assets-cdn.github.com/images/modules/site/integrators/waffleio.png
https://assets-cdn.github.com/images/modules/site/integrators/heroku.png
https://assets-cdn.github.com/images/modules/site/logos/airbnb-logo.png
https://assets-cdn.github.com/images/modules/site/logos/sap-logo.png
https://assets-cdn.github.com/images/modules/site/logos/ibm-logo.png
https://assets-cdn.github.com/images/modules/site/logos/google-logo.png
https://assets-cdn.github.com/images/modules/site/logos/paypal-logo.png
https://assets-cdn.github.com/images/modules/site/logos/bloomberg-logo.png
https://assets-cdn.github.com/images/modules/site/logos/spotify-logo.png
https://assets-cdn.github.com/images/modules/site/logos/swift-logo.png
https://assets-cdn.github.com/images/modules/site/logos/facebook-logo.png
https://assets-cdn.github.com/images/modules/site/logos/node-logo.png
https://assets-cdn.github.com/images/modules/site/logos/nasa-logo.png
https://assets-cdn.github.com/images/modules/site/logos/walmart-logo.png
https://assets-cdn.github.com/assets/compat-8a4318ffea09a0cdb8214b76cf2926b9f6a0ced318a317bed419db19214c690d.js
https://assets-cdn.github.com/assets/frameworks-6d109e75ad8471ba415082726c00c35fb929ceab975082492835f11eca8c07d9.js
https://assets-cdn.github.com/assets/github-5d29649478f4a2b05588bbd0d25cd56ff5445b21df31b4cccca942ad8687e1e8.js
https://assets-cdn.github.com/images/modules/site/heroes/home-code-bg-alt-01.svg
https://assets-cdn.github.com/static/fonts/roboto/roboto-light.woff
https://assets-cdn.github.com/static/fonts/roboto/roboto-regular.woff
https://assets-cdn.github.com/static/fonts/roboto/roboto-medium.woff複製程式碼
這是一個非常棒的方式去檢視載入了那些資源,但是如果我想要操作頁面上的 DOM 元素呢?我們用可以像這樣的一個指令碼拉取github.com
上所有的img
標籤。
const CDP = require("chrome-remote-interface");
CDP(chrome => {
chrome.Page
.enable()
.then(() => {
return chrome.Page.navigate({ url: "https://github.com" });
})
.then(() => {
chrome.DOM.getDocument((error, params) => {
if (error) {
console.error(params);
return;
}
const options = {
nodeId: params.root.nodeId,
selector: "img"
};
chrome.DOM.querySelectorAll(options, (error, params) => {
if (error) {
console.error(params);
return;
}
params.nodeIds.forEach(nodeId => {
const options = {
nodeId: nodeId
};
chrome.DOM.getAttributes(options, (error, params) => {
if (error) {
console.error(params);
return;
}
console.log(params.attributes);
});
});
});
});
});
}).on("error", err => {
console.error(err);
});複製程式碼
我們可以得到以下的資料結構展現了頁面上標籤包含所有圖片的連結。
[ 'src',
'https://assets-cdn.github.com/images/modules/site/home-illo-conversation.svg',
'alt',
'',
'width',
'360',
'class',
'd-block width-fit mx-auto' ]
[ 'src',
'https://assets-cdn.github.com/images/modules/site/home-illo-chaos.svg',
'alt',
'',
'class',
'd-block width-fit mx-auto' ]
[ 'src',
'https://assets-cdn.github.com/images/modules/site/home-illo-business.svg',
'alt',
'',
'class',
'd-block width-fit mx-auto mb-4' ]
[ 'src',
'https://assets-cdn.github.com/images/modules/site/integrators/slackhq.png',
'alt',
'',
'class',
'd-block integrations-collage-img width-fit mx-auto' ]
[ 'src',
'https://assets-cdn.github.com/images/modules/site/integrators/zenhubio.png',
'alt',
'',
'class',
'd-block integrations-collage-img width-fit mx-auto' ]
[ 'src',
'https://assets-cdn.github.com/images/modules/site/integrators/travis-ci.png',
'alt',
'',
'class',
'd-block integrations-collage-img width-fit mx-auto' ]
[ 'src',
'https://assets-cdn.github.com/images/modules/site/integrators/atom.png',
'alt',
'',
'class',
'd-block integrations-collage-img width-fit mx-auto' ]
[ 'src',
'https://assets-cdn.github.com/images/modules/site/integrators/circleci.png',
'alt',
'',
'class',
'd-block integrations-collage-img width-fit mx-auto' ]
[ 'src',
'https://assets-cdn.github.com/images/modules/site/integrators/codeship.png',
'alt',
'',
'class',
'd-block integrations-collage-img width-fit mx-auto' ]
[ 'src',
'https://assets-cdn.github.com/images/modules/site/integrators/codeclimate.png',
'alt',
'',
'class',
'd-block integrations-collage-img width-fit mx-auto' ]
[ 'src',
'https://assets-cdn.github.com/images/modules/site/integrators/gitterhq.png',
'alt',
'',
'class',
'd-block integrations-collage-img width-fit mx-auto' ]
[ 'src',
'https://assets-cdn.github.com/images/modules/site/integrators/waffleio.png',
'alt',
'',
'class',
'd-block integrations-collage-img width-fit mx-auto' ]
[ 'src',
'https://assets-cdn.github.com/images/modules/site/integrators/heroku.png',
'alt',
'',
'class',
'd-block integrations-collage-img width-fit mx-auto' ]
[ 'src',
'https://assets-cdn.github.com/images/modules/site/logos/airbnb-logo.png',
'alt',
'Airbnb',
'class',
'logo-img px-2 px-sm-4 px-md-5 px-lg-0' ]
[ 'src',
'https://assets-cdn.github.com/images/modules/site/logos/sap-logo.png',
'alt',
'SAP',
'class',
'logo-img px-2 px-sm-4 px-md-5 px-lg-0' ]
[ 'src',
'https://assets-cdn.github.com/images/modules/site/logos/ibm-logo.png',
'alt',
'IBM',
'class',
'logo-img px-2 px-sm-4 px-md-5 px-lg-0' ]
[ 'src',
'https://assets-cdn.github.com/images/modules/site/logos/google-logo.png',
'alt',
'Google',
'class',
'logo-img px-2 px-sm-4 px-md-5 px-lg-0' ]
[ 'src',
'https://assets-cdn.github.com/images/modules/site/logos/paypal-logo.png',
'alt',
'PayPal',
'class',
'logo-img px-2 px-sm-4 px-md-5 px-lg-0' ]
[ 'src',
'https://assets-cdn.github.com/images/modules/site/logos/bloomberg-logo.png',
'alt',
'Bloomberg',
'class',
'logo-img px-2 px-sm-4 px-md-5 px-lg-0' ]
[ 'src',
'https://assets-cdn.github.com/images/modules/site/logos/spotify-logo.png',
'alt',
'Spotify',
'class',
'logo-img px-2 px-sm-4 px-md-5 px-lg-0' ]
[ 'src',
'https://assets-cdn.github.com/images/modules/site/logos/swift-logo.png',
'alt',
'Swift',
'class',
'logo-img px-2 px-sm-4 px-md-5 px-lg-0' ]
[ 'src',
'https://assets-cdn.github.com/images/modules/site/logos/facebook-logo.png',
'alt',
'Rails',
'class',
'logo-img px-2 px-sm-4 px-md-5 px-lg-0' ]
[ 'src',
'https://assets-cdn.github.com/images/modules/site/logos/node-logo.png',
'alt',
'Node',
'class',
'logo-img px-2 px-sm-4 px-md-5 px-lg-0' ]
[ 'src',
'https://assets-cdn.github.com/images/modules/site/logos/nasa-logo.png',
'alt',
'Nasa',
'class',
'logo-img px-2 px-sm-4 px-md-5 px-lg-0' ]
[ 'src',
'https://assets-cdn.github.com/images/modules/site/logos/walmart-logo.png',
'alt',
'Walmart',
'class',
'logo-img px-2 px-sm-4 px-md-5 px-lg-0' ]複製程式碼
讓我們愉快的抓取吧!