可以在Google Cloud功能中运行Headless Chrome / Chromium吗?

时间:2017-05-02 08:03:21

标签: google-chrome google-cloud-platform google-cloud-functions

有没有办法在Google Cloud功能中运行Headless Chrome/Chromium?我知道我可以在GCF中包含和运行静态编译的二进制文件。我可以获得适用于此的静态编译版Chrome吗?

4 个答案:

答案 0 :(得分:15)

Google Cloud Functions的Node.js 8运行时现在包含了运行Headless Chrome所需的所有必需的OS软件包。

这是返回屏幕截图的HTTP函数的代码示例:

主要index.js文件:

const puppeteer = require('puppeteer');

exports.screenshot = async (req, res) => {
  const url = req.query.url;

  if (!url) {
    return res.send('Please provide URL as GET parameter, for example: <a href="?url=https://example.com">?url=https://example.com</a>');
  }

  const browser = await puppeteer.launch({
    args: ['--no-sandbox']
  });
  const page = await browser.newPage();
  await page.goto(url);
  const imageBuffer = await page.screenshot();
  await browser.close();

  res.set('Content-Type', 'image/png');
  res.send(imageBuffer);
}

package.json

{
  "name": "screenshot",
  "version": "0.0.1",
  "dependencies": {
    "puppeteer": "^1.6.2"
  }
}

答案 1 :(得分:4)

我刚刚部署了运行无头Chrome的GCF功能。几条走道:

  1. 你必须在Debian 8上静态编译Chromium和NSS
  2. 您必须在启动Chromium
  3. 之前修改环境变量以指向NSS
  4. 性能远远低于您在AWS Lambda(3秒以上)上获得的性能
  5. 对于1,您应该能够在线找到大量说明。

    对于2,我正在使用的代码如下:

    static executablePath() {
      let bin = path.join(__dirname, '..', 'bin', 'chromium');
      let nss = path.join(__dirname, '..', 'bin', 'nss', 'Linux3.16_x86_64_cc_glibc_PTH_64_OPT.OBJ');
    
      if (process.env.PATH === undefined) {
        process.env.PATH = path.join(nss, 'bin');
      } else if (process.env.PATH.indexOf(nss) === -1) {
        process.env.PATH = [path.join(nss, 'bin'), process.env.PATH].join(':');
      }
    
      if (process.env.LD_LIBRARY_PATH === undefined) {
        process.env.LD_LIBRARY_PATH = path.join(nss, 'lib');
      } else if (process.env.LD_LIBRARY_PATH.indexOf(nss) === -1) {
        process.env.LD_LIBRARY_PATH = [path.join(nss, 'lib'), process.env.LD_LIBRARY_PATH].join(':');
      }
    
      if (fs.existsSync('/tmp/chromium') === true) {
        return '/tmp/chromium';
      }
    
      return new Promise(
        (resolve, reject) => {
          try {
            fs.chmod(bin, '0755', () => {
              fs.symlinkSync(bin, '/tmp/chromium'); return resolve('/tmp/chromium');
            });
          } catch (error) {
            return reject(error);
          }
        }
      );
    }
    

    启动Chrome时还需要使用一些必需的参数,即:

    --disable-dev-shm-usage
    --disable-setuid-sandbox
    --no-first-run
    --no-sandbox
    --no-zygote
    --single-process
    

    我希望这会有所帮助。

答案 2 :(得分:1)

正如评论中所提到的,正在开展有关在云功能中运行无头浏览器的可能解决方案的工作。直接适用的讨论:&#34; headless chrome & aws lambda&#34;可以在Google网上论坛上阅读。

答案 3 :(得分:-1)

问题在。曾经可以在Firebase Cloud Functions中运行无头铬或铬吗?答案是否定的!由于node.js项目将无法访问任何chrome / chromium可执行文件,因此将失败! (请试一试-我已经尝试过!)。

更好的解决方案是使用Phantom npm软件包,该软件包在后台使用PhantomJS: https://www.npmjs.com/package/phantom

文件和信息可以在这里找到:

http://amirraminfar.com/phantomjs-node/#/

https://github.com/amir20/phantomjs-node

