Веб-скрапинг - это термин, обычно используемый для использования автоматизированного метода извлечения данных с веб-сайта. В случаях, когда веб-сайт не предоставляет 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">Free</h4>
    </div>
    <div class="card-body">
      <h1 class="card-title pricing-card-title">$0 <small class="text-muted">/ mo</small></h1>
      <ul class="list-unstyled mt-3 mb-4">
        <li>10 users included</li>
        <li>2 GB of storage</li>
        <li>Email support</li>
        <li>Help center access</li>
      </ul>
      <button type="button" class="btn btn-lg btn-block btn-outline-primary">Sign up for free</button>
    </div>
  </div>
  <div class="card mb-4 shadow-sm">
    <div class="card-header">
      <h4 class="my-0 font-weight-normal">Pro</h4>
    </div>
    <div class="card-body">
      <h1 class="card-title pricing-card-title">$15 <small class="text-muted">/ mo</small></h1>
      <ul class="list-unstyled mt-3 mb-4">
        <li>20 users included</li>
        <li>10 GB of storage</li>
        <li>Priority email support</li>
        <li>Help center access</li>
      </ul>
      <button type="button" class="btn btn-lg btn-block btn-primary">Get started</button>
    </div>
  </div>
</div>

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

require 'nokogiri'

# The whole document
html = '<div class="card-deck mb-...'

# create a Nokogiri document
doc = Nokogiri::HTML(html)

# Get all the plan names
doc.css('.card').map { |card| card.css('.card-header h4').text }
# => ["Free", "Pro"]

# Get structured data
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=>"Free",
#      :price=>"$0 / mo",
#      :features=>
#      ["10 users included",
#        "2 GB of storage",
#        "Email support",
#        "Help center access"]},
#    {:title=>"Pro",
#      :price=>"$15 / mo",
#      :features=>
#      ["20 users included",
#        "10 GB of storage",
#        "Priority email support",
#        "Help center access"]}]

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

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

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

<html lang="en">
<head>
  <title>Single Page Application</title>
</head>
<body>
  <div id="app"></div>
  <script src="https://example.org/some-framework.js"></script>
  <script>
    MyFramework.start(document.getElementById('app'));
  </script>
</body>
</html>

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

Безголовый Chrome и прокси

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

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

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

Давай поскребем что-нибудь!

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

  • "Рубин",
  • Жемчужина нокогири,
  • Исключительный драгоценный камень,
  • Учетная запись RapidAPI

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

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

Настроить ScrapingBee

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

После этого перейдите на вкладку Конечные точки и возьмите свой хост и ключ 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 мы можем получить статистику внизу, например, популярность. Напишем код:

View full code at 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%"},
 # and more...
]

Выводы и подсказки

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

  1. Протестируйте селекторы CSS в браузере: используя что-то вроде document.querySelectorAll(), вы можете протестировать свои селекторы CSS и убедиться, что они действительно выбирают то, что вам нужно, а не больше.
  2. Некоторым веб-сайтам может потребоваться какое-то взаимодействие для загрузки всего. Например, вам нужно прокрутить, чтобы запустить загрузчик с бесконечной прокруткой. В этом случае можно отправить код Javascript, который может быть выполнен в автономном экземпляре Chrome. Просто убедитесь, что он закодирован в Base64.
  3. Если вам нужно, чтобы ScrapingBee подождал несколько секунд перед отправкой вам содержимого веб-сайта (например, веб-сайту требуется больше времени, чем обычно для рендеринга), вы можете отправить дополнительный параметр с именем wait, в котором вы можете указать до 10 000 миллисекунд время ожидания.

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

Первоначально опубликовано на https://rapidapi.com 16 декабря 2019 г.