【翻译】怎么在 macOS 上安装和使用 Headless Chrome

【翻译】怎么在 macOS 上安装和使用 Headless Chrome

原文:How to install and use Headless Chrome on OSX

这个教程会用详细的步骤教你在 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' ]

让我们愉快的抓取吧!

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

推荐阅读更多精彩内容