有没有办法在Google Cloud功能中运行Headless Chrome/Chromium?我知道我可以在GCF中包含和运行静态编译的二进制文件。我可以获得适用于此的静态编译版Chrome吗?
答案 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,您应该能够在线找到大量说明。
对于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);
}
爬行愉快!