پوسته کرومی بدون سر

پوسته کروم بدون سر (Headless Chrome shell) در کروم ۵۹ ارائه شد. این پوسته روشی برای اجرای مرورگر کروم در یک محیط بدون سر است. اساساً، اجرای کروم بدون کروم! این پوسته تمام ویژگی‌های پلتفرم وب مدرن ارائه شده توسط کرومیوم و موتور رندر Blink را به خط فرمان می‌آورد.

یک مرورگر بدون سربرگ ابزاری عالی برای تست خودکار و محیط‌های سرور است که در آن‌ها به پوسته رابط کاربری قابل مشاهده نیاز ندارید. به عنوان مثال، ممکن است بخواهید برخی تست‌ها را روی یک صفحه وب واقعی اجرا کنید، یک PDF از آن ایجاد کنید یا فقط نحوه رندر کردن یک URL توسط مرورگر را بررسی کنید.

شروع بدون سر (CLI)

ساده‌ترین راه برای شروع کار با حالت بی‌سر (headless mode) باز کردن فایل باینری کروم از خط فرمان است. اگر کروم ۵۹+ را نصب کرده‌اید، کروم را با پرچم --headless اجرا کنید:

chrome \
--headless \                   # Runs Chrome in headless mode.
--disable-gpu \                # Temporarily needed if running on Windows.
--remote-debugging-port=9222 \
https://www.chromestatus.com   # URL to open. Defaults to about:blank.

chrome باید به محل نصب کروم شما اشاره کند. مکان دقیق آن از پلتفرمی به پلتفرم دیگر متفاوت خواهد بود. از آنجایی که من در مک هستم، برای هر نسخه از کروم که نصب کرده‌ام، نام‌های مستعار مناسبی ایجاد کرده‌ام.

اگر از نسخه پایدار کروم استفاده می‌کنید و نمی‌توانید نسخه بتا را دریافت کنید، chrome-canary استفاده کنید:

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

کروم کانری را دانلود کنید.

ویژگی‌های خط فرمان

در برخی موارد، ممکن است نیازی به اسکریپت‌نویسی Headless Chrome از طریق برنامه‌نویسی نداشته باشید. برخی از پرچم‌های خط فرمان مفید برای انجام وظایف رایج وجود دارد.

چاپ DOM

پرچم --dump-dom مقدار document.body.innerHTML را در خروجی استاندارد چاپ می‌کند:

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


### Create a PDF

The `--print-to-pdf` flag creates a PDF of the page:

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

گرفتن اسکرین شات

برای گرفتن اسکرین شات از یک صفحه، از فلگ --screenshot استفاده کنید:

chrome --headless --disable-gpu --screenshot https://www.chromestatus.com/

# Size of a standard letterhead.
chrome --headless --disable-gpu --screenshot --window-size=1280,1696 https://www.chromestatus.com/

# Nexus 5x
chrome --headless --disable-gpu --screenshot --window-size=412,732 https://www.chromestatus.com/

اجرای دستور --screenshot ‎ باعث ایجاد فایلی با نام screenshot.png در دایرکتوری کاری فعلی می‌شود. اگر به دنبال اسکرین‌شات‌های تمام صفحه هستید، موضوع کمی پیچیده‌تر است. یک پست وبلاگ عالی از دیوید اشنور وجود دارد که شما را پوشش می‌دهد. به «استفاده از کروم بدون سر به عنوان یک ابزار اسکرین‌شات خودکار» مراجعه کنید.

حالت REPL (حلقه خواندن-ارزیابی-چاپ)

پرچم --repl Headless را در حالتی اجرا می‌کند که می‌توانید عبارات JS را در مرورگر، مستقیماً از خط فرمان، ارزیابی کنید:

$ chrome --headless --disable-gpu --repl --crash-dumps-dir=./tmp https://www.chromestatus.com/
[0608/112805.245285:INFO:headless_shell.cc(278)] Type a Javascript expression to evaluate or "quit" to exit.
>>> location.href
{"result":{"type":"string","value":"https://www.chromestatus.com/features"}}
>>> quit
$

