Main Website
Web Scraping
Web Scraping
Updated on
March 25, 2024

How to Get HTML in Puppeteer?

When working with Puppeteer, a popular Node.js library for controlling headless Chrome or Chromium browsers, one common task is to retrieve the HTML source of a web page. This can be particularly useful for tasks such as web scraping, automated testing, or generating screenshots of web pages. In this section, we’ll discuss the method to obtain the page source HTML in Puppeteer and explain why it is considered more reliable than basic functions.

How to Get Page Source HTML in Puppeteer?

The most reliable metting of getting page source HTML in Puppeteer uses a function named waitForFunction that enables you to wait for a specific Javascript expression to evaluate to true in the context of the page. By using this function in conjunction with the networkIdle feature, you can ensure that the page is fully loaded before capturing its HTML source. Here’s an example:


const puppeteer = require('puppeteer');
(async () => {
  // Launching a headless browser
  const browser = await puppeteer.launch();

  // Creating a new page
  const page = await browser.newPage();

  // Navigating to a website
  await page.goto('https://example.com');

  // Waiting for network activity to be idle for at least 500 milliseconds
  await page.waitForFunction(
    'window.performance.timing.loadEventEnd - window.performance.timing.navigationStart >= 500'
  );

  // Getting the page source HTML
  const pageSourceHTML = await page.content();

  // Closing the browser
  await browser.close();

  console.log(pageSourceHTML); // Output the page source HTML
})();

To ensure that the page has fully loaded, including Javascript-generated content, the code uses page.waitForFunction(). This method waits for network activity to be idle for at least 500 milliseconds. It calculates the time it takes for the page to load, from navigationStart to loadEventEnd. Once the page is fully loaded, the code captures the page source HTML of the page that includes any changes made by Javascript after the initial load. 

Why is networkIdle Reliable?

The reliability of networkIdle approach is based on its ability to ensure the page has loaded all its assets and that any dynamic Javascript code has executed. But before delving into the comparison between networkIdle and basic methods, let’s discuss the importance of the page source. The page source refers to the HTML markup that represents the content of a web page. Capturing this source accurately is crucial, as it forms the basis for any further processing or analysis. For many, the terms “page source” and “page source HTML” are used interchangeably, as they both signify the same concept: the raw HTML of a web page.

Now, let’s discuss what makes the networkIdle approach superior to basic methods in ensuring the accuracy of the page source.

Comprehensive wait for full page load

One of the key advantages of the networkIdle method is that it provides a comprehensive waiting mechanism. When working with a basic approach, developers often use methods like page.waitForSelector() to wait for a particular element to appear on the page. While this approach can work, it has limitations:

  • Selective Waiting: Basic methods focus on waiting for a specific element to appear. If the chosen element is not representative of the entire page, there is a risk of capturing the page source prematurely, missing dynamically generated content or asynchronous updates.
  • Potential Incompleteness: If you capture the page source before all asynchronous Javascript code has executed, you may miss important dynamic elements, leaving your page source incomplete and potentially inconsistent.

In contrast, the networkIdle method waits for the entire network activity of the page to settle down, ensuring that all resources, including dynamically generated content, are fully loaded. This approach minimizes the risk of capturing an incomplete page source and provides a more comprehensive representation of the page’s content.

Ensuring Stability for Accurate Data

The reliability of the networkIdle method stems from its ability to put the page in a stable state before capturing the page source. By waiting for the network activity to be idle, it ensures that the page is no longer undergoing changes. This stable state minimizes the chances of capturing inconsistent or erroneous data.

In contrast, basic methods do not guarantee the stability of the page at the time of capturing the page source. For instance, if a dynamic element is in the process of rendering when the basic method captures the HTML, it may lead to inaccuracies in the data.

Extracting Specific HTML Elements

Once we have successfully captured the page source HTML using the reliable networkIdle method, the next step is to extract specific HTML elements from this source. Puppeteer provides the page.$() and page.$$() methods that allow you to select single or multiple elements based on CSS selectors.

Extracting a Single Element by Selector

To extract a single HTML element by its CSS selector, you can use the page.$() method. Here’s an example that demonstrates how to do this:


const puppeteer = require('puppeteer');

(async () => {
  // Launching a headless browser
  const browser = await puppeteer.launch();

  // Creating a new page
  const page = await browser.newPage();

  // Navigating to a website
  await page.goto('https://example.com');

  // Waiting for network activity to be idle
  await page.waitForFunction(
    'window.performance.timing.loadEventEnd - window.performance.timing.navigationStart >= 500'
  );

  // Extracting a single element using a CSS selector
  const element = await page.$('h1'); // Example selector: h1

  // Extracting and logging the text content of the element
  const elementText = await page.evaluate(element => element.textContent, element);
  console.log('Extracted element:', elementText);

  // Closing the browser
  await browser.close();
})();

In this script, we navigate to a web page, wait for network activity to be idle using the networkIdle approach discussed earlier, and then use the page.$('h1') to select the first <h1> element on the page. We then extract and log the text content of the selected element using page.evaluate().

Extracting multiple elements by Selector

If you need to extract multiple elements based on a CSS selector, you can use the page.$$() method. Here’s an example:


const puppeteer = require('puppeteer');

