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

Помните о важности соскабливания с уважением.

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

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

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

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

  1. Визуальный осмотр: выясните, что извлечь
  2. Сделайте HTTP-запрос к веб-странице
  3. Разобрать ответ HTTP
  4. Сохранять / использовать соответствующие данные

Первый шаг включает использование встроенных инструментов браузера (таких как Chrome DevTools и Firefox Developer Tools) для поиска необходимой нам информации на веб-странице и определения структур / шаблонов для ее программного извлечения.

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

Например, предположим, что мы хотим извлечь количество подписчиков PewDiePie и сравнить его с T-серией. Простой поиск в Google приводит меня на страницу подсчета подписчиков на Youtube в режиме реального времени от Socialblade.

При визуальном осмотре мы обнаруживаем, что количество подписчиков находится внутри тега <p> с идентификатором rawCount.

Давайте напишем простую функцию Python, чтобы получить это значение. Мы будем использовать BeautifulSoup для разбора HTML.

import requests from bs4 import BeautifulSoup html = requests.get(url).content soup = BeautifulSoup(html) return soup.select('#rawCount')[0].text

Теперь посмотрим на счетчики:

get_subscribers('https://socialblade.com/youtube/user/pewdiepie/realtime') '80520035' get_subscribers('https://socialblade.com/youtube/user/tseries/realtime') '79965479'

Кажется, это простой процесс, правда? Что может пойти не так?

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

Давайте перечислим эти сложности одну за другой и посмотрим, как их решить в следующем разделе.

Сложности парсинга веб-сайтов

Асинхронная загрузка и рендеринг на стороне клиента

То, что вы видите, не то, что вы получаете.

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

Это происходит потому, что информация, которую мы на самом деле ищем, либо отображается на стороне браузера библиотеками, такими как Handlebars или React, либо извлекается путем выполнения будущих вызовов AJAX на сервер, а затем отображается браузером.

Вот несколько примеров:

  • Веб-страницы с бесконечной прокруткой (Twitter, Facebook и т. Д.)
  • Веб-страницы с предзагрузчиками, такими как процентные столбцы или загрузочные счетчики

Аутентификация

Многие веб-сайты имеют своего рода аутентификацию, о которой нам придется позаботиться в нашей программе парсинга. Для более простых веб-сайтов аутентификация может быть такой же простой, как запрос POST с именем пользователя и паролем или сохранение файла cookie. Однако могут быть и такие тонкости, как:

  • Скрытые значения: наряду с именем пользователя и паролем вам может потребоваться добавить другие поля в полезные данные POST (обычно CSRF_TOKEN, но также могут быть некоторые странные данные).
  • Установка заголовков: могут быть определенные заголовки, которые нам нужно установить (referer, authorization и т. д.)

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

Черный список на стороне сервера

Как мы упоминали ранее, ответ на вопрос «Что может пойти не так при очистке?» также зависит от намерений владельцев веб-сайтов.

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

Простые идеи для такого анализа включают:

Анализируя скорость запросов

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

Еще один красный флаг - это повторение (клиент делает X запросов каждые Y секунд). Серверы могут измерять такие показатели и определять пороговые значения, превышающие которые они могут занести клиента в черный список. Механизмы могут быть намного сложнее, но идею вы поняли. Блокировка клиента обычно носит временный характер (в пользу бесплатного и открытого Интернета для всех), но в некоторых случаях может быть даже навсегда.

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

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

Приманки

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

Обнаружение паттернов

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

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

Редиректы и капчи

Некоторые сайты просто перенаправляют свои старые сопоставления ссылок на новые (например, перенаправляют HTTP ссылки на https), возвращая код ответа 3xx.

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

Такие компании, как Cloudflare, которые предоставляют услуги защиты от ботов или DDoS-атак, еще больше усложняют ботам доступ к реальному контенту.

Структурные сложности

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

Неструктурированный HTML

Это когда сервер отправляет HTML, но не всегда предоставляет шаблон. Например, классы и атрибуты CSS динамически генерируются на стороне сервера и каждый раз уникальны. Иногда неструктурированный HTML также является следствием плохого программирования.