اشکال‌زدایی کروم بدون رابط کاربری مرورگر؟

وقتی کروم را با --remote-debugging-port=9222 اجرا می‌کنید، یک نمونه با پروتکل DevTools فعال شروع می‌شود. این پروتکل برای ارتباط با کروم و هدایت نمونه مرورگر بدون سر استفاده می‌شود. همچنین ابزارهایی مانند Sublime، VS Code و Node از آن برای اشکال‌زدایی از راه دور یک برنامه استفاده می‌کنند. #هم‌افزایی

از آنجایی که رابط کاربری مرورگر برای دیدن صفحه ندارید، در مرورگر دیگری به http://localhost:9222 بروید تا بررسی کنید که همه چیز درست کار می‌کند. فهرستی از صفحات قابل بررسی را مشاهده خواهید کرد که می‌توانید روی آنها کلیک کنید و ببینید Headless چه چیزی را رندر می‌کند:

ابزارهای توسعه از راه دور
رابط کاربری اشکال‌زدایی از راه دور DevTools

از اینجا، می‌توانید از ویژگی‌های آشنای DevTools برای بررسی، اشکال‌زدایی و تنظیم صفحه طبق معمول استفاده کنید. اگر از Headless به صورت برنامه‌نویسی استفاده می‌کنید، این صفحه همچنین یک ابزار اشکال‌زدایی قدرتمند برای مشاهده تمام دستورات خام پروتکل DevTools است که از طریق سیم و با مرورگر در ارتباط هستند.

استفاده از برنامه‌نویسی (گره)

عروسک‌گردان

Puppeteer یک کتابخانه Node است که توسط تیم Chrome توسعه داده شده است. این کتابخانه یک API سطح بالا برای کنترل Chrome بدون سر (یا کامل) ارائه می‌دهد. این کتابخانه مشابه سایر کتابخانه‌های تست خودکار مانند Phantom و NightmareJS است، اما فقط با آخرین نسخه‌های Chrome کار می‌کند.

در میان چیزهای دیگر، می‌توان از Puppeteer برای گرفتن آسان اسکرین‌شات، ایجاد فایل‌های PDF، پیمایش صفحات و دریافت اطلاعات مربوط به آن صفحات استفاده کرد. اگر می‌خواهید تست مرورگر را به سرعت خودکار کنید، این کتابخانه را توصیه می‌کنم. این کتابخانه پیچیدگی‌های پروتکل DevTools را پنهان می‌کند و از وظایف اضافی مانند راه‌اندازی یک نمونه اشکال‌زدایی از کروم مراقبت می‌کند.

نصبش کن:

npm i --save puppeteer

مثال - چاپ عامل کاربر

const puppeteer = require('puppeteer');

(async() => {
  const browser = await puppeteer.launch();
  console.log(await browser.version());
  await browser.close();
})();

مثال - گرفتن اسکرین شات از صفحه

const puppeteer = require('puppeteer');

(async() => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://www.chromestatus.com', {waitUntil: 'networkidle2'});
  await page.pdf({path: 'page.pdf', format: 'A4'});

  await browser.close();
})();

برای کسب اطلاعات بیشتر در مورد API کامل، مستندات Puppeteer را بررسی کنید.

کتابخانه CRI

chrome-remote-interface یک کتابخانه سطح پایین‌تر از API مربوط به Puppeteer است. اگر می‌خواهید به دنیای واقعی نزدیک باشید و مستقیماً از پروتکل DevTools استفاده کنید، آن را توصیه می‌کنم.

راه‌اندازی کروم

chrome-remote-interface کروم را برای شما اجرا نمی‌کند، بنابراین باید خودتان این کار را انجام دهید.

در بخش رابط خط فرمان (CLI)، ما کروم را به صورت دستی با استفاده از --headless --remote-debugging-port=9222 اجرا کردیم. با این حال، برای خودکارسازی کامل تست‌ها، احتمالاً می‌خواهید کروم را از برنامه خود اجرا کنید.

