go to all blogs

In this post, I will try to walk you through the basics of Puppeteer, a browser automation library for Node.js. Puppeteer is created and maintained by Google Chrome and it's the de-facto standard when it comes for browser automation in JavaScript.

Let's get started with this post 🚀.

Puppeteer

What is Puppeteer?

Puppeteer is a Node library which provides a high-level API to control headless Chrome or Chromium over the DevTools Protocol. It can also be configured to use full (non-headless) Chrome or Chromium.

This is the definition from Puppeteer' official Website. Simply stated, it is a headless browser API which gives you ability to run Chrome or Chromium browser automatically based on the code you wrote to automate it. Now, you will ask "What is a headless browser?". So, headless browser is a browser without GUI. You can also run Puppeteer in non-headless( GUI ) mode (as given in the above definition). More on that further.

It can do various things for you and some of them are listed below:

  1. Web Scrapping

  2. Take screenshot of the page

  3. Generate PDF's of the page/s

  4. Automate certain repetitive tasks

    ... and many more.

Let's see how to install this awesome package now!

Installation

There are two ways to install this library in your machine.

  1. The standard way (Library + Browser):

    If you install this way, it will download a new version of Chromium Browser in your project directory of size ~180MB. This download will definitely take time and depends on your internet speed. After installing, you don't need to do any custom settings in order to run the code. Puppeteer will register the locally installed browser in your pwd as default to run any code involving Puppeteer.

    1npm install --save puppeteer

    Well what if you don't wanna download this ~180MB browser? There's the next step for this.

  2. The short way (Only Library):

    This is the short and less in size solution to avoid the browser download. This will only install the core package (~3MB), not the browser. If you do this way, you must have working version of Chrome or Chrome Canary browser installed in your machine which you use for daily purposes which you can use for Puppeteer by passing additional info while writing code specifying the path of the Chrome installation. (We will see this later in the post. Don't worry!)

    1npm install --save puppeteer-core

If you want to read more on puppeteer vs puppeteer-core, here is the link

Now that we have completed the installation, let's write some code using this library.

Setup

In this post we will see two working examples using Puppeteer.

  1. Scrapping Google Search Results

  2. Take screenshot of any Webpage

To get up and running for this demo, create a new Node.js project by typing

1npm run init

After initialization, you can install the package by either of the above mentioned ways. If you are using the short way, there is only one place where you have to make changes to the code. That will be clear as we see in action.

Grab some coffee and let us see the examples in action.

Scrapping Google Search Results

Now, here we will be scrapping search results for any query of your choice from Google. We will store the scrapped results in an array of objects. Actual application may require DB access after scrapping. I leave that up to you.

Firstly, we import puppeteer from puppeteer-core and then we create a browser object with puppeteer.launch() passing it launchOptions , which is an object containing optional parameters. I have used async/await while writing this code. If you want to use .then() , you can use that as well, it is basically a way to handle the returned Promise.

Description of the used launchOptions properties:

  1. headless : Whether to open Puppeteer in headless mode or not? Default value is true .

  2. defaultViewport : An object with width and height properties, which depicts its purpose itself.

  3. executablePath : Path of Chrome/ Chrome Canary/ Chromium installed in your machine. Here is an easy guide on how to find that path. You should use this property only if you are using puppeteer-core. Double "\" denotes character escaping.

You can find a detailed list of arguments here .

After this, we create a new page using browser.newPage(), which opens a new tab in the launched browser and navigates to https://www.google.com/search?q=coffee to scrape search results from. Upon successful page load, we grab the page content using page.content(). If you try to print the scraped content at this point, you will see the entire page source in the console, but we are interested in only the search title and the associated link with the result. For that, we shall use a separate package named cheerio. Cheerio is a package which can parse and do all the things with the page-source at back-end/ server which jQuery does on the front-end.

Cheerios is a Fast, flexible, and lean implementation of core jQuery designed specifically for the server.

We parse the content using cheerio and store it in a variable '\$' (used to show similarity with jQuery ). A div with class 'r' is a container for both, the search title and the actual link of one result. We then loop over all the "divs" elements with class='.r' to get the title, which is a "h3" heading with class="LC20lb DKV0Md" .Now, grab the link from the children anchor tag of the parent div using the "href " property with .attr('href') and then push the {title,link} to the links array and here we finish the process by closing the tab and the browser.

Here is the full working code for the same:

1//scrapeGoogle.js
2const puppeteer = require('puppeteer-core')
3const cheerio = require('cheerio')
4
5const run = async () => {
6 let launchOptions = {
7 headless: false, //to see the execution as it happens
8 executablePath:
9 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
10 }
11
12 let browser = await puppeteer.launch(launchOptions)
13 let page = await browser.newPage()
14
15 try {
16 await page.goto('https://www.google.com/search?q=coffee', {
17 waitUntil: 'domcontentloaded',
18 })
19 } catch (err) {
20 if (err instanceof puppeteer.errors.TimeoutError) {
21 throw new Error(err)
22 await browser.close()
23 }
24 }
25
26 let content = await page.content()
27 //cheerio
28 let $ = cheerio.load(content)
29
30 var links = []
31 $('.r').each(function (i, el) {
32 var title = $(this).find('.LC20lb').text()
33 var link = $(this).children('a').attr('href')
34 if (title.length > 0 && link.length > 0) {
35 links.push({ title, link })
36 }
37 })
38 console.log(links)
39
40 await page.close()
41
42 await browser.close()
43}
44
45run()

In this way, we have succesfully scrapped Google search results using Puppeteer. You can improve this further by adding more and more features and scrapping more data. We completed the first example here.

Taking screenshot of any Webpage

Now, this section will be very similar as above, except content scrapping. We take the screenshot with page.screenshot() which returns a Promise and on its successful resolution, our image will be saved in the folder path you specify.

1//screenshot.js
2const ss = async () => {
3 let launchOptions = {
4 headless: false,
5 executablePath:
6 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
7 defaultViewport: {
8 width: 1536,
9 height: 763,
10 },
11 }
12
13 let browser = await puppeteer.launch(launchOptions)
14 let page = await browser.newPage()
15
16 try {
17 await page.goto('https://www.google.com/search?q=chelsea', {
18 waitUntil: 'domcontentloaded',
19 })
20 } catch (err) {
21 if (err instanceof puppeteer.errors.TimeoutError) {
22 throw new Error(err)
23 await browser.close()
24 }
25 }
26
27 //main line
28 await page.screenshot({ path: 'screenshot.png' })
29
30 await page.close()
31 await browser.close()
32}
33
34ss()

As said, everything is same here except just one line where to take the screenshot and save it with name 'screenshot.png'. {path:"your_path"} is necessary, without which it will not save the screenshot.

Conclusion

Hooray, that's it for this post guys. If you have any queries regarding this post, feel free to contact me personally. If you liked this post, share it with your developer friends and social media.

Thank you. See you next time ;)