Puppeteer

How to fix Puppetteer ‘Running as root without –no-sandbox is not supported’

Problem:

When you try to run your puppetteer application, e.g. under docker, you see this error message:

Solution:

Note: Unless you are running in a Docker or similar container, first consider running the application as non-root-user!

You have to pass the --no-sandbox option to puppeteer.launch():

const browser = await puppeteer.launch({
    headless: true,
    args: ['--no-sandbox']
});

We recommend to use this slightly more complex solution to pass it only if the process is being run as root:

/**
 * Return true if the current process is run by the root user
 * https://techoverflow.net/2019/11/07/how-to-check-if-nodejs-is-run-by-root/
 */
function isCurrentUserRoot() {
   return process.getuid() == 0; // UID 0 is always root
}

const browser = await puppeteer.launch({
    headless: true,
    args: isCurrentUserRoot() ? ['--no-sandbox'] : undefined
});

This ensures Chromium is run in the most secure mode possible with the current user.

Posted by Uli Köhler in Javascript, NodeJS, Puppeteer

How to emulate the Enter key in Puppeteer

To emulate a keypress to the Enter key in Puppeteer, use

await page.keyboard.press("Enter");

The E needs to be uppercase for this to work!

Posted by Uli Köhler in Javascript, Puppeteer

How to emulate keyboard input in Puppeteer

To emulate the user typing something on the keyboard, use

await page.keyboard.type("the text");

This will type the text extremely fast with virtually no delay between the characters.

In order to simulate the finite typing speed of real users, use

await page.keyboard.type("the text", {delay: 100});

instead. The delay betwen characters in this example is 100 Milliseconds, i.e. the emulated user types 10 characters per second.

Posted by Uli Köhler in Javascript, NodeJS, Puppeteer

How to emulate TAB key press in Puppeteer

In order to emulate a tab key press in Puppeteer, use

await page.keyboard.press("Tab");

Full example:

// Minimal puppeteer example
const puppeteer = require('puppeteer');
(async () => {
  const browser = await puppeteer.launch({defaultViewport: {width: 1920, height: 1080}});
  const page = await browser.newPage();
  await page.goto('https://techoverflow.net', {waitUntil: 'domcontentloaded'});
  // Press tab 10 times (effectively scrolls down on techoverflow.net)
  for (let i = 0; i < 10; i++) {
      await page.keyboard.press("Tab");
  }
  // Screenshot to verify result
  await page.screenshot({path: 'screenshot.png'});
  // Cleanup
  await browser.close();
})();

 

Posted by Uli Köhler in Javascript, Puppeteer

How to set <input> value in Puppeteer

Use this snippet to set the value of an HTML <input> element in Puppeteer:

const newInputValue = "test 123";
await page.evaluate(val => document.querySelector('.search-form-input').value = val, newInputValue);amp

Remember to replace '.search-form-input' by whatever CSS selector is suitable to select your <input>. Examples include 'input[name="username"]' or '.username > input'.

Full example:

// Minimal puppeteer example
const puppeteer = require('puppeteer');
(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://techoverflow.net', {waitUntil: 'domcontentloaded'});
  // Set input value
  const newInputValue = "test 123";
  await page.evaluate(val => document.querySelector('.search-form-input').value = val, newInputValue);
  // Screenshot to verify result
  await page.screenshot({path: 'screenshot.png'});
  // Cleanup
  await browser.close();
})();

Note that this method will work for any simple <input>, however it might not work for some heavily Javascripted inputs which you can find on some modern websites.

Posted by Uli Köhler in Javascript, Puppeteer

How to save screenshot in Puppeteer as PNG?

You can take a screenshot in Puppeteer using

await page.screenshot({path: 'screenshot.png'});

The path is relative to the current working directory.

Want to have a screenshot in a size different to 800×600? See How to set screenshot size in Puppeteer?

Full example:

// Minimal puppeteer example
const puppeteer = require('puppeteer');
(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://techoverflow.net', {waitUntil: 'domcontentloaded'});
  // Screenshot to verify result
  await page.screenshot({path: 'screenshot.png'});
  // Cleanup
  await browser.close();
})();

 

Posted by Uli Köhler in Javascript, NodeJS, Puppeteer

Minimal puppeteer response interception example