یک راه استفاده از child_process است:

const execFile = require('child_process').execFile;

function launchHeadlessChrome(url, callback) {
  // Assuming MacOSx.
  const CHROME = '/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome';
  execFile(CHROME, ['--headless', '--disable-gpu', '--remote-debugging-port=9222', url], callback);
}

launchHeadlessChrome('https://www.chromestatus.com', (err, stdout, stderr) => {
  ...
});

اما اگر بخواهید یک راه‌حل قابل حمل داشته باشید که در چندین پلتفرم کار کند، اوضاع پیچیده می‌شود. فقط به آن مسیر کدنویسی شده به کروم نگاه کنید :(

استفاده از لانچر کروم

Lighthouse ابزاری فوق‌العاده برای آزمایش کیفیت برنامه‌های وب شماست. یک ماژول قوی برای اجرای کروم در Lighthouse توسعه داده شده و اکنون برای استفاده مستقل استخراج شده است. ماژول NPM chrome-launcher محل نصب کروم را پیدا می‌کند، یک نمونه اشکال‌زدایی راه‌اندازی می‌کند، مرورگر را اجرا می‌کند و وقتی برنامه شما تمام شد، آن را از بین می‌برد. بهترین بخش این است که به لطف Node.js به صورت چند پلتفرمی کار می‌کند!

به طور پیش‌فرض، chrome-launcher سعی می‌کند Chrome Canary را اجرا کند (اگر نصب شده باشد)، اما می‌توانید آن را تغییر دهید تا به صورت دستی انتخاب کنید که از کدام Chrome استفاده کنید. برای استفاده از آن، ابتدا از npm نصب کنید:

npm i --save chrome-launcher

مثال - استفاده از chrome-launcher برای اجرای Headless

const chromeLauncher = require('chrome-launcher');

// Optional: set logging level of launcher to see its output.
// Install it using: npm i --save lighthouse-logger
// const log = require('lighthouse-logger');
// log.setLevel('info');

/**
 * Launches a debugging instance of Chrome.
 * @param {boolean=} headless True (default) launches Chrome in headless mode.
 *     False launches a full version of Chrome.
 * @return {Promise<ChromeLauncher>}
 */
function launchChrome(headless=true) {
  return chromeLauncher.launch({
    // port: 9222, // Uncomment to force a specific port of your choice.
    chromeFlags: [
      '--window-size=412,732',
      '--disable-gpu',
      headless ? '--headless' : ''
    ]
  });
}

launchChrome().then(chrome => {
  console.log(`Chrome debuggable on port: ${chrome.port}`);
  ...
  // chrome.kill();
});

اجرای این اسکریپت کار زیادی انجام نمی‌دهد، اما باید نمونه‌ای از اجرای کروم را در task manager ببینید که about:blank را بارگذاری می‌کند. به یاد داشته باشید، هیچ رابط کاربری مرورگری وجود نخواهد داشت. ما headless هستیم.

برای کنترل مرورگر، به پروتکل DevTools نیاز داریم!

بازیابی اطلاعات مربوط به صفحه

برای نصب کتابخانه:

npm i --save chrome-remote-interface
مثال‌ها

مثال - چاپ عامل کاربر

const CDP = require('chrome-remote-interface');

...

launchChrome().then(async chrome => {
  const version = await CDP.Version({port: chrome.port});
  console.log(version['User-Agent']);
});

نتایج چیزی شبیه به این خواهد بود: HeadlessChrome/60.0.3082.0

مثال - بررسی کنید که آیا سایت دارای مانیفست برنامه وب است یا خیر

const CDP = require('chrome-remote-interface');

...

(async function() {

const chrome = await launchChrome();
const protocol = await CDP({port: chrome.port});

// Extract the DevTools protocol domains we need and enable them.
// See API docs: https://chromedevtools.github.io/devtools-protocol/
const {Page} = protocol;
await Page.enable();

Page.navigate({url: 'https://www.chromestatus.com/'});

// Wait for window.onload before doing stuff.
Page.loadEventFired(async () => {
  const manifest = await Page.getAppManifest();

  if (manifest.url) {
    console.log('Manifest: ' + manifest.url);
    console.log(manifest.data);
  } else {
    console.log('Site has no app manifest');
  }

  protocol.close();
  chrome.kill(); // Kill Chrome.
});

})();

مثال - استخراج <title> صفحه با استفاده از DOM APIs.

const CDP = require('chrome-remote-interface');

...

(async function() {

const chrome = await launchChrome();
const protocol = await CDP({port: chrome.port});

// Extract the DevTools protocol domains we need and enable them.
// See API docs: https://chromedevtools.github.io/devtools-protocol/
const {Page, Runtime} = protocol;
await Promise.all([Page.enable(), Runtime.enable()]);

Page.navigate({url: 'https://www.chromestatus.com/'});

// Wait for window.onload before doing stuff.
Page.loadEventFired(async () => {
  const js = "document.querySelector('title').textContent";
  // Evaluate the JS expression in the page.
  const result = await Runtime.evaluate({expression: js});

  console.log('Title of page: ' + result.result.value);

  protocol.close();
  chrome.kill(); // Kill Chrome.
});

})();

استفاده از سلنیوم، وب‌درایور و کروم‌درایور

در حال حاضر، سلنیوم یک نمونه کامل از کروم را باز می‌کند. به عبارت دیگر، این یک راه‌حل خودکار است اما کاملاً بی‌هدف نیست. با این حال، سلنیوم را می‌توان با کمی کار پیکربندی کرد تا کروم بی‌هدف را اجرا کند. اگر می‌خواهید دستورالعمل‌های کامل نحوه راه‌اندازی خودتان را داشته باشید، من «اجرای سلنیوم با بی‌هدف کروم» را توصیه می‌کنم، اما برای شروع، چند مثال در زیر آورده‌ام.

استفاده از کروم درایور

ChromeDriver 2.32 از کروم ۶۱ استفاده می‌کند و با کروم بدون سر (headless Chrome) به خوبی کار می‌کند.

نصب:

npm i --save-dev selenium-webdriver chromedriver

مثال:

const fs = require('fs');
const webdriver = require('selenium-webdriver');
const chromedriver = require('chromedriver');

const chromeCapabilities = webdriver.Capabilities.chrome();
chromeCapabilities.set('chromeOptions', {args: ['--headless']});

const driver = new webdriver.Builder()
  .forBrowser('chrome')
  .withCapabilities(chromeCapabilities)
  .build();

// Navigate to google.com, enter a search.
driver.get('https://www.google.com/');
driver.findElement({name: 'q'}).sendKeys('webdriver');
driver.findElement({name: 'btnG'}).click();
driver.wait(webdriver.until.titleIs('webdriver - Google Search'), 1000);

// Take screenshot of results page. Save to disk.
driver.takeScreenshot().then(base64png => {
  fs.writeFileSync('screenshot.png', new Buffer(base64png, 'base64'));
});

driver.quit();

استفاده از WebDriverIO

WebDriverIO یک API سطح بالاتر بر پایه Selenium WebDriver است.

نصب:

npm i --save-dev webdriverio chromedriver

مثال: فیلتر کردن ویژگی‌های CSS در chromestatus.com

const webdriverio = require('webdriverio');
const chromedriver = require('chromedriver');

const PORT = 9515;

chromedriver.start([
  '--url-base=wd/hub',
  `--port=${PORT}`,
  '--verbose'
]);

(async () => {

const opts = {
  port: PORT,
  desiredCapabilities: {
    browserName: 'chrome',
    chromeOptions: {args: ['--headless']}
  }
};

const browser = webdriverio.remote(opts).init();

await browser.url('https://www.chromestatus.com/features');

const title = await browser.getTitle();
console.log(`Title: ${title}`);

await browser.waitForText('.num-features', 3000);
let numFeatures = await browser.getText('.num-features');
console.log(`Chrome has ${numFeatures} total features`);

await browser.setValue('input[type="search"]', 'CSS');
console.log('Filtering features...');
await browser.pause(1000);

numFeatures = await browser.getText('.num-features');
console.log(`Chrome has ${numFeatures} CSS features`);

const buffer = await browser.saveScreenshot('screenshot.png');
console.log('Saved screenshot...');

chromedriver.stop();
browser.end();

})();

دانلود کروم قدیمی Headless به عنوان chrome-headless-shell

از نسخه ۱۱۲ کروم، حالت جدید Headless کروم ( --headless=new ) در دسترس قرار گرفت. این حالت به توسعه‌دهندگان این امکان را می‌دهد که خود کروم را به جای یک فایل باینری جداگانه در یک محیط بدون نظارت و بدون هیچ رابط کاربری قابل مشاهده‌ای اجرا کنند - که برای موارد آزمایش و خودکارسازی مفید است.

موارد استفاده متمایزی برای پوسته قدیمی بدون سر (headless shell) و حالت جدید بدون سر (headless mode) وجود دارد:

  1. پوسته قدیمی Headless یک پوشش سبک وزن در اطراف ماژول //content کرومیوم است و بنابراین وابستگی‌های قابل توجهی کمتری دارد. به طور خاص، به X11/Wayland و D-Bus نیاز ندارد و از برخی جهات عملکرد بهتری نسبت به مرورگر کروم کامل دارد. این امر آن را برای مواردی مانند اسکرین شات خودکار یا وب اسکرپینگ مناسب می‌کند.
  2. از سوی دیگر، حالت جدید Headless مرورگر واقعی کروم است و بنابراین معتبرتر، قابل اعتمادتر و دارای ویژگی‌های بیشتری است. این امر آن را برای آزمایش دقیق برنامه‌های وب یا آزمایش افزونه‌های مرورگر مناسب‌تر می‌کند.

به عبارت دیگر، بین عملکرد و اصالت، یک بده بستان وجود دارد. کدام حالت Headless برای شما مناسب‌تر است؟ بستگی به مورد استفاده شما دارد.

نموداری که اطلاعات ارائه شده در لیست قبلی را نشان می‌دهد.

توسعه‌دهندگان و آزمایش‌کنندگانی که برای موارد استفاده خودکار خود به عملکرد کامل کروم نیاز ندارند، ممکن است بخواهند از Headless قدیمی استفاده کنند. در غیر این صورت، Headless جدید احتمالاً بهترین انتخاب است.

برای اطمینان از اینکه توسعه‌دهندگان و آزمایش‌کنندگان همچنان می‌توانند بین این دو گزینه حق انتخاب داشته باشند، خوشحالیم اعلام کنیم که پیاده‌سازی قدیمی Headless اکنون به عنوان یک فایل باینری chrome-headless-shell مستقل در دسترس است. این فایل‌های باینری جدید chrome-headless-shell برای هر نسخه کروم که در دسترس کاربر قرار می‌گیرد، تولید می‌شوند و از طریق زیرساخت Chrome for Testing که از Chrome 120 شروع می‌شود، برای دانلود در دسترس هستند.

چگونه می‌توانم فایل‌های باینری chrome-headless-shell را دریافت کنم؟

همانند سایر باینری‌های Chrome برای تست، ساده‌ترین راه برای دانلود chrome-headless-shell برای پلتفرم شما، استفاده از ابزار خط فرمان @puppeteer/browsers است که با استفاده از npm در دسترس است. در اینجا چند مثال آورده شده است:

# Download the latest available `chrome-headless-shell` binary corresponding to the Stable channel.
npx @puppeteer/browsers install chrome-headless-shell@stable

# Download a specific `chrome-headless-shell` version.
npx @puppeteer/browsers install chrome-headless-shell@120.0.6098.0

اگر ترجیح می‌دهید اسکریپت‌های خودکار خودتان را برای دانلود فایل‌های باینری chrome-headless-shell بسازید، ما شما را پوشش می‌دهیم. Chrome for Testing، نقاط پایانی JSON API را با آخرین نسخه‌های موجود برای هر کانال انتشار Chrome (پایدار، بتا، توسعه‌دهندگان و Canary) ارائه می‌دهد. برای دریافت یک مرور سریع از آخرین وضعیت، به داشبورد در دسترس بودن Chrome for Testing مراجعه کنید.

بازخورد

مشتاقانه منتظر شنیدن نظرات شما در مورد chrome-headless-shell هستیم. اگر با هرگونه مشکلی مواجه شدید، لطفاً آن را گزارش دهید .

منابع بیشتر

در اینجا چند منبع مفید برای شروع کار آورده شده است:

اسناد

ابزارها

  • chrome-remote-interface - ماژول گره‌ای که پروتکل DevTools را در بر می‌گیرد
  • Lighthouse - ابزار خودکار برای آزمایش کیفیت برنامه وب؛ استفاده زیادی از پروتکل می‌کند
  • chrome-launcher - ماژول گره برای اجرای کروم، آماده برای اتوماسیون

دموها

  • « وب بی‌سر » - پست وبلاگ عالی پاول کینلان در مورد استفاده از بی‌سر با api.ai.

سوالات متداول

آیا به فلگ --disable-gpu نیاز دارم؟

فقط روی ویندوز. پلتفرم‌های دیگر دیگر به آن نیازی ندارند. پرچم --disable-gpu یک راه حل موقت برای چند اشکال است. در نسخه‌های بعدی کروم به این پرچم نیازی نخواهید داشت. برای اطلاعات بیشتر به crbug.com/737678 مراجعه کنید.

پس من هنوز به Xvfb نیاز دارم؟

خیر. کروم بدون سر (Headless Chrome) از پنجره استفاده نمی‌کند، بنابراین دیگر به سرور نمایش مانند Xvfb نیازی نیست. می‌توانید با خوشحالی تست‌های خودکار خود را بدون آن اجرا کنید.

Xvfb چیست؟ Xvfb یک سرور نمایش درون حافظه‌ای برای سیستم‌های شبه یونیکس است که به شما امکان می‌دهد برنامه‌های گرافیکی (مانند کروم) را بدون نمایشگر فیزیکی متصل اجرا کنید. بسیاری از افراد از Xvfb برای اجرای نسخه‌های قبلی کروم جهت انجام تست "بدون سر" استفاده می‌کنند.

چگونه می‌توانم یک کانتینر داکر ایجاد کنم که کروم بدون سر (Headless Chrome) را اجرا کند؟

lighthouse-ci را بررسی کنید. این یک نمونه Dockerfile دارد که از node:8-slim به عنوان تصویر پایه استفاده می‌کند، Lighthouse را روی App Engine Flex نصب و اجرا می‌کند .

آیا می‌توانم از این با Selenium / WebDriver / ChromeDriver استفاده کنم ؟

بله. به بخش «استفاده از سلنیوم، وب‌درایور و کروم‌درایور» مراجعه کنید.

این چه ربطی به PhantomJS داره؟

Headless Chrome مشابه ابزارهایی مانند PhantomJS است. هر دو می‌توانند برای تست خودکار در یک محیط headless استفاده شوند. تفاوت اصلی بین این دو این است که Phantom از نسخه قدیمی WebKit به عنوان موتور رندر خود استفاده می‌کند در حالی که Headless Chrome از آخرین نسخه Blink استفاده می‌کند.

در حال حاضر، فانتوم همچنین یک API سطح بالاتر از پروتکل DevTools ارائه می‌دهد.

اشکالات را کجا گزارش کنم؟

برای اشکالات مربوط به Headless Chrome، آنها را در crbug.com ثبت کنید.

برای اشکالات موجود در پروتکل DevTools، آنها را در github.com/ChromeDevTools/devtools-protocol ثبت کنید.