(async () => {
  // Launching a headless browser
  const browser = await puppeteer.launch();

  // Creating a new page
  const page = await browser.newPage();

  // Navigating to a website
  await page.goto('https://example.com');

  // Waiting for network activity to be idle
  await page.waitForFunction(
    'window.performance.timing.loadEventEnd - window.performance.timing.navigationStart >= 500'
  );

  // Extracting all elements with a specific class using a CSS selector
  const elements = await page.$$('.example-class'); 

  // Extracting and logging the text content of each element
  for (const element of elements) {
    const elementText = await page.evaluate(element => element.textContent, element);
    console.log('Extracted element:', elementText);
  }

  // Closing the browser
  await browser.close();
})();

In this code, we use page.$$('.example-class') to select all elements with the CSS class '.example-class'. We then iterate through these elements, extracting and logging their text content using page.evaluate().

Extracting Rendered HTML of JavaScript-reliant Web Page

In many modern web applications and single-page applications (SPAs), the content is dynamically generated by Javascript. This poses a unique challenge when attempting to extract the rendered HTML because the initial page source HTML often lacks the content generated by Javascript. Here are some tips to tackle this challenge:

Wait for Javascript Execution

To capture the rendered HTML of a Javascript-reliant page, it’s crucial to wait for the Javascript code to execute and generate the content. As mentioned earlier, you can use the networkIdle method to wait for network activity to be idle. This ensures that all Javascript requests have been fulfilled and dynamic content has been rendered.

Use Javascript Evaluation

Puppeteer allows you to evaluate Javascript expressions within the context of the page. This capability is invaluable for accessing and manipulating the Document Object Model (DOM) of a web page. You can use page.evaluate() to run Javascript code on the page and extract or modify elements as needed.

Let’s dive into a code example that demonstrates these tips:


const puppeteer = require('puppeteer');

(async () => {
  // Launching a headless browser
  const browser = await puppeteer.launch();

  // Creating a new page
  const page = await browser.newPage();

  // Navigating to a JavaScript-reliant web page
  await page.goto('https://example-spa.com');

  // Waiting for network activity to be idle
  await page.waitForFunction(
    'window.performance.timing.loadEventEnd - window.performance.timing.navigationStart >= 500'
  );

  // Waiting for a specific element to be generated by JavaScript
  await page.waitForSelector('.dynamic-element');

  // Extracting the rendered HTML of a specific section using JavaScript evaluation
  const renderedHTML = await page.evaluate(() => {
    const dynamicElement = document.querySelector('.dynamic-element');
    return dynamicElement ? dynamicElement.innerHTML : 'Element not found';
  });

  // Logging the extracted HTML
  console.log('Rendered HTML:', renderedHTML);

  // Closing the browser
  await browser.close();
})();

In this script, we:

  • Launch a headless browser and navigate to a Javascript-reliant web page (e.g., a SPA).
  • Wait for network activity to be idle to ensure Javascript execution has completed.
  • Wait for a specific element with the class .dynamic-element to be generated by Javascript.
  • Use page.evaluate() to run Javascript code that extracts the inner HTML of the dynamic element. This ensures that we capture the content generated by Javascript.

Common Errors and How to Fix Them

Below are the common errors with an explanation of how to fix them.

Anti-Bot Measures and Proxies

Error: Some websites employ anti-bot measures to detect and block automated web scraping or testing. These measures can include CAPTCHAs, rate limiting, or blocking of IP addresses.

Solution: To bypass anti-bot measures, you can use proxies in Puppeteer to mask your IP address and mimic natural human behavior. Proxies allow you to make requests from different IP addresses, reducing the risk of being detected as a bot. There are various proxy services available, and you can integrate them with Puppeteer to rotate IP addresses and avoid detection.

Page Navigation Timing Issues

Error: Occasionally, when navigating to web pages, you may encounter timing issues where the page content is not fully loaded or Javascript execution is incomplete.

Solution: To mitigate these issues, make use of Puppeteer’s page.waitForNavigation() and page.waitForFunction() functions to ensure that the page has fully loaded before interacting with it. Additionally, you can leverage the networkIdle approach, as discussed earlier, to wait for network activity to be idle.

Selecting Unavailable Elements

Error: When using selectors to target HTML elements, you might encounter errors if the element is not present on the page or if Javascript has not generated it yet.

Solution: To address this issue, use page.waitForSelector() or similar methods to wait for the element to become available. You can set a reasonable timeout to avoid waiting indefinitely. Further, consider using page.evaluate() to access elements directly through Javascript that allows you to work with the DOM if the element is not initially present in the page source HTML.

Conclusion

In this guide, we discussed how to effectively work with Puppeteer to capture the page source HTML, extract specific elements, and handle Javascript-reliant web pages. We highlighted the reliability of the networkIdle method for ensuring the accurate capture of the page source. Additionally we discussed the use of CSS selectors for extracting specific elements. To tackle common errors, we addressed the importance of proxies in bypassing anti-bot measures and discussed how to handle page navigation timing issues and selecting unavailable elements.

Related Articles

Puppeteer Scraping: Get Started in 10 Minutes

Downloading Images in Puppeteer: 6 Methods Explained

How to Take Screenshot in Puppeteer: Complete Guide