日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
使用Headless Chrome進(jìn)行頁(yè)面渲染

使用 Headless Chrome 進(jìn)行頁(yè)面渲染 從屬于筆者的 Web 開(kāi)發(fā)基礎(chǔ)與工程實(shí)踐系列文章,主要介紹了使用 Node.js 利用 Chrome Remote Protocol 遠(yuǎn)程控制 Headless Chrome 渲染界面的基礎(chǔ)用法。本文涉及的參考與引用資料統(tǒng)一列舉在這里。

創(chuàng)新互聯(lián)公司從2013年開(kāi)始,先為寧遠(yuǎn)等服務(wù)建站,寧遠(yuǎn)等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢(xún)服務(wù)。為寧遠(yuǎn)企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問(wèn)題。

近日筆者在為 declarative-crawler 編寫(xiě)動(dòng)態(tài)頁(yè)面的蜘蛛,即在使用 declarative-crawler 爬取知乎美圖 一文中介紹的 HeadlessChromeSpider 時(shí),需要選擇某個(gè)無(wú)界面瀏覽器以執(zhí)行 JavaScript 代碼來(lái)動(dòng)態(tài)生成頁(yè)面。之前筆者往往是使用 PhantomJS 或者 Selenium 執(zhí)行動(dòng)態(tài)頁(yè)面渲染,而在 Chrome 59 之后 Chrome 提供了 Headless 模式,其允許在命令行中使用 Chromium 以及 Blink 渲染引擎提供的完整的現(xiàn)代 Web 平臺(tái)特性。需要注意的是,Headless Chrome 仍然存在一定的局限,相較于 Nightmare 或 Phantom 這樣的工具, Chrome 的遠(yuǎn)程接口仍然無(wú)法提供較好的開(kāi)發(fā)者體驗(yàn)。我們?cè)谙挛慕榻B的代碼示例中也會(huì)發(fā)現(xiàn),目前我們?nèi)孕枰罅康哪0宕a進(jìn)行控制。

安裝與啟動(dòng)

在 Chrome 安裝完畢后我們可以利用其包體內(nèi)自帶的命令行工具啟動(dòng):

 
 
 
 
  1. $ chrome --headless --remote-debugging-port=9222 https://chromium.org 

筆者為了部署方便,使用 Docker 鏡像來(lái)進(jìn)行快速部署,如果你本地存在 Docker 環(huán)境,可以使用如下命令快速啟動(dòng):

 
 
 
 
  1. docker run -d -p 9222:9222 justinribeiro/chrome-headless 

如果是在 Mac 下本地使用的話我們還可以創(chuàng)建命令別名:

 
 
 
 
  1. alias chrome="/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome" 
  2. alias chrome-canary="/Applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary" 
  3. alias chromium="/Applications/Chromium.app/Contents/MacOS/Chromium" 

如果是在 Ubuntu 環(huán)境下我們可以使用 deb 進(jìn)行安裝:

 
 
 
 
  1. # Install Google Chrome 
  2. # https://askubuntu.com/questions/79280/how-to-install-chrome-browser-properly-via-command-line 
  3. sudo apt-get install libxss1 libappindicator1 libindicator7 
  4. wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb 
  5. sudo dpkg -i google-chrome*.deb  # Might show "errors", fixed by next line 
  6. sudo apt-get install -f 

chrome 命令行也支持豐富的命令行參數(shù),--dump-dom 參數(shù)可以將 document.body.innerHTML 打印到標(biāo)準(zhǔn)輸出中:

 
 
 
 
  1. chrome --headless --disable-gpu --dump-dom https://www.chromestatus.com/ 

而 --print-to-pdf 標(biāo)識(shí)則會(huì)將網(wǎng)頁(yè)輸出位 PDF:

 
 
 
 
  1. chrome --headless --disable-gpu --print-to-pdf https://www.chromestatus.com/ 

初次之外,我們也可以使用 --screenshot 參數(shù)來(lái)獲取頁(yè)面截圖:

 
 
 
 
  1. chrome --headless --disable-gpu --screenshot https://www.chromestatus.com/ 
  2.  
  3. # Size of a standard letterhead. 
  4. chrome --headless --disable-gpu --screenshot --window-size=1280,1696 https://www.chromestatus.com/ 
  5.  
  6. # Nexus 5x 
  7. chrome --headless --disable-gpu --screenshot --window-size=412,732 https://www.chromestatus.com/ 

如果我們需要更復(fù)雜的截圖策略,譬如進(jìn)行完整頁(yè)面截圖則需要利用代碼進(jìn)行遠(yuǎn)程控制。