iframe теги

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

Хорошо! Мы перечислили сложности; теперь пришло время найти для них обходные пути.

Устранение сложностей парсинга веб-страниц с помощью Python

Выбор правильных инструментов, библиотек и фреймворков

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

Для парсинга веб-страниц в Python доступно множество инструментов. Мы рассмотрим несколько популярных (и проверенных самостоятельно) вариантов, и когда их использовать. Для быстрой очистки простых веб-сайтов я обнаружил, что комбинация Python Requests (для обработки сеансов и выполнения HTTP-запросов) и Beautiful Soup (для анализа ответа и навигации по нему для извлечения информации) является идеальной парой.

Для больших проектов по парсингу (где мне нужно собирать и обрабатывать много данных и иметь дело со сложностями, не связанными с JS), Scrapy был весьма полезен.

Scrapy - это фреймворк (а не библиотека), который абстрагирует множество тонкостей для эффективного парсинга (одновременные запросы, использование памяти и т. Д.), А также позволяет подключать кучу промежуточного программного обеспечения (для файлов cookie, перенаправления, сеансов, кеширования и т. Д.) .) решать разные сложности. Scrapy также предоставляет оболочку, которая может помочь в быстром создании прототипа и проверке вашего подхода к очистке (селекторы, ответы и т. Д.). Эта структура достаточно зрелая, расширяемая и пользуется хорошей поддержкой сообщества.

Для сайтов с большим количеством JavaScript (или сайтов, которые кажутся слишком сложными) обычно подходит Selenium. Хотя очистка с помощью Selenium не так эффективна по сравнению с Scrapy или Beautiful Soup, она почти всегда дает вам желаемые данные (что единственное, что в большинстве случаев имеет значение).

Обработка аутентификации

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

Мы также можем проверить, какие заголовки отправляются на сервер с помощью инструментов браузера, чтобы мы также могли воспроизвести это поведение в коде, например, если аутентификация зависит от таких заголовков, как Authorization и Authentication). Если сайт использует простую аутентификацию на основе файлов cookie (что в наши дни маловероятно), мы также можем скопировать содержимое файла cookie и добавить его в код вашего парсера (опять же, мы можем использовать для этого встроенные инструменты браузера).

Обработка асинхронной загрузки

Обнаружение асинхронной загрузки

Мы можем обнаружить асинхронную загрузку на самом этапе визуальной проверки, просмотрев источник страницы (параметр «Просмотреть источник» в браузере при щелчке правой кнопкой мыши), а затем выполнив поиск нужного контента. Если вы не нашли текст в источнике, но по-прежнему видите его в браузере, то, вероятно, он отображается с помощью JavaScript. Дальнейшая проверка может быть проведена с помощью сетевого инструмента браузера, чтобы проверить, есть ли какие-либо запросы XHR, сделанные сайтом.

Решение проблемы асинхронной загрузки

Использование веб-драйвера

Веб-драйвер похож на симуляцию браузера с интерфейсом, которым можно управлять с помощью скриптов. Он может выполнять функции браузера, такие как рендеринг JavaScript, управление файлами cookie и сеансами и т. Д. Selenium Web Driver - это среда веб-автоматизации, разработанная для тестирования UI / UX веб-сайтов, но он также стал популярным вариантом для очистки динамически отображаемых сайтов с течением времени.

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

Selenium поддерживает несколько языков для написания сценариев, включая Python. Обычно он запускает экземпляр браузера, и мы можем видеть такие вещи, как нажатие и ввод данных на экране, что полезно во время тестирования. Но если нас интересует только парсинг, мы можем использовать «безголовые браузеры», у которых нет пользовательского интерфейса и которые работают быстрее с точки зрения производительности.

Chrome Headless - популярный выбор для веб-драйвера без головы, другие варианты включают Headless Firefox, PhantomJS, spynner и HtmlUnit. Для некоторых из них может потребоваться установка xvfb и его оболочки Python (xvfbwrapper или pyvirtualdisplay) для имитации отображения экрана в виртуальной памяти без реального вывода на экран.

