{"componentChunkName":"component---src-gatsby-theme-chronoblog-templates-note-js","path":"/notes/browser-automation-puppeteer/","result":{"data":{"mdx":{"parent":{"__typename":"File","fields":{"gitLogLatestDate":"2026-06-11 21:09:12 +0200"}},"id":"2a25cf66-32c3-5444-8e7f-5b83723c1ba0","excerpt":"Puppeteer is a headless browser for automating browser tasks. Here's the list of some of the features: Turn off headless mode Resize the…","frontmatter":{"title":"Browser automation with Puppeteer","date":"2023-08-26 18:09:00 UTC","job_ad":null,"job_ad_id":null,"job_ad_url":null,"tags":["puppeteer","node"],"cover":{"childImageSharp":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAAoklEQVQoz7WS2wqDQAxE9/+/sjf1oa73JHW1e4pWoQVp+2ADQ0hmJhASx87hYoysWOO1t4VVs6V138yT73P9zrl9F44473vKYsB0xExRFUyHuTdxXXsn9CNlESiKgHR3zAwRRU0xG56cD9ws4s6nmiTpaJuA9zl5fqUqhSxVjodyHizSk6XC5dzS1OOi83OuK1m4BpX4h5W/H+V3/Odt9n7sB/SFFfLtKv0bAAAAAElFTkSuQmCC","aspectRatio":2.0869565217391304,"src":"/static/1e8d31e808f41f4fb38808f01f53e0e7/c4ecb/cover.png","srcSet":"/static/1e8d31e808f41f4fb38808f01f53e0e7/57ab0/cover.png 192w,\n/static/1e8d31e808f41f4fb38808f01f53e0e7/f4739/cover.png 384w,\n/static/1e8d31e808f41f4fb38808f01f53e0e7/c4ecb/cover.png 768w","srcWebp":"/static/1e8d31e808f41f4fb38808f01f53e0e7/dd090/cover.webp","srcSetWebp":"/static/1e8d31e808f41f4fb38808f01f53e0e7/ae504/cover.webp 192w,\n/static/1e8d31e808f41f4fb38808f01f53e0e7/fef30/cover.webp 384w,\n/static/1e8d31e808f41f4fb38808f01f53e0e7/dd090/cover.webp 768w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":366},"resize":{"src":"/static/1e8d31e808f41f4fb38808f01f53e0e7/c4ecb/cover.png"}}}},"fields":{"slug":"/notes/browser-automation-puppeteer/","readingTime":{"text":"4 min read"}},"body":"function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"title\": \"Browser automation with Puppeteer\",\n  \"date\": \"2023-08-26 18:09:00 UTC\",\n  \"cover\": \"./cover.png\",\n  \"tags\": [\"puppeteer\", \"node\"],\n  \"canonical_url\": \"https://sevic.dev/notes/browser-automation-puppeteer/\"\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, [\"components\"]);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"Puppeteer is a headless browser for automating browser tasks. Here's the list of some of the features:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Turn off headless mode\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const browser = await puppeteer.launch({\\n  headless: false\\n  // ...\\n});\\n\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Resize the viewport to the window size\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const browser = await puppeteer.launch({\\n  // ...\\n  defaultViewport: null\\n});\\n\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Emulate screen how it's shown to the user via the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"emulateMediaType\"), \" method\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"await page.emulateMediaType('screen');\\n\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Save the page as a PDF file with a specified path, format, scale factor, and page range\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"await page.pdf({\\n  path: 'path.pdf',\\n  format: 'A3',\\n  scale: 1,\\n  pageRanges: '1-2',\\n  printBackground: true\\n});\\n\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Use preexisting user's credentials to skip logging in to some websites. The user data directory is a parent of the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Profile Path\"), \" value from the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"chrome://version\"), \" page.\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const browser = await puppeteer.launch({\\n  userDataDir:\\n    'C:\\\\\\\\Users\\\\\\\\<USERNAME>\\\\\\\\AppData\\\\\\\\Local\\\\\\\\Google\\\\\\\\Chrome\\\\\\\\User Data',\\n  args: []\\n});\\n\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Use Chrome instance instead of Chromium by utilizing the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Executable Path\"), \" from the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"chrome://version\"), \" URL. Close Chrome browser before running the script\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const browser = await puppeteer.launch({\\n  executablePath: puppeteer.executablePath('chrome')\\n  // ...\\n});\\n\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Switch to the selected tab\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"await page.bringToFront();\\n\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Get value based on evaluation in the browser page\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const shouldPaginate = await page.evaluate(\\n  (param1, param2) => {\\n    // ...\\n  },\\n  param1,\\n  param2\\n);\\n\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Get HTML content from the specific element\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const html = await page.evaluate(\\n  () => document.querySelector('.field--text').outerHTML\\n);\\n\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Wait for a specific selector to be loaded. You can also provide a timeout in milliseconds\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"await page.waitForSelector('.success', { timeout: 5000 });\\n\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Manipulate with a specific element and click on some of the elements\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"await page.$eval('#header', async (headerElement) => {\\n  // ...\\n  headerElement\\n    .querySelectorAll('svg')\\n    .item(13)\\n    .parentNode.click();\\n});\\n\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Extend execution of the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"$eval\"), \" method\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const browser = await puppeteer.launch({\\n  // ...\\n  protocolTimeout: 0\\n});\\n\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Manipulate with multiple elements\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"await page.$$eval('.some-class', async (elements) => {\\n  // ...\\n});\\n\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Wait for navigation (e.g., form submitting) to be done\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"await page.waitForNavigation({ waitUntil: 'networkidle0', timeout: 0 });\\n\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Trigger hover event on some of the elements\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"await page.$eval('#header', async (headerElement) => {\\n  const hoverEvent = new MouseEvent('mouseover', {\\n    view: window,\\n    bubbles: true,\\n    cancelable: true\\n  });\\n\\n  headerElement.dispatchEvent(hoverEvent);\\n});\\n\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Expose a function in the browser and use it in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"$eval\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"$$eval\"), \" callbacks (e.g., simulate typing using the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"window.type\"), \" function)\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"await page.exposeFunction('type', async (selector, text, options) => {\\n  await page.type(selector, text, options);\\n});\\n\\nawait page.$$eval('.some-class', async (elements) => {\\n  // ...\\n  window.type(selector, text, { delay: 0 });\\n});\\n\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Press the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Enter\"), \" button after typing the input field value\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"await page.type(selector, `${text}${String.fromCharCode(13)}`, options);\\n\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Open a file chooser and select file for upload\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const [fileChooser] = await Promise.all([page.waitForFileChooser(), page.click(selector)]);\\n\\nconst filePath = `C:/Users/<USERNAME>/Downloads/test.jpeg`; // use \\\"/\\\" instead of \\\"\\\\\\\" in file path\\nawait fileChooser.accept([filePath]);\\n\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Remove the value from the input field before typing the new one\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"await page.click(selector, { clickCount: 3 });\\nawait page.type(selector, text, options);\\n\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Expose a variable in the browser by passing it as the third argument for \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"$eval\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"$$eval\"), \" methods and use it in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"$eval\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"$$eval\"), \" callbacks\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"await page.$eval(\\n  '#element',\\n  async (element, customVariable) => {\\n    // ...\\n  },\\n  customVariable\\n);\\n\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Mock response for the specific request\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"await page.setRequestInterception(true);\\npage.on('request', async function(request) {\\n  const url = request.url();\\n  if (url !== REDIRECTION_URL) {\\n    return request.continue();\\n  }\\n\\n  await request.respond({\\n    contentType: 'text/html',\\n    status: 304,\\n    body: '<body></body>'\\n  });\\n});\\n\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Intercept page redirections (via interceptor) and open them in new tabs rather than following them in the same tab\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"await page.setRequestInterception(true);\\npage.on('request', async function(request) {\\n  const url = request.url();\\n  if (url !== REDIRECTION_URL) {\\n    return request.continue();\\n  }\\n\\n  await request.respond({\\n    contentType: 'text/html',\\n    status: 304,\\n    body: '<body></body>'\\n  });\\n  const newPage = await browser.newPage();\\n  await newPage.goto(url, { waitUntil: 'domcontentloaded', timeout: 0 });\\n  // ...\\n  await newPage.close();\\n});\\n\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Intercept page response\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"page.on('response', async (response) => {\\n  if (response.url() === RESPONSE_URL) {\\n    if (response.status() === 200) {\\n      // ...\\n    }\\n    // ...\\n  }\\n});\\n\")))), mdx(\"h3\", {\n    \"id\": \"demo\"\n  }, \"Demo\"), mdx(\"p\", null, \"Runnable tests for each API pattern live in the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"browser-automation-puppeteer-demo\"), \" folder. Get access via \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://sevic.dev/demos\"\n  }), \"code demos\"), \".\"));\n}\n;\nMDXContent.isMDXComponent = true;"}},"pageContext":{"id":"2a25cf66-32c3-5444-8e7f-5b83723c1ba0"}},"staticQueryHashes":["1961101537","2542493696"]}