我尝试抓取的网站已实现了屏幕抓取软件,其窍门是通过搜索期望的字符串或regex匹配项来等待页面加载,即如果需要regex的话,我会为regex做一个正则表达式为您带来的任何复杂性-与https://AppLogics.uk/保持联系-起价为5英镑(GPB)。

下面是一个可进行http或https调用的打字稿片段:

        const phantom = require('phantom');
        const instance: any = await phantom.create(['--ignore-ssl-errors=yes', '--load-images=no']);
        const page: any = await instance.createPage();
        const status = await page.open('https://somewebsite.co.uk/');
        const content = await page.property('content');

在JavaScript中再次相同:

        const phantom = require('phantom');
        const instance = yield phantom.create(['--ignore-ssl-errors=yes', '--load-images=no']);
        const page = yield instance.createPage();
        const status = yield page.open('https://somewebsite.co.uk/');
        const content = yield page.property('content');

那很容易!如果它是静态页面,则说明您已经完成了很多工作,您可以将HTML解析成类似cheerio npm软件包:https://github.com/cheeriojs/cheerio-专为服务器设计的核心JQuery实现!

但是,如果它是动态加载页面,即延迟加载,甚至是反抓取方法,您将需要通过循环并调用page.property('content')方法并运行文本搜索来等待页面更新。 regex,以查看您的页面是否已完成加载。

我创建了一个通用异步函数,该函数在成功时返回页面内容(作为字符串),并在失败或超时时引发异常。它以页面,文本(要搜索的字符串表示成功),错误(指示失败的字符串或表示不检查错误的null)以及超时(数字-自我说明)的变量为参数:

TypeScript:

    async function waitForPageToLoadStr(page: any, text: string, error: string, timeout: number): Promise<string> {
        const maxTime = timeout ? (new Date()).getTime() + timeout : null;
        let html: string = '';
        html = await page.property('content');
        async function loop(): Promise<string>{
            async function checkSuccess(): Promise <boolean> {
                html = await page.property('content');
                if (!isNullOrUndefined(error) && html.includes(error)) {
                    throw new Error(`Error string found: ${ error }`);
                }
                if (maxTime && (new Date()).getTime() >= maxTime) {
                    throw new Error(`Timed out waiting for string: ${ text }`);
                }
                return html.includes(text)
            }
            if (await checkSuccess()){
                return html;
            } else {
                return loop();
            }                
        }
        return await loop();
    }

JavaScript:

    function waitForPageToLoadStr(page, text, error, timeout) {
            return __awaiter(this, void 0, void 0, function* () {
                const maxTime = timeout ? (new Date()).getTime() + timeout : null;
                let html = '';
                html = yield page.property('content');
                function loop() {
                    return __awaiter(this, void 0, void 0, function* () {
                        function checkSuccess() {
                            return __awaiter(this, void 0, void 0, function* () {
                                html = yield page.property('content');
                                if (!isNullOrUndefined(error) && html.includes(error)) {
                                    throw new Error(`Error string found: ${error}`);
                                }
                                if (maxTime && (new Date()).getTime() >= maxTime) {
                                    throw new Error(`Timed out waiting for string: ${text}`);
                                }
                                return html.includes(text);
                            });
                        }
                        if (yield checkSuccess()) {
                            return html;
                        }
                        else {
                            return loop();
                        }
                    });
                }
                return yield loop();
            });
        }

我个人是这样使用此功能的:

TypeScript:

    try {
        const phantom = require('phantom');
        const instance: any = await phantom.create(['--ignore-ssl-errors=yes', '--load-images=no']);
        const page: any = await instance.createPage();
        const status = await page.open('https://somewebsite.co.uk/');
        await waitForPageToLoadStr(page, '<div>Welcome to somewebsite</div>', '<h1>Website under maintenance, try again later</h1>', 1000);
    } catch (error) {
        console.error(error);
    }

JavaScript:

    try {
        const phantom = require('phantom');
        const instance = yield phantom.create(['--ignore-ssl-errors=yes', '--load-images=no']);
        const page = yield instance.createPage();
        yield page.open('https://vehicleenquiry.service.gov.uk/');
        yield waitForPageToLoadStr(page, '<div>Welcome to somewebsite</div>', '<h1>Website under maintenance, try again later</h1>', 1000);
    } catch (error) {
        console.error(error);
    }

爬行愉快!