Проверка вызовов AJAX

Этот метод основан на идее «Если он отображается в браузере, он должен откуда-то поступать». Мы можем использовать инструменты разработчика браузера для проверки вызовов AJAX и попытаться выяснить, какие запросы отвечают за получение данных, которые мы ищем. Возможно, нам потребуется установить заголовок X-Requested-With для имитации запросов AJAX в вашем скрипте.

Используйте бесконечную прокрутку

Мы можем справиться с бесконечной прокруткой, внедрив некоторую логику javascript в селен (см. Этот поток SO). Кроме того, обычно бесконечная прокрутка включает в себя дальнейшие вызовы AJAX на сервер, которые мы можем проверить с помощью инструментов браузера и воспроизвести в нашей программе очистки.

Поиск подходящих селекторов

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

Селекторы CSS часто используются при парсинге. Однако другой метод выбора элементов, называемый XPath (язык запросов для выбора узлов в XML-документах), может быть полезен в определенных сценариях. Он предоставляет более универсальные возможности, например:

  • Выбор элементов на основе их содержания. Это не рекомендуемая практика, но она удобна для плохо структурированных страниц.
  • Искать в любом направлении. Мы можем создавать запросы, которые ищут бабушек и дедушек, а затем искать их ребенка с определенными атрибутами / текстом. Это невозможно с селекторами CSS.

Некоторые люди утверждают, что XPath медленнее, чем селекторы CSS, но по моему личному опыту, оба работают одинаково хорошо. Хотя иногда один быстрее другого, разница в миллисекундах. Кроме того, при парсинге не очень сложных и хорошо структурированных веб-страниц я просто использую инструмент выбора Chrome / Firefox, чтобы получить XPath целевого элемента, вставить его в свой скрипт, и все готово в течение нескольких секунд. Сказав это, есть несколько проверок, которые могут пригодиться при выборе селекторов:

Нажав Ctrl + F в инспекторе DOM, мы можем использовать выражение CSS (или XPath) в качестве поискового запроса. Браузер будет циклически повторяться, и мы сможем увидеть все совпадения. Это быстрый способ проверить, работает ли выражение.

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

Борьба с занесением в черный список на стороне сервера

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

  1. Использование прокси-серверов и ротации IP-адресов. Для сервера это будет выглядеть так, как будто сайт просматривают несколько пользователей. Есть несколько сайтов, где вы можете найти список бесплатных прокси для использования (например, this). И requests, и scrapy имеют функции для использования вращающихся прокси. При использовании прокси следует помнить о следующих вещах:
  • Бесплатные прокси-адреса обычно временные; через некоторое время они начнут выдавать ошибки подключения. Так что лучше предоставлять прокси динамически. Мы можем либо очистить список активных прокси (да, очистить для дальнейшей очистки) с сайтов со списком прокси, либо использовать какой-то API (несколько прокси-сервисов премиум-класса имеют эту функцию).
  • Некоторые прокси устанавливают и отправляют заголовок HTTP_X_FORWARDED_FOR или HTTP_VIA (или оба), который сервер может использовать для определения того, что мы используем прокси (и даже реальный IP-адрес). Поэтому рекомендуется использовать элитные прокси (прокси, которые отправляют оба этих поля заголовка как пустые).

2. Подмена и ротация пользовательского агента. Идея состоит в том, чтобы передать поле заголовка другого пользовательского агента (или нескольких разных пользовательских агентов по очереди), чтобы обмануть сервер. Список различных возможных User-агентов доступен здесь. Подмена пользовательского агента может не всегда работать, потому что веб-сайты могут предлагать клиентские JS-методы, чтобы определить, является ли агент тем, о чем он заявляет. Мы также должны иметь в виду, что ротация пользовательских агентов без ротации IP-адресов в тандеме может сигнализировать серверу красный флаг.