代碼控制

啟動(dòng)

在上文中我們介紹了如何利用命令行來(lái)手動(dòng)啟動(dòng) Chrome,這里我們嘗試使用 Node.js 來(lái)啟動(dòng) Chrome,最簡(jiǎn)單的方式就是使用 child_process 來(lái)啟動(dòng):

 
 
 
 
  1. const exec = require('child_process').exec; 
  2.  
  3. function launchHeadlessChrome(url, callback) { 
  4.   // Assuming MacOSx. 
  5.   const CHROME = '/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome'; 
  6.   exec(`${CHROME} --headless --disable-gpu --remote-debugging-port=9222 ${url}`, callback); 
  7.  
  8. launchHeadlessChrome('https://www.chromestatus.com', (err, stdout, stderr) => { 
  9.   ... 
  10. }); 

遠(yuǎn)程控制

這里我們使用 chrome-remote-interface 來(lái)遠(yuǎn)程控制 Chrome ,實(shí)際上 chrome-remote-interface 是對(duì)于 Chrome DevTools Protocol 的遠(yuǎn)程封裝,我們可以參考協(xié)議文檔了解詳細(xì)的功能與參數(shù)。使用 npm 安裝完畢之后,我們可以用如下代碼片進(jìn)行簡(jiǎn)單控制:

 
 
 
 
  1. const CDP = require('chrome-remote-interface'); 
  2.  
  3. CDP((client) => { 
  4.     // extract domains 
  5.     const {Network, Page} = client; 
  6.     // setup handlers 
  7.     Network.requestWillBeSent((params) => { 
  8.         console.log(params.request.url); 
  9.     }); 
  10.     Page.loadEventFired(() => { 
  11.         client.close(); 
  12.     }); 
  13.     // enable events then start! 
  14.     Promise.all([ 
  15.         Network.enable(), 
  16.         Page.enable() 
  17.     ]).then(() => { 
  18.         return Page.navigate({url: 'https://github.com'}); 
  19.     }).catch((err) => { 
  20.         console.error(err); 
  21.         client.close(); 
  22.     }); 
  23. }).on('error', (err) => { 
  24.     // cannot connect to the remote endpoint 
  25.     console.error(err); 
  26. }); 

我們也可以使用 chrome-remote-interface 提供的命令行功能,譬如我們可以在命令行中訪問(wèn)某個(gè)界面并且記錄所有的網(wǎng)絡(luò)請(qǐng)求:

 
 
 
 
  1. $ chrome-remote-interface inspect 
  2. >>> Network.enable() 
  3. { result: {} } 
  4. >>> Network.requestWillBeSent(params => params.request.url) 
  5. { 'Network.requestWillBeSent': 'params => params.request.url' } 
  6. >>> Page.navigate({url: 'https://www.wikipedia.org'}) 
  7. { 'Network.requestWillBeSent': 'https://www.wikipedia.org/' } 
  8. { result: { frameId: '5530.1' } } 
  9. { 'Network.requestWillBeSent': 'https://www.wikipedia.org/portal/wikipedia.org/assets/img/Wikipedia_wordmark.png' } 
  10. { 'Network.requestWillBeSent': 'https://www.wikipedia.org/portal/wikipedia.org/assets/img/Wikipedia-logo-v2.png' } 
  11. { 'Network.requestWillBeSent': 'https://www.wikipedia.org/portal/wikipedia.org/assets/js/index-3b68787aa6.js' } 
  12. { 'Network.requestWillBeSent': 'https://www.wikipedia.org/portal/wikipedia.org/assets/js/gt-ie9-c84bf66d33.js' } 
  13. { 'Network.requestWillBeSent': 'https://www.wikipedia.org/portal/wikipedia.org/assets/img/sprite-bookshelf_icons.png?16ed124e8ca7c5ce9d463e8f99b2064427366360' } 
  14. { 'Network.requestWillBeSent': 'https://www.wikipedia.org/portal/wikipedia.org/assets/img/sprite-project-logos.png?9afc01c5efe0a8fb6512c776955e2ad3eb48fbca' } 

我們也可以直接查看內(nèi)置的接口文檔:

 
 
 
 
  1. >>> Page.navigate 
  2. { [Function] 
  3.   category: 'command', 
  4.   parameters: { url: { type: 'string', description: 'URL to navigate the page to.' } }, 
  5.   returns: 
  6.    [ { name: 'frameId', 
  7.        '$ref': 'FrameId', 
  8.        hidden: true, 
  9.        description: 'Frame id that will be navigated.' } ], 
  10.   description: 'Navigates current page to the given URL.', 
  11.   handlers: [ 'browser', 'renderer' ] }>>> Page.navigate 
  12. { [Function] 
  13.   category: 'command', 
  14.   parameters: { url: { type: 'string', description: 'URL to navigate the page to.' } }, 
  15.   returns: 
  16.    [ { name: 'frameId', 
  17.        '$ref': 'FrameId', 
  18.        hidden: true, 
  19.        description: 'Frame id that will be navigated.' } ], 
  20.   description: 'Navigates current page to the given URL.', 
  21.   handlers: [ 'browser', 'renderer' ] } 

我們?cè)谏衔闹羞€提到需要以代碼控制瀏覽器進(jìn)行完整頁(yè)面截圖,這里需要利用 Emulation 模塊控制頁(yè)面視口縮放:

 
 
 
 
  1. const CDP = require('chrome-remote-interface'); 
  2. const argv = require('minimist')(process.argv.slice(2)); 
  3. const file = require('fs'); 
  4.  
  5. // CLI Args 
  6. const url = argv.url || 'https://www.google.com'; 
  7. const format = argv.format === 'jpeg' ? 'jpeg' : 'png'; 
  8. const viewportWidth = argv.viewportWidth || 1440; 
  9. const viewportHeight = argv.viewportHeight || 900; 
  10. const delay = argv.delay || 0; 
  11. const userAgent = argv.userAgent; 
  12. const fullPage = argv.full; 
  13.  
  14. // Start the Chrome Debugging Protocol 
  15. CDP(async function(client) { 
  16.   // Extract used DevTools domains. 
  17.   const {DOM, Emulation, Network, Page, Runtime} = client; 
  18.  
  19.   // Enable events on domains we are interested in. 
  20.   await Page.enable(); 
  21.   await DOM.enable(); 
  22.   await Network.enable(); 
  23.  
  24.   // If user agent override was specified, pass to Network domain 
  25.   if (userAgent) { 
  26.     await Network.setUserAgentOverride({userAgent}); 
  27.   } 
  28.  
  29.   // Set up viewport resolution, etc. 
  30.   const deviceMetrics = { 
  31.     width: viewportWidth, 
  32.     height: viewportHeight, 
  33.     deviceScaleFactor: 0, 
  34.     mobile: false, 
  35.     fitWindow: false, 
  36.   }; 
  37.   await Emulation.setDeviceMetricsOverride(deviceMetrics); 
  38.   await Emulation.setVisibleSize({width: viewportWidth, height: viewportHeight}); 
  39.  
  40.   // Navigate to target page 
  41.   await Page.navigate({url}); 
  42.  
  43.   // Wait for page load event to take screenshot 
  44.   Page.loadEventFired(async () => { 
  45.     // If the `full` CLI option was passed, we need to measure the height of 
  46.     // the rendered page and use Emulation.setVisibleSize 
  47.     if (fullPage) { 
  48.       const {root: {nodeId: documentNodeId}} = await DOM.getDocument(); 
  49.       const {nodeId: bodyNodeId} = await DOM.querySelector({ 
  50.         selector: 'body', 
  51.         nodeId: documentNodeId, 
  52.       }); 
  53.       const {model: {height}} = await DOM.getBoxModel({nodeId: bodyNodeId}); 
  54.  
  55.       await Emulation.setVisibleSize({width: viewportWidth, height: height}); 
  56.       // This forceViewport call ensures that content outside the viewport is 
  57.       // rendered, otherwise it shows up as grey. Possibly a bug? 
  58.       await Emulation.forceViewport({x: 0, y: 0, scale: 1}); 
  59.     } 
  60.  
  61.     setTimeout(async function() { 
  62.       const screenshot = await Page.captureScreenshot({format}); 
  63.       const buffer = new Buffer(screenshot.data, 'base64'); 
  64.       file.writeFile('output.png', buffer, 'base64', function(err) { 
  65.         if (err) { 
  66.           console.error(err); 
  67.         } else { 
  68.           console.log('Screenshot saved'); 
  69.         } 
  70.         client.close(); 
  71.       }); 
  72.     }, delay); 
  73.   }); 
  74. }).on('error', err => { 
  75.   console.error('Cannot connect to browser:', err); 
  76. }); 

 【本文是專(zhuān)欄作者“張梓雄 ”的原創(chuàng)文章,如需轉(zhuǎn)載請(qǐng)通過(guò)與作者聯(lián)系】

戳這里,看該作者更多好文


名稱(chēng)欄目:使用Headless Chrome進(jìn)行頁(yè)面渲染
本文URL:http://m.5511xx.com/article/djjddgi.html