有没有办法在Google Cloud Function中运行无头Chrome / Chromium?我知道可以在GCF中包含和运行静态编译的二进制文件。我能否获得一个静态编译版本的Chrome来实现这一点?
有没有办法在Google Cloud Function中运行无头Chrome / Chromium?我知道可以在GCF中包含和运行静态编译的二进制文件。我能否获得一个静态编译版本的Chrome来实现这一点?
Google云函数的Node.js 8运行时现在包含所有必要的操作系统软件包来运行Headless Chrome。
这是一个返回屏幕截图的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"
}
}
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);
}
}
);
}
--disable-dev-shm-usage
--disable-setuid-sandbox
--no-first-run
--no-sandbox
--no-zygote
--single-process
正如评论中所提到的,正在努力寻找在云函数中运行无头浏览器的可能解决方案。可以在Google Groups上阅读一个直接适用的讨论:“headless chrome & aws lambda”。
我曾经问过一个问题:在Firebase Cloud Functions中能否运行无头Chrome或Chromium...答案是否定的!因为node.js项目将无法访问任何chrome/chromium可执行文件,因此会失败!(相信我-我已经尝试过了!)
更好的解决方案是使用Phantom npm包,其在幕后使用PhantomJS: https://www.npmjs.com/package/phantom
文档和信息可以在这里找到:
http://amirraminfar.com/phantomjs-node/#/
或者
https://github.com/amir20/phantomjs-node
我试图爬取的网站已经实现了屏幕抓取软件,诀窍是通过搜索预期的字符串或正则表达式匹配来等待页面加载,例如,我对一个进行正则表达式匹配,如果您需要任何复杂度的正则表达式 - 请联系https://AppLogics.uk/ - 起价为5英镑(GBP)。
这里是一个TypeScript片段,用于进行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')
方法并运行文本搜索或正则表达式来查看页面是否已经加载完成。
我创建了一个通用的异步函数,在成功时返回页面内容(作为字符串),在失败或超时时抛出异常。它以页面、文本(指示成功的字符串)、错误(指示失败的字符串或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();
});
}
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);
}
祝你爬取愉快!