How to get number of pages using Puppeteer?

Viewed 7659

I am an crawling beginner using Puppeteer. I succeeded in crawling the below site. Below is a code for extracting a specific product name from the shopping mall.

const express = require('express');
const puppeteer = require('puppeteer');
const app = express();

(async () => {

    const width = 1600, height = 1040;

    const option = { headless: true, slowMo: true, args: [`--window-size=${width},${height}`] };

    const browser = await puppeteer.launch(option);
    const page = await browser.newPage();
    const vp = {width: width, height: height};
    await page.setViewport(vp);

    const navigationPromise = page.waitForNavigation();

    // 네이버 스토어팜
    await page.goto('https://shopping.naver.com/home/p/index.nhn');
    await navigationPromise;
    await page.waitFor(2000);

    const textBoxId = 'co_srh_input';
    await page.type('.' + textBoxId, '양말', {delay: 100});
    await page.keyboard.press('Enter');

    await page.waitFor(5000);
    await page.waitForSelector('ul.goods_list');
    await page.addScriptTag({url: 'https://code.jquery.com/jquery-3.2.1.min.js'});

    const result = await page.evaluate(() => {

        const data = [];

        $('ul.goods_list > li._itemSection').each(function () {

            const title = $.trim($(this).find('div.info > a.tit').text());
            const price = $(this).find('div.info > .price .num').text();
            const image = $(this).find('div.img_area img').attr('src');

            data.push({ title, price, image })

        });

        return data;

    });

    console.log(result);
    await browser.close();

})();

app.listen(3000, () => console.log("Express!!!"));

I have a question. If I want to get information from number of pages, What should I do? for example ( 1 page, 2 page , 3page .... )

3 Answers

This is a difficult one due to the nature of how many pages are displayed, by default, on that site. But bear with me: I'll show you what you can achieve at the very least with this one.

Firstly, the site you provided lists 10 pages at a time underneath the list of items which you can cycle through. I'm sorry to say that I don't understand the language it is in so I don't know if there is an option somewhere to display more pages. So when you enter your search text, it displays as follows:

First ten pages listed

However when you click on the last number (the number 10), the list of pages updates to as follows:

More pages added dynamically

This makes finding the number of overall pages much more difficult since there's no option to "jump" to the very last page (and subsequently there's also no option to jump back to the very first one either). I'll show you another example later of a site which does this.

What I'd recommend you do in your case is to use some simple maths to determine exactly how many pages are going to be listed. It's going to get far too complicated to keep telling puppeteer, for example, to "keep clicking the last available page number until you reach the end" or such-like. But we can determine how many pages are there just by executing a few simple steps.

Firstly, you need to get the total number of items returned in the search list via this element:

Total number of results

You can do that by executing this code, below:

const totalItems = await page.$eval('._productSet_total', (items) => {
  // Remove the characters before the total number, leaving only the number in isolation
  const child = items.querySelector('em');
  items.removeChild(child);

  // Now remove all commas from the total number
  let finalItems = items.textContent.trim();
  while(finalItems.indexOf(',') > -1) {
    finalItems = finalItems.replace(',', '').trim();
  }
  return finalItems;
});
console.log(totalItems); // Outputs 4337903 (or something similar)

So now you've that total number. The next step is to determine how many items are going to be displayed on each page. You can do that by counting the number of items displayed on the current page - as follows:

const itemsPerPage = await page.$$eval('.goods_list li', (items) => {
  return items.length;
});
console.log(itemsPerPage); // Outputs 180 on my machine

So now you've the total number of items found and the number of items to be displayed on each page. The next part is your simple maths to determine how many pages it will take to list all of those items:

const pages = totalItems / itemsPerPage;
console.log(Math.ceil(pages));

And that's it! This was a rather difficult example due to the poor design of the site itself (really it should have a route straight to the first and / or last pages at all times).

For example, if you click on the puppeteer tag in Stack Overflow (this very wonderful site), it will bring you to: https://stackoverflow.com/questions/tagged/puppeteer

Now scroll to the bottom of the page, you'll see something like this:

enter image description here

This is ideal for determining how many pages are listed in one single, simple step, as follows:

const lastPage = await page.$$eval('div[class*="pager"] > a > span[class*="page-numbers"]', (spans) => {
  return spans[spans.length - 2].textContent;
});
console.log(lastPage); // Outputs 78

Hopefully something in here helps you with your puppeteer learning journey!

use attribute footerTemplate with displayHeaderFooter for show pages originally using puppeteer API

await page.pdf({
  path: 'hacks.pdf',
  format: 'A4',
  displayHeaderFooter: true,
  footerTemplate: '<div><div class='pageNumber'></div> <div>/</div><div class='totalPages'></div></div>'
});

https://github.com/puppeteer/puppeteer/blob/master/docs/api.md#pagepdfoptions

// footerTemplate HTML template for the print footer.

// Should be valid HTML markup with following CSS classes used to inject printing values into them:

// - date formatted print date

// - title document title

// - url document location

// - pageNumber current page number

// - totalPages total pages in the document

  if (!this.browser) {
    this.browser = await puppeteer.launch(this.OPT)
    const pages: puppeteer.Page[] = await this.browser.pages()

    // close chromium by catching 'targetdestryed'
    this.browser.on('targetdestroyed', async () => {
      if (this.browser) {
        const pages: puppeteer.Page[] = await this.browser.pages()
        if (pages.length === 0) {
          process.exit(0)
        }
      }
    })
  }

Above is the code I wrote in Typescript. You can get Array of Page(=Tab) from browser.pages() And Puppeteer absolutely has 1 tab first time.

Related