3. Уменьшение скорости сканирования за счет добавления случайного времени ожидания между действиями (например, отправкой запросов, вводом данных, щелчком по элементам и т. Д.). Это приведет к рандомизации шаблона просмотра и усложнит серверу различение между нашим парсером и реальным пользователем.

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

Обработка редиректов и каптч

Современные библиотеки, такие как requests, уже заботятся о перенаправлениях HTTP, отслеживая их (сохраняя историю) и возвращая последнюю страницу. Scrapy также имеет промежуточное программное обеспечение перенаправления для обработки перенаправлений. Переадресация не представляет большой проблемы, если мы в конечном итоге перенаправляемся на страницу, которую ищем. Но если нас перенаправляют на капчу, это становится непросто.

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

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

Обработка iframe тегов и неструктурированных ответов

Для тегов iframe достаточно просто запросить правильный URL-адрес, чтобы получить обратно те данные, которые вам нужны. Мы должны запросить внешнюю страницу, затем найти iframe, а затем сделать еще один HTTP-запрос к атрибуту src iframe. Кроме того, мы ничего не можем поделать с неструктурированными HTML или URL-шаблонами, кроме как придумывать хаки (придумывать сложные запросы XPath, использовать регулярные выражения и т. Д.).

Другие полезные инструменты и библиотеки для парсинга

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

  • Газета: Newspaper3k - библиотека для парсинга статей. Он поддерживает несколько языков, предоставляет API для получения метаинформации, такой как сведения об авторе и дате публикации, а также функции NLP, такие как извлечение резюме и ключевых слов, извлечение авторов.
  • PyAutoGUI: PyAutoGUI - это модуль автоматизации графического интерфейса пользователя, который позволяет программно управлять клавиатурой и мышью. Приятная функция PyAutoGUI - поиск изображения на экране. Я наблюдал, как несколько человек использовали PyAutoGUI для навигации по сайту.
  • Расширение браузера EditThisCookie очень полезно, когда вам нужно поиграться с файлами cookie и их содержимым.
  • Cloudflare-scrape: я использовал этот модуль в прошлом, чтобы обойти антиботы Cloudflare. В экосистеме парсинга в Python хорошо то, что есть множество функций, которые вы найдете с открытым исходным кодом или в виде фрагментов на Stack Overflow.
  • Tcpdump: вы можете использовать tcpdump для сравнения заголовков двух запросов (тот, который отправляет ваш парсер, а другой, который отправляет ваш браузер во время просмотра сайта).
  • Burp Suite: Burp Suite полезен для перехвата запросов, которые браузер делает на сайте, и их анализа.
  • Stem: На всякий случай, если вы хотите делать запросы с использованием python поверх TOR.
  • Сервисы визуального парсинга, такие как Octoparse, Portia (с открытым исходным кодом и созданная командой scrapy), ParseHub, Dext и FMiner.
  • Расширения браузера, такие как Web Scraper, Data Scraper и Agenty (для Chrome).

Соскабливание с уважением

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

Как упоминалось в начале, очистка похожа на игру в кошки-мышки, действующую в серой зоне закона, и может вызвать проблемы для обеих сторон, если не будет сделано с уважением. Нарушение авторских прав и злоупотребление информацией может повлечь за собой юридические последствия. Пара примеров, вызвавших разногласия, - это публикация исследователями данных OK Cupid и HIQ-лаборатории, использующие данные Linkedin для HR-продуктов.

Стандарт исключения роботов был разработан, чтобы передать намерение владельцев сайтов проиндексировать / сканировать. В идеале наш парсер должен подчиняться инструкциям в файле robots.txt. Даже если robots.txt разрешает очистку, выполнение этой агрессивной операции может привести к перегрузке сервера, вызывая проблемы с производительностью или нехватку ресурсов на стороне сервера (даже сбои).

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

Пропустили ли мы какие-нибудь советы по парсингу для разработчиков Python? Если да, дайте нам знать в разделе комментариев ниже!

Изучите Python, создавая проекты с DevProjects

Первоначально опубликовано на https://www.codementor.io.