Using Python (pyppeteer)? Check out Pyppetteer minimal network response interception example

This example shows you how to intercept network responses in puppeteer.

Note: This intercepts the response, not the request! This means you can abort the request before it is actually sent to the server, but you can’t read the content of the response! See Minimal puppeteer request interception example for an example on how to intercept requests.

const puppeteer = require('puppeteer');
(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  // Enable response interception
  page.on('response', async (response) => {
      console.info("URL", response.request().url());
      console.info("Method", response.request().method())
      console.info("Response headers", response.headers())
      console.info("Request headers", response.request().headers())
      // Use this to get the content as text
      const responseText = await response.text();
      // ... or as buffer (for binary data)
      const responseBuffer = await response.buffer();
      // ... or as JSON, if it's a JSON (else, this will throw!)
      const responseObj = await response.json();
  })
  await page.goto('https://techoverflow.net', {waitUntil: 'domcontentloaded'});
  // Make a screenshot
  await page.screenshot({path: 'screenshot.png'});
  await browser.close();
})();

 

Posted by Uli Köhler in Javascript, Puppeteer

Minimal puppeteer request interception example

Using Python (pyppeteer)? Check out Pyppetteer minimal network request interception example

This example shows you how to intercept network requests in puppeteer:

Note: This intercepts the request, not the response! This means you can abort the request made, but you can’t read the content of the response! See Minimal puppeteer response interception example for an example on how to intercept responses.

const puppeteer = require('puppeteer');
(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  // Enable request interception
  await page.setRequestInterception(true);
  page.on('request', async (request) => {
      console.info("URL", request.url());
      console.info("Method", request.method())
      console.info("Headers", request.headers())
      return request.continue(); // Allow request to continue
      // return request.abort(); // use this instead to abort the request!
  })
  await page.goto('https://techoverflow.net', {waitUntil: 'domcontentloaded'});
  // Make a screenshot
  await page.screenshot({path: 'screenshot.png'});
  await browser.close();
})();

 

Posted by Uli Köhler in Javascript, Puppeteer

How to fix Puppeteer ‘Error: Protocol error (Emulation.setDeviceMetricsOverride): Invalid parameters width: integer value expected; height: integer value expected’

Problem:

You are trying to set the viewport size in Puppeteer using

const browser = await puppeteer.launch({
    defaultViewport: '1920x1080'
});

but you see an error message like

(node:15692) UnhandledPromiseRejectionWarning: Error: Protocol error (Emulation.setDeviceMetricsOverride): Invalid parameters width: integer value expected; height: integer value expected
    at /home/uli/dev/myproject/node_modules/puppeteer/lib/Connection.js:183:56
    at new Promise (<anonymous>)
    at CDPSession.send (/home/uli/dev/myproject/node_modules/puppeteer/lib/Connection.js:182:12)
    at EmulationManager.emulateViewport (/home/uli/dev/myproject/node_modules/puppeteer/lib/EmulationManager.js:41:20)
    at Page.setViewport (/home/uli/dev/myproject/node_modules/puppeteer/lib/Page.js:808:54)
    at Page.<anonymous> (/home/uli/dev/myproject/node_modules/puppeteer/lib/helper.js:112:23)
    at Function.create (/home/uli/dev/myproject/node_modules/puppeteer/lib/Page.js:49:18)
    at processTicksAndRejections (internal/process/task_queues.js:85:5)
    at async Browser._createPageInContext (/home/uli/dev/myproject/node_modules/puppeteer/lib/Browser.js:177:18)
    at async /home/uli/dev/myproject/index.js:8:16
  -- ASYNC --
    at Target.<anonymous> (/home/uli/dev/myproject/node_modules/puppeteer/lib/helper.js:111:15)
    at Browser._createPageInContext (/home/uli/dev/myproject/node_modules/puppeteer/lib/Browser.js:177:31)
    at processTicksAndRejections (internal/process/task_queues.js:85:5)
    at async /home/uli/dev/myproject/index.js:8:16
  -- ASYNC --
    at Browser.<anonymous> (/home/uli/dev/myproject/node_modules/puppeteer/lib/helper.js:111:15)
    at /home/uli/dev/myproject/index.js:8:30
    at processTicksAndRejections (internal/process/task_queues.js:85:5)
(node:15692) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:15692) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Solution:

You need to use

{defaultViewport: {width: 1920, height: 1080}}

instead of

{defaultViewport: '1920x1080'}

Full example:

// Minimal puppeteer example
const puppeteer = require('puppeteer');
(async () => {
  const browser = await puppeteer.launch({
      defaultViewport: {width: 1920, height: 1080}
  });
  const page = await browser.newPage();
  await page.goto('https://techoverflow.net', {waitUntil: 'domcontentloaded'});
  // Make a screenshot
  await page.screenshot({path: 'screenshot.png'});
  await browser.close();
})();

 

Posted by Uli Köhler in Javascript, Puppeteer

How to set screenshot size in Puppeteer?

Problem:

You are trying to make a screenshot using Puppeteer like this:

await page.screenshot({path: 'screenshot.png'});

but the screenshot is always 800×600 pixels. How can you increase the size of the screenshot?

Solution:

You can’t set the size for the screenshot, you need to set the size for the viewport in puppeteer.launch:

const browser = await puppeteer.launch({
    defaultViewport: {width: 1920, height: 1080}
});

Full example to produce a 1920x1080 screenshot:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({
      defaultViewport: {width: 1920, height: 1080}
  });
  const page = await browser.newPage();
  await page.goto('https://techoverflow.net', {waitUntil: 'domcontentloaded'});
  // Wait until page has loaded completely
  // Make a screenshot
  await page.screenshot({path: 'screenshot.png'});

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

 

Posted by Uli Köhler in Javascript, Puppeteer

Puppeteer minimal example

You can use this example as a starting point for your puppeteer application.

// Minimal puppeteer example
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://techoverflow.net', {waitUntil: 'domcontentloaded'});
  //
  // YOUR CODE GOES HERE!
  //
  await browser.close();
})();

The screenshot is 800x600px by default and could look like this:

Posted by Uli Köhler in Javascript, NodeJS, Puppeteer

How to intercept AJAX JSON response in Pyppeteer

This example shows you how to intercept and print the content of a JSON response requested via any AJAX request on a web page by using Pyppeteer:

import asyncio
import json
from pyppeteer import launch

async def intercept_network_response(response):
    # In this example, we care only about responses returning JSONs
    if "application/json" in response.headers.get("content-type", ""):
        # Print some info about the responses
        print("URL:", response.url)
        print("Method:", response.request.method)
        print("Response headers:", response.headers)
        print("Request Headers:", response.request.headers)
        print("Response status:", response.status)
        # Print the content of the response
        try:
            # await response.json() returns the response as Python object
            print("Content: ", await response.json())
        except json.decoder.JSONDecodeError:
            # NOTE: Use await response.text() if you want to get raw response text
            print("Failed to decode JSON from", await response.text())

async def main():
    browser = await launch()
    page = await browser.newPage()
    
    page.on('response', intercept_network_response)
            
    await page.goto('https://instagram.com')
    await browser.close()

asyncio.get_event_loop().run_until_complete(main())
Posted by Uli Köhler in Puppeteer, Python

Pyppetteer minimal network response interception example

Using Javascript (puppeteer)? Check out Minimal puppeteer response interception example

This example shows you how to intercept network responses in pyppeteer.

Note: This intercepts the response, not the request! This means you can abort the request before it is actually sent to the server, but you can’t read the content of the response! See Pyppetteer minimal network request interception example for an example on how to intercept requests.

import asyncio
from pyppeteer import launch

async def intercept_network_response(response):
    # In this example, we only care about HTML responses!
    if "text/html" in response.headers.get("content-type", ""):
        # Print some info about the responses
        print("URL:", response.url)
        print("Method:", response.request.method)
        print("Response headers:", response.headers)
        print("Request Headers:", response.request.headers)
        print("Response status:", response.status)
        # Print the content of the response
        print("Content: ", await response.text())
        # NOTE: Use await response.json() if you want to get the JSON directly

async def main():
    browser = await launch()
    page = await browser.newPage()
    
    page.on('response', intercept_network_response)
            
    await page.goto('https://techoverflow.net')
    await browser.close()

asyncio.get_event_loop().run_until_complete(main())

 

Posted by Uli Köhler in Puppeteer, Python

Pyppetteer minimal network request interception example

Using Javascript (puppeteer)? Check out Minimal puppeteer request interception example

