перейти к содержанию

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

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

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

Что такое скрытые веб-данные?

Скрытые веб-данные — это любые данные, которые не видны напрямую в необработанном HTML-источнике страницы. Это включает в себя:

  • Данные загружаются динамически через JavaScript после загрузки страницы. Например, рендеринг содержимого <div> тег, вставив динамически создаваемые элементы HTML.

  • Данные, хранящиеся в переменных JavaScript и объектах, встроенных в <script> теги. Часто объекты JSON содержат целые наборы данных.

  • HTML-контент, создаваемый действиями пользователя посредством запросов AJAX. Например, расширение ветки комментариев или бесконечная прокрутка страниц.

  • Данные и метаданные внутреннего запроса API, используемые интерфейсом для работы. Например, токены CSRF, информация о пользователе, временные кеши.

  • Запутанные и зашифрованные данные с целью предотвратить доступ к ним парсеров.

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

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

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

Поиск скрытых данных в HTML

Первым шагом является подтверждение того, действительно ли нужные нам данные скрыты где-то в JavaScript страницы.

Вот простой способ проверки:

  • Загрузите целевую страницу в браузер и найдите уникальный идентификатор данных, который мы хотим очистить. Например, название или идентификатор продукта.

  • Отключите JavaScript в браузере и перезагрузите страницу. Это можно сделать в инструментах разработчика.

  • Проверьте, присутствует ли уникальный идентификатор в необработанном исходном коде HTML.

Если данные исчезли — скорее всего, они динамически отображаются с помощью JavaScript при загрузке страницы.

Теперь нам нужно покопаться в исходном коде HTML, чтобы выяснить, где и как он генерирует этот контент.

Извлечение данных из тегов

Одно из наиболее распространенных мест хранения скрытых данных — внутри. <script> теги.

Это могут быть объекты JSON, переменные JavaScript, целые наборы данных или код, управляющий страницей.

Например:

<html>
<body>

  <div id="product"></div>

  <script>
    // product data as javascript object 
    var data = {
      "product": {
        "name": "Super Product",
        "price": 99.99
      }
    }

    // data rendered on page load
    document.getElementById("product").innerHTML = data.product.name + ": £" + data.product.price;

  </script>

</body>  
</html>

Здесь фактические данные о продукте хранятся в переменной объекта JavaScript, называемой data.

Продукт <div> изначально пуст и заполняется динамически при загрузке страницы.

Итак, чтобы извлечь эти данные, нам сначала нужно найти соответствующие <script> тег в необработанном HTML. Это можно сделать с помощью любой библиотеки синтаксического анализа HTML, такой как BeautifulSoup или Parsel:

# extract scripts from HTML with BeautifulSoup
from bs4 import BeautifulSoup

html = # page HTML 
soup = BeautifulSoup(html, ‘html.parser‘)

scripts = soup.find_all(‘script‘)

Далее нам нужно извлечь данные конкретно из содержимого скрипта.

Способ 1: загрузить как JSON

Если данные являются допустимым объектом JSON, мы можем просто загрузить их напрямую с помощью Python. json модуль:

import json

# find script with data variable 
script = soup.find(‘script‘, text=lambda t: ‘data =‘ in t)

# load json directly
data = json.loads(script.string)

print(data[‘product‘])
# {‘name‘: ‘Super Product‘, ‘price‘: 99.99}

Это отлично работает, если тег сценария указывает type="application/json".

Метод 2: Сопоставление регулярных выражений

Для более сложных данных нам придется самостоятельно анализировать необработанный код JavaScript. Здесь на помощь приходят регулярные выражения.

Мы можем сканировать код и извлекать части, соответствующие шаблону, например, наш объект данных.

import re
import json

script = soup.find(‘script‘, text=lambda t: ‘data =‘ in t)

# match the data object by surrounding syntax 
match = re.search(r‘data = ({.+})‘, script.string)

# load matched json 
data = json.loads(match.group(1))

print(data[‘product‘])  
# {‘name‘: ‘Super Product‘, ‘price‘: 99.99}

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

Способ 3: анализ JavaScript

