CoderCastrov logo
CoderCastrov
Веб-разработка

Парсинг веб-страниц с использованием Ruby (и ScrapingBee API)

Парсинг веб-страниц с использованием Ruby (и ScrapingBee API)
просмотров
8 мин чтение
#Веб-разработка

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

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


Как работает парсинг веб-страниц?

Парсинг веб-страниц довольно прост на первый взгляд. Вы начинаете с загрузки веб-страницы в вашем браузере, а затем, используя инструменты разработчика, вы можете проанализировать структуру контента. Веб-сайты обычно имеют предсказуемую структуру, идентификаторы и имена классов, которые позволяют вам точно определить различные элементы на сайте. Возьмем, например, этот фрагмент HTML, который отображает таблицу цен:

<div class="card-deck mb-3 text-center">
  <div class="card mb-4 shadow-sm">
    <div class="card-header">
      <h4 class="my-0 font-weight-normal">Бесплатно</h4>
    </div>
    <div class="card-body">
      <h1 class="card-title pricing-card-title">$0 <small class="text-muted">/ месяц</small></h1>
      <ul class="list-unstyled mt-3 mb-4">
        <li>10 пользователей включено</li>
        <li>2 ГБ хранилища</li>
        <li>Поддержка по электронной почте</li>
        <li>Доступ к справочному центру</li>
      </ul>
      <button type="button" class="btn btn-lg btn-block btn-outline-primary">Бесплатная регистрация</button>
    </div>
  </div>
  <div class="card mb-4 shadow-sm">
    <div class="card-header">
      <h4 class="my-0 font-weight-normal">Профессиональный</h4>
    </div>
    <div class="card-body">
      <h1 class="card-title pricing-card-title">$15 <small class="text-muted">/ месяц</small></h1>
      <ul class="list-unstyled mt-3 mb-4">
        <li>20 пользователей включено</li>
        <li>10 ГБ хранилища</li>
        <li>Приоритетная поддержка по электронной почте</li>
        <li>Доступ к справочному центру</li>
      </ul>
      <button type="button" class="btn btn-lg btn-block btn-primary">Начать</button>
    </div>
  </div>
</div>

Вы можете перебрать все элементы с классом .card, чтобы получить оба варианта тарифных планов, а затем в каждом из них вы можете получить название плана, используя класс .card-header. Используя элементы li, вы даже можете получить характеристики плана. Давайте сделаем это на Ruby:

require 'nokogiri'

# Весь документ
html = '<div class="card-deck mb-...'

# Создаем документ Nokogiri
doc = Nokogiri::HTML(html)

# Получаем все названия планов
doc.css('.card').map { |card| card.css('.card-header h4').text }
# => ["Бесплатно", "Профессиональный"]

# Получаем структурированные данные
doc.css('.card').map do |card|
  title = card.css('.card-header h4').text
  price = card.css('.card-body .pricing-card-title').text
  features = card.css('.card-body ul li').map do |feature|
    feature.text
  end

  { title: title, price: price, features: features }
end

# => [{:title=>"Бесплатно",
#      :price=>"$0 / месяц",
#      :features=>
#      ["10 пользователей включено",
#        "2 ГБ хранилища",
#        "Поддержка по электронной почте",
#        "Доступ к справочному центру"]},
#    {:title=>"Профессиональный",
#      :price=>"$15 / месяц",
#      :features=>
#      ["20 пользователей включено",
#        "10 ГБ хранилища",
#        "Приоритетная поддержка по электронной почте",
#        "Доступ к справочному центру"]}]

Мы используем Nokogiri, отличную библиотеку для разбора XML/HTML документов на Ruby (о ней поговорим позже). В первой выделенной строке мы загружаем документ в Nokogiri. Затем, во второй выделенной строке, мы извлекаем названия планов. Мы делаем это, перебирая все элементы с CSS классом .card, как мы обсудили выше. В последнем выделенном блоке мы идем еще дальше и извлекаем название, цену и все характеристики. Используя различные CSS селекторы, вы можете получить практически все, что вам нужно из документа.