This example shows you how to intercept network requests in pyppeteer:

Note: This intercepts the request, not the response! This means you can abort the request made, but you can’t read the content of the response! See Pyppetteer minimal network response interception example for an example on how to intercept responses.

import asyncio
from pyppeteer import launch

async def intercept_network_request(request):
    # Print some info about the request
    print("URL:", request.url)
    print("Method:", request.method)
    print("Headers:", request.headers)
    # NOTE: You can also await request.abort() to abort the requst1
    await request.continue_()

async def main():
    browser = await launch()
    page = await browser.newPage()
    await page.setRequestInterception(True)
    
    page.on('request', intercept_network_request)
            
    await page.goto('https://techoverflow.net')
    await browser.close()

asyncio.get_event_loop().run_until_complete(main())

 

Posted by Uli Köhler in Puppeteer, Python

How to fix pyppeteer.errors.NetworkError: Request interception is not enabled.

Note: Also see Pyppetteer minimal network request interception example

Problem:

You are trying to intercept a request in Pyppeteer using

page.on('request', my_intercept_request)

but you’re getting an error message like this:

Traceback (most recent call last):
  File "/usr/lib/python3.6/asyncio/events.py", line 145, in _run
    self._callback(*self._args)
  File "/usr/local/lib/python3.6/dist-packages/pyee/_compat.py", line 62, in _callback
    self.emit('error', exc)
  File "/usr/local/lib/python3.6/dist-packages/pyee/_base.py", line 106, in emit
    self._emit_handle_potential_error(event, args[0] if args else None)
  File "/usr/local/lib/python3.6/dist-packages/pyee/_base.py", line 83, in _emit_handle_potential_error
    raise error
  File "run.py", line 6, in intercept
    await request.continue_()
  File "/usr/local/lib/python3.6/dist-packages/pyppeteer/network_manager.py", line 481, in continue_
    raise NetworkError('Request interception is not enabled.')
pyppeteer.errors.NetworkError: Request interception is not enabled.

Solution:

Add

await page.setRequestInterception(True)

directly after your call to

page = await browser.newPage()

This will enable request interception and your code will run just fine.

Posted by Uli Köhler in Puppeteer, Python

Puppeteer: Get text content / inner HTML of an element

Problem:

You want to use puppeteer to automate testing a webpage. You need to get either the text or the inner HTML of some element, e.g. of

<div id="mydiv">
</div>

on the page.

Solution:

// Get inner text
const innerText = await page.evaluate(() => document.querySelector('#mydiv').innerText);

// Get inner HTML
const innerHTML = await page.evaluate(() => document.querySelector('#mydiv').innerHTML);

Note that .innerText includes the text of sub-elements. You can use the complete DOM API inside page.evaluate(...). You can use any CSS selector as an argument for document.querySelector(...).

Posted by Uli Köhler in Javascript, Puppeteer

How to fix puppetteer error while loading shared libraries: libX11-xcb.so.1: cannot open shared object file: No such file or directory

Problem:

You are trying to run puppetteer on Ubuntu, but when it starts to run chrome, you are facing the following issue:

/home/user/erp/node_modules/puppeteer/.local-chromium/linux-555668/chrome-linux/chrome: error while loading shared libraries: libX11-xcb.so.1: cannot open shared object file: No such file or directory

Solution:

Install the missing packages using

sudo apt install -y gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget

Credits to @coldner on the puppetteer issue tracker for assembling the required pkgs.

If you encounter E: Unable to locate package errors, run sudo apt-get update.

Background information

If you want to know more on why this issue occurs, continue reading here.

Puppetteer is essentially a minimal headless (see What is a headless program or application?) Chromium instance with an additional API for controlling and monitoring it from NodeJS.

Even though Puppetteer does not actually display a GUI, the Chromium instance it uses still requires some of the libraries to draw a GUI and connect to the X11 server, even though that isn’t used in Puppetteer. One of those libraries is libxcb which provides the shared library libX11-xcb.so.1. You can fix this by installing the libx11-xcb1 package on most Debian-based systems.

However, as it is so often the case with missing shared libraries, once you install the one that is missing, there will be at least one other library missing after that. That’s why we need to install the large number of libraries listed above.

Posted by Uli Köhler in Linux, Puppeteer