Для расширенного парсинга нам может потребоваться проанализировать весь код JavaScript, включая переменные, функции и объекты.

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

Мы можем использовать такие библиотеки, как PyJavascript и Js2Py интерпретировать JavaScript в Python.

Например, с PyJavascript:

import javascript

script = soup.find(‘script‘, text=lambda t: ‘data =‘ in t)

# init JavaScript interpreter 
js = javascript.Interpreter()

# run script to define data variable
js.execute(script.string)

# access parsed data object
print(js.context[‘data‘][‘product‘])
# {‘name‘: ‘Super Product‘, ‘price‘: 99.99} 

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

Очистка данных API из JavaScript

API обеспечивают большую часть динамического поведения на современных веб-сайтах. JavaScript отправляет запросы на загрузку данных, отправку форм или инициирование взаимодействия.

Копаясь в коде страницы, мы можем найти эти конечные точки API и имитировать запросы на извлечение данных.

Например, вот простой скрипт, который загружает данные о продукте из /api/products/123 конечная точка:

async function loadProduct(){

  let response = await fetch(‘/api/products/123‘);

  let product = await response.json();

  // render product data to page
  document.getElementById("product").innerHTML = product.name;

}

loadProduct();

Найдя этот скрипт в HTML, мы сможем:

  • Извлеките URL-адрес API из fetch() призывают

  • Анализируйте форматы запросов и ответов AJAX.

  • Реплицируйте запрос API непосредственно в Python с помощью таких библиотек, как Requests.

Это позволяет извлекать данные из API, на которые опирается JavaScript, без выполнения какого-либо кода браузера.

Поиск данных в переменных JavaScript

Данные страницы также обычно хранятся непосредственно в переменных JavaScript.

Например:

// javascript data
var products = [
  {name: "Product 1", price: 19.99}, 
  {name: "Product 2", price: 24.99}
];

function renderProducts(){
  // loop through products and render HTML
} 

Здесь полный список продуктов хранится в переменной с именем products.

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

import re
import json

# find products variable
script = soup.find(‘script‘, text=lambda t: ‘var products =‘ in t)

# match products json
match = re.search(r‘var products = ({.+});‘, script.string)  
data = json.loads(match.group(1))

print(data)
# [{name: "Product 1", price: 19.99}, {name: "Product 2", price: 24.99}]

Если структура данных более сложная, мы можем проанализировать всю среду JavaScript для доступа к любым переменным в области видимости.

Парсинг контента, загруженного через AJAX

Веб-сайты часто загружают контент динамически через AJAX после загрузки страницы.

Например, расширение веток комментариев, бесконечная прокрутка страниц или вкладки.

Это содержимое отсутствует в исходном HTML-коде, но запрашивается с сервера по мере необходимости.

Мы можем очистить эти фрагменты AJAX следующим образом:

  • Мониторинг сетевых запросов на странице для определения URL-адресов AJAX.

  • Реконструкция запросов AJAX и их отправка непосредственно из кода Python.

  • Анализ ответов AJAX, содержащих данные HTML/JSON.

Например, рассмотрим этот скрипт, который загружает данные с разбивкой на страницы при прокрутке:

// initially loaded page data
var results = [ /* initial page of data */]; 

// paginate on scroll
window.addEventListener(‘scroll‘, function() {

  var page = results.length / 20 + 1;

  // request next page
  fetch(‘/data?page=‘ + page)
    .then(res => res.json())
    .then(data => {
      results.push(...data);

      // render new data
    });

});

Здесь мы видим, что он запрашивает страницы из /data конечная точка и добавление контента в results Переменная.

Мы можем реплицировать эти запросы и напрямую очищать данные, избегая необходимости анализа полного отображаемого HTML.

Выполнение JavaScript с помощью безголовых браузеров

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

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

Вот пример с драматургом на Python:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:

  browser = p.chromium.launch()
  page = browser.new_page()

  page.goto(‘https://targetpage.com‘)

  # evaluate browser context to get data
  data = page.evaluate(‘window.products‘) 

  browser.close()

print(data)

Ключ использует page.evaluate() для запуска пользовательского кода JavaScript в контексте загруженной страницы.

Это дает нам полный доступ к очистке любых скрытых в противном случае данных.

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

Запутанные и зашифрованные данные

Веб-сайты часто намеренно запутывают свой JavaScript, чтобы предотвратить парсинг.

Некоторые примеры включают в себя:

  • Уменьшение имен переменных и функций до бессмысленных символов, таких как a, b, fn1()

  • Разделение наборов данных на несколько переменных и сценариев

  • Шифрование/кодирование данных, чтобы они были недоступны для чтения человеком.

  • Динамическая сборка данных во время выполнения из фрагментированных частей.

  • Методы защиты кода, такие как упаковка, обфускация, защита от отладки, выполнение виртуальной машины.

Это может сильно затруднить анализ JavaScript. Небольшие изменения кода могут легко сломать наши парсеры.

Есть несколько способов обработки сильно запутанных страниц:

  • Используйте безголовые браузеры, такие как Playwright или Puppeteer, для загрузки исполняемого кода, а не непосредственно анализируйте запутанный источник.

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

  • Проанализируйте, как реальные пользователи взаимодействуют со страницей, чтобы определить источники данных.

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

  • Для шифрования попробуйте найти ключи шифрования или перепроектировать алгоритмы дешифрования.

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

Очистка скрытых API с помощью прокси

Скрытые веб-API часто используют передовые методы защиты от парсинга, такие как ограничение скорости IP-адреса, проверка капчи и обнаружение ботов для предотвращения доступа.

Именно здесь прокси-серверы очень пригодятся для парсинга. Направляя запросы через резидентные IP-адреса, мы можем обойти многие средства защиты и получить доступ к API в большом масштабе.

Несколько советов по парсингу с помощью прокси:

  • Используйте регулярную ротацию прокси, чтобы предотвратить блокировку на определенных IP-адресах.

  • Включите ротацию прокси в зависимости от региона или интернет-провайдера для широкого разнообразия.

  • Используйте прокси-серверы обратного подключения, которые предоставляют тысячи уникальных IP-адресов для циклического переключения.

  • Ограничьте частоту запросов для каждого прокси-сервера, чтобы имитировать поведение реального пользователя.

  • Используйте прокси-авторизацию, чтобы выдавать себя за реальные устройства, а не только за анонимные IP-адреса.

  • Отслеживайте и обрабатывайте распространенные блокировки, такие как капчи, блокирующие страницы, 429.

При правильной настройке прокси мы можем получить доступ практически к любому целевому сайту или скрытому API.

Службы очистки скрытых данных

Существуют также управляемые службы парсинга, специально созданные для извлечения данных, отображаемых JavaScript.

Они обеспечивают автоматизацию браузера, управление прокси-серверами и возможности выполнения JavaScript.

Некоторые примеры включают в себя:

Соскоб – API браузера и прокси, который может оценивать JS на страницах.

СкребокAPI – Безголовый API-интерфейс браузера с автоматической ротацией прокси.

апифай – Среда выполнения актеров для масштабной автоматизации браузера.

ScrapeOps – Визуальный конструктор автоматизации браузера с извлечением JS.

ScrapFly – Неблокируемый API-интерфейс парсинга с миллионами резидентных прокси с обратным подключением.

Эти сервисы справляются со всеми сложностями динамического рендеринга страниц и упрощают сбор скрытых данных.

Основные выводы

Вот ключевые моменты для очистки скрытых данных веб-сайта:

  • Проверьте страницы без JavaScript, чтобы убедиться, что данные загружаются динамически.

  • Извлечение и анализ скриптов, переменных и объектов JSON из HTML.

  • Анализируйте и реплицируйте запросы AJAX для доступа к скрытым API.

  • Используйте прокси и автономные браузеры, когда это необходимо для тяжелых JS-сайтов.

  • Сопоставление с образцом и реверс-инжиниринг запутанного кода

  • Адаптируйте парсеры для защиты от ботов

При использовании правильных методов можно извлечь практически любые данные общедоступного веб-сайта. Нам просто нужно знать, где искать!

Теги:

Присоединяйтесь к беседе

Ваш электронный адрес не будет опубликован. Обязательные поля помечены * *