Парсинг веб-страниц с помощью Puppeteer

Из этого пошагового руководства вы узнаете, как парсить статические и динамические сайты с помощью Puppeteer.
6 min read
web scraping with puppeteer

Puppeteer is a browser testing and automation library that’s also nice for web scraping. In comparison to simpler tools like Axios and cheerio, Puppeteer enables developers to scrape dynamic content (ie content that changes based on user actions).

То есть вы можете использовать ее для парсинга веб-приложений (одностраничных приложений), которые загружают контент с помощью JavaScript. Это вы и будете делать здесь.

Web Scraping Using Puppeteer

In this tutorial, you’ll learn how to scrape static and dynamic data (ie post titles and links from Bright Data’s Blog) with Puppeteer.

Setting Up

Before you begin the tutorial, you need to make sure that you have Node.js installed on your computer. You can download it from their official Downloads page.

Затем создайте новый каталог для проекта и перейдите к нему с помощью следующих команд:

mkdir puppeteer_tutorial 
cd puppeteer_tutorial 
npm init -y 

Далее установите Puppeteer с помощью этой команды:

npm i puppeteer --save

Эта команда также загружает специальный браузер, который будет использоваться библиотекой.

Scraping a Static Site

Как и все инструменты для парсинга, Puppeteer позволяет соскабливать HTML-код веб-страниц.

Ниже мы описали шаги, которые вы можете выполнить, чтобы использовать Puppeteer для парсинга первой страницы статей блога Bright Data:

Create an index.js file and import Puppeteer:

const puppeteer = require('puppeteer');

Затем вставьте шаблон, который нужен для запуска Puppeteer:

(async () => {
  const browser = await puppeteer.launch({
    headless: false,
    defaultViewport: null
  });

  const page = await browser.newPage();
  await page.goto('https://brightdata.com/blog');

  // all the web scraping will happen here  

  await browser.close();

})();

Эта функция открывает браузер, переходит на страницу для ее парсинга и закрывает браузер.

Все, что осталось сделать, это собрать данные со страницы.

In Puppeteer, the easiest way to access HTML data is through the page.evaluate method. While Puppeteer has $ and $$ methods, which are wrappers useful for fetching elements, it’s simpler to just get all the data from page.evaluate.

On the Bright Data blog, all blog post data is wrapped in an <a> tag with the class of brd_post_entry. The title of the post is in an <h3> element with the class of brd_post_title. The link to the post is the href value of the brd_post_entry.

Here’s what a page.evaluate function that extracts those values looks like:

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

    let data = [];
    const titles = document.querySelectorAll('.brd_post_entry');

    for (const title of titles) {
      const titleText = title.querySelector('.brd_post_title').textContent;
      const titleLink = title.href;

      const article = { title: titleText, link: titleLink };
      data.push(article);
    }

    return data;

  })

Теперь вы можете вывести данные в консоль:

  console.log(data);

Полный скрипт выглядит так:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({
    headless: false,
    defaultViewport: null
  });

  const page = await browser.newPage();
  await page.goto('https://brightdata.com/blog');

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

    let data = [];
    const titles = document.querySelectorAll('.brd_post_entry');

    for (const title of titles) {
      const titleText = title.querySelector('.brd_post_title').textContent;
      const titleLink = title.href;

      const article = { title: titleText, link: titleLink };
      data.push(article);
    }

    return data;

  })

  console.log(data);

  await browser.close();

})();

Run it by calling node index.js in the terminal. The script should return a list of post titles and links:

[
  {
    title: 'APIs for Dummies: Learning About APIs',
    link: 'https://brightdata.com/blog/web-data/apis-for-dummies'
  },
  {
    title: 'Guide to Using cURL with Python',
    link: 'https://brightdata.com/blog/how-tos/curl-with-python'
  },
  {
    title: 'Guide to Scraping Walmart',
    link: 'https://brightdata.com/blog/how-tos/guide-to-scraping-walmart'
  },
…

Scraping Dynamic Content

Парсинг статического контента — простая задача, которую легко решить с помощью несложных инструментов. К счастью, Puppeteer можно использовать и для выполнения более широкого спектра действий, например, клик, ввод текста и прокрутка. Вы можете использовать все это для взаимодействия с динамическими страницами и имитирования действий пользователя.

Обычная задача парсинга веб-страниц с помощью такой библиотеки — поиск определенного набора данных на странице. Например, вы можете использовать Puppeteer для поиска всех статей Bright Data про Puppeteer.

Вот как это сделать:

Step 1: Accept Cookies

Когда человек заходит в блог Bright Data, иногда появляется баннер cookie:

To deal with that, you need to click on the Accept all button with the following code:

  await page.waitForSelector('#brd_cookies_bar_accept', {timeout: 5000})
    .then(element => element.click())
    .catch(error => console.log(error));

The first line of the code waits for an item with the #brd_cookies_bar_accept to appear for 5 seconds. The second line clicks on that element. The third line makes sure that the script doesn’t crash if the cookie bar doesn’t appear.

Обратите внимание, что ожидание в Puppeteer происходит путем предоставления некоторого условия, которого вы хотите дождаться, а не путем установки определенного времени ожидания для выполнения предыдущего действия. Первое называется неявным ожиданием, а второе — явным.

Явное ожидание в Puppeteer не рекомендуется, поскольку приводит к проблемам с выполнением. Если вы укажете явное время ожидания, оно будет либо слишком длинным (что неэффективно), либо слишком коротким (скрипт будет выполняться некорректно).

Step 2: Search for Posts

После этого в скрипте нужно нажать на иконку поиска. Ввести Puppeteer и снова нажать значок поиска, чтобы запустить его:

Это можно сделать с помощью следующего кода:


await page.click('.search_icon');

  await page.waitForSelector('.search_container.active');
  const search_form = await page.waitForSelector('#blog_search');
  await search_form.type('puppeteer');

 await page.click('.search_icon');

  await new Promise(r => setTimeout(r, 2000));

This example works similarly to the cookie banner example. After clicking the button, you need to wait until the search container appears, which is why the code waits for an element that matches the CSS selector of .search_container.active.

Кроме того, в конце вам нужно добавить 2-секундную остановку для загрузки элементов. Хотя явное ожидание не рекомендуется в Puppeteer, сейчас здесь нет других хороших вариантов.

In most websites, if a change in the URL happens, you can use the waitForNavigation method. If a new element appears, you can use the waitForSelector method. Figuring out if some elements are refreshed is a bit more difficult and out of scope for this article.

If you want to try it on your own, this Stack Overflow answer can be of help.

Step 3: Collect the Posts

После поиска постов вы можете взять код, который вы уже использовали для парсинга статической страницы, чтобы получить заголовки текстов в блоге:

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

    let data = [];
    const titles = document.querySelectorAll('.brd_post_entry');

    for (const title of titles) {
      const titleText = title.querySelector('.brd_post_title').textContent;
      const titleLink = title.href;

      const article = { title: titleText, link: titleLink };
      data.push(article);
    }

    return data;

  })

  console.log(data);

Вот полный код скрипта:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({
    headless: false,
    defaultViewport: null
  });

  const page = await browser.newPage();
  await page.goto('https://brightdata.com/blog');

  const cookie_bar_accept = await page.waitForSelector('#brd_cookies_bar_accept');
  await cookie_bar_accept.click();
  await new Promise(r => setTimeout(r, 500));

  await page.click('.search_icon');

  await page.waitForSelector('.search_container.active');
  const search_form = await page.waitForSelector('#blog_search');
  await search_form.type('puppeteer');

  await page.click('.search_icon');

  await new Promise(r => setTimeout(r, 2000));

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

    let data = [];
    const titles = document.querySelectorAll('.brd_post_entry');

    for (const title of titles) {
      const titleText = title.querySelector('.brd_post_title').textContent;
      const titleLink = title.href;

      const article = { title: titleText, link: titleLink };
      data.push(article);
    }

    return data;

  })

  console.log(data);

  await browser.close();

})();

Can You Do Better?

Веб-скрапинг с помощью Puppeteer возможен, однако он не идеален. Библиотека создана для автоматизации тестирования, что делает ее немного неудобной для парсинга веб-страниц.

For example, if you want to achieve scale and efficiency in your scripts, it’s important to be able to scrape without being blocked. To do this, you can use proxies—gateways between you and the website you scrape. While Puppeteer supports the use of proxies, you need to find and contract with a proxy network on your own (learn more about Puppeteer proxy integration with Bright Data).

Кроме того, оптимизировать Puppeteer для параллельного использования непросто. Если вы хотите собрать много данных, вам придется много работать, чтобы добиться оптимальной производительности.

Эти недостатки означают, что Puppeteer — хороший выбор для небольших скриптов для хобби, но для масштабирования операции вам может потребоваться много времени.

In case you want something that’s easier to use, you can pick a web data platform like Bright Data. It enables companies to gather massive amounts of structured data from the web using easy-to-use tools, like the Scraping Browser (Puppeteer/Playwright compatible), that’s specially made for scraping.

Conclusion

В этой статье вы узнали, как использовать Puppeteer для парсинга статических и динамических веб-страниц.

Puppeteer может выполнять многие действия как браузер, включая нажатие элементов, ввод текста и выполнение JavaScript. А благодаря использованию неявных ожиданий скрипты, написанные в Puppeteer, пишутся быстро и легко.

Однако могут быть и некоторые проблемы: Puppeteer — не самый эффективный инструмент для парсинга веб-страниц, а его документация не совсем подходит для новичков. Кроме того, если вы не имеете достаточно опыта работы с Puppeteer, вам будет трудно масштабировать операции парсинга с его помощью.

Tired of scraping data yourself? Get precollected or custom datasets.