Различные методы и динамический контент

Существует множество различных методов выполнения парсинга веб-страниц. Все зависит от ваших потребностей и формата данных. Часто приходится прибегать к комбинации способов отображения и извлечения данных. Например, простая веб-страница, подобная моей, очень легко извлекается, потому что она имеет простую структуру и отсутствие динамического контента. Сравните это, например, с веб-сайтом Facebook, где есть множество динамического контента, и вы столкнетесь с очень сложной задачей по извлечению данных.

<html lang="en">
<head>
  <title>Одностраничное приложение</title>
</head>
<body>
  <div id="app"></div>
  <script src="[https://example.org/some-framework.js](https://example.org/some-framework.js)"></script>
  <script>
    MyFramework.start(document.getElementById('app'));
  </script>
</body>
</html>

Дело в том, что на динамических веб-сайтах (также называемых "одностраничными приложениями") HTML, который сначала отображается, обычно почти пустой. За кулисами работает JavaScript, который загружает фреймворк и выполняет некоторые сетевые запросы, которые, наконец, отображают контент. Для таких ситуаций вам понадобится что-то вроде браузера, чтобы отобразить веб-сайт, и только после этого вы сможете начать извлекать данные. Посмотрите, например, на приведенный выше фрагмент кода: здесь нет контента! Это потому, что сам фреймворк будет выполнять всю тяжелую работу после этого.

Headless Chrome и Прокси

Одно из решений проблемы, описанной выше, - использовать фактический веб-браузер для получения контента. Если вы попытаетесь получить веб-сайт, используя cURL, вы получите только (почти) пустой документ. Вы можете открыть свой браузер, загрузить веб-сайт, скопировать отрендеренный HTML из инструментов разработчика и разобрать его, но в конечном итоге вам потребуется автоматизировать этот процесс. Вот где приходят на помощь инструменты, такие как Headless Chrome. Headless Chrome позволяет вам управлять экземпляром Chrome программно, и без необходимости открытия окна с полным пользовательским интерфейсом (отсюда и "безголовый"). Поскольку это полноценный браузер, вы получаете все функции, в частности, рендеринг окончательного размера, который вам нужен.

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

Одним из таких прокси-сервисов является ScrapingBee, который выполняет оба этих действия: headless chrome и проксирование. И их услуги доступны в RapidAPI, поэтому начать очень просто.

Давайте сделаем парсер!

Мы создадим простой парсер, который будет использовать ScrapingBee для рендеринга веб-сайта и проксирования подключения. Для этого вам понадобятся:

  • Ruby,
  • гем nokogiri,
  • гем excon,
  • учетная запись RapidAPI.

Чтобы установить Ruby, ознакомьтесь с их официальным руководством по установке. Нам также понадобится Nokogiri. Этот гем, как вы видели раньше, позволяет легко парсить и извлекать данные из HTML-документов. Установка немного сложна и действительно зависит от вашей платформы. Поэтому мы также хотели бы ссылаться на их руководства по установке, которые должны указать вам правильное направление для установки. Если вы можете запустить gem install nokogiri в терминале без ошибок, вы готовы к работе. Также убедитесь, что вы установили Excon, запустив gem install excon.

Чтобы получить учетную запись RapidAPI, просто перейдите на rapidapi.com для регистрации. API ScrapingBee имеет бесплатный тариф использования, но все равно требует настройки кредитной карты. В любом случае, вам не нужно будет платить за то, что мы делаем в этом руководстве, так как их бесплатный план достаточно щедрый.

Настройка ScrapingBee

Перейдите на страницу ScrapingBee в RapidAPI и нажмите кнопку "Подписаться" для бесплатного плана. Введите данные своей кредитной карты, если требуется.

После этого перейдите на вкладку "Endpoints" и скопируйте ваш хост и API-ключ. Они отображаются как X-RapidAPI-Host и X-RapidAPI-Key соответственно. Вам понадобятся оба значения для следующих разделов.

Извлечение данных API с веб-сайта RapidAPI

В качестве примера того, что можно сделать с ScrapingBee и Ruby, мы извлечем список API с RapidAPI. Давайте сначала рассмотрим, как можно извлечь некоторые основные данные. Если мы перейдем на rapidapi.com, а затем нажмем на категорию слева, мы попадем на список API. Это список, который мы будем анализировать.

Если вы используете Chrome, вы можете щелкнуть правой кнопкой мыши на элементе и выбрать "Инспектировать". Это открывает инструменты разработчика и позволяет легко увидеть CSS-селекторы, которые можно использовать для извлечения нужных данных. В нашем случае, используя класс ApiItemstyled__ResponsiveWrapper-qvgmn9-8, мы получаем все API, отображаемые в представлении категории. Используя класс ApiItemstyled__ApiItemWrapperName-qvgmn9-2, мы можем извлечь название API. Описание можно получить, используя класс ApiItemstyled__Description-qvgmn9-4, а с помощью класса ApiItemstyled__Footer-qvgmn9-5 мы можем получить статистику внизу, например, популярность. Давайте напишем некоторый код:

Просмотрите полный код по ссылке [https://rapidapi.com/blog/web-scraping-ruby/](https://rapidapi.com/blog/web-scraping-ruby/)

Убедитесь, что вы используете инструменты разработчика в вашем браузере, чтобы вы могли следовать за тем, как мы используем CSS-селекторы здесь. В первом выделенном блоке мы делаем запрос к ScrapingBee через RapidAPI. Мы указываем render_js равным true, чтобы наш экземпляр безголового Chrome рендерил Javascript на веб-сайтах. Затем мы анализируем ответ с помощью Nokogiri. Наконец, мы получаем все элементы API на странице, перебираем их и извлекаем из них название, описание и статистику. Особое внимание следует обратить на то, как мы получаем статистику. (Очень упрощенный) HTML выглядит так:

<div class="ApiItemstyled__Footer-qvgmn9-5 kTwcIW">
  <div>
    <div class="FlexLayouts__FlexRowCenterCenter-sc-1j19v2f-8 jlDTbN">
      <img src="/static-assets/default/popularity.svg">
      <div class="ApiItemstyled__FooterItem-qvgmn9-6 dzVIkT">9.9</div>
    </div>
  </div>
  <div>
    <div class="FlexLayouts__FlexRowCenterCenter-sc-1j19v2f-8 jlDTbN">
      <img src="/static-assets/default/latency.svg">
      <div class="ApiItemstyled__FooterItem-qvgmn9-6 dzVIkT">170ms</div>
    </div>
  </div>
  <div>
    <div class="FlexLayouts__FlexRowCenterCenter-sc-1j19v2f-8 jlDTbN">
      <img src="/static-assets/default/success-new.svg">
      <div class="ApiItemstyled__FooterItem-qvgmn9-6 dzVIkT">83%</div>
    </div>
  </div>
</div>

Итак, мы получаем все третьи div-элементы из элемента с классом ApiItemstyled__Footer-qvgmn9-5. Затем мы преобразуем этот массив результатов и получаем текст из каждого элемента. Теперь, после выполнения нашего скрипта, мы получаем массив, содержащий что-то вроде этого:

[{:title=>"City Geo-Location Lookup",
  :description=>
   "This API gives you Latitude, Longitude, Time-Zone of any city",
  :popularity=>"9.8",
  :latency=>"1492ms",
  :success=>"94%"},
 {:title=>"Get Video and Audio URL",
  :description=>
   "Get direct links to download video or audio files from almost any hosting website, like Youtube, Twitch, SoundCloud, etc with download api.",
  :popularity=>"9.8",
  :latency=>"6977ms",
  :success=>"99%"},
 {:title=>"Currency Exchange",
  :description=>
   "Live currency and foreign exchange rates by specifying source and destination quotes and optionally amount to calculate. Support vast amount of quotes around the world.",
  :popularity=>"9.8",
  :latency=>"1347ms",
  :success=>"96%"},
 # и так далее...
]

Выводы и советы

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

Связанные ресурсы


Оригинальная публикация на https://rapidapi.com от 16 декабря 2019 года.