СоХабр закрыт.

С 13.05.2019 изменения постов больше не отслеживаются, и новые посты не сохраняются.

H Пытаемся защитить сайт от краулеров (парсеров) в черновиках

Привет всем,

На Хабре было множество статей про Парсинг сайтов, или правильнее было бы сказать Краулинг (для придир — Краулинг + Парсинг).
Тема эта волнительна для любой e-Commerce площадки: велик соблазн взять уже готовый контент у конкурентов, сэкономив время и средства. Но пройдя путь наполнения значительных по размеру каталогов, каждый осознает доступность информации, и захочет обзавестись защитой. Ведь средства в ведение каталогов вкладываются значительные.

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



Учитывая открытость WWW — очевидно, 100%-ной защиты не получится. Сайты должны быть легко доступны для людей (и для полезных роботов тоже), а поведение скрипта может быть настолько хитроумным, что отличить от человека его будет непросто. Однако защита по крайне мере способна 1) создать трудности, 2) значительно — в сотни раз — снизить производительность краулеров. Расчет может быть и на то, что разработчики краулеров не захотят разгадывать хитрости защиты и пойдут искать более легкую добычу, отстанут таким образом от вашего сайта.


Не брезгуем Watermark-ами


В основном краулеры создаются с целью скопировать каталоги товаров. В этом случае установка «водяных знаков» на полноразмерные изображения продуктов — уже серьезная защита. На Хабре, впрочем, были уже статьи на тему снятия водяных знаков. По крайне мере, убрать простой текстовый watermark в пакетном режиме — не проблема.
Используйте watermark на основе полупрозрачной картинки, желательно с тонким полностью непрозрачным контуром.

Программная защита


Не нужно защищать все подряд разделы, нужно сосредоточиться на защите наиболее важного контента (непосредственно, каталога товаров).
Ведь хорошая защита потребует логики, а соответственно — жертв в виде сколько-то миллисекунд на отдачу страницы сервером. К тому же есть риски спутать реального человека с краулером — такая ситуация должна быть разрешена по возможности максимально деликатно (задать какой-то вопрос, запросить ввод captcha).

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

Особое внимание следует уделить краулерам поисковых систем — аккуратно в коде защиты прописать все известные данные о поисковых ботах, индексация которых важна для продвижения сайта в Сети. Обычно такие боты имеют определенную сигнатуру user-agent в http-заголовке — например Googlebot.
Чтобы не рисковать индексацией сайта, скорей всего придется дать максимально широкую дорогу всем запросам с определенными сигнатурами, ну и заодно оставить широчайших размеров дыру для остальных краулеров. Краулеру останется только правильно представиться.
Впрочем, можно еще в отдельных ветках кода попытаться проверить поведение бота — обычно нормальные поисковые системы достаточно аккуратны с запросами индексирования, стремясь не создавать чрезмерную нагрузку на сайт.

Также по причине необходимости холить и лелеять эти уважаемые поисковые боты мы вынуждены отказаться от нескольких элегантных методов защиты, особенно «PHP-код в картинке» (см An image to download).

IP-адрес клиента — единственная объективная информация, которая доступна о клиенте на сервере. Возможности анализа по IP также очень ограничены — никогда не знаем, какая внутренняя сеть может скрываться за IP — может быть один домашний компьютер, а может быть офис компании со штатом 1000 человек. Блокировка IP также опасна — IP может быть динамически выделенным, и в последующем назначен другому случайному пользователю.
Важно определиться, что мы делаем в случае обнаружения нежелательного краулера по определенному IP-адресу. Мы якобы блокируем IP — это значит что при следующем обращении к защищенным ресурсам (страницам каталога) пользователь с таким IP должен будет пройти проверку — ввести captcha. Если пользователь вводит captcha (можно еще и ограничение по времени поставить) — IP-адрес «разблокируется», то есть
update ips set locked = false, verified = now() where ip = (ip); 

Пр этом сохраняется момент времени проверки в поле verified, и последующие 3 часа система не будет анализировать данный IP, и соответственно — не будет блокировать.

Тут мы оставляем еще одну зияющую дыру — зная это логику, запустил краулер, дождался запроса проверки, прошел верификацию вручную — и имеешь 3 часа полной свободы. Расчет может быть только на то, что нервы на той стороне сдадут раньше — ведь у краулера много других проблем для отладки — например вариации структуры html от страницы к странице… а тут еще надо серверную логику угадать.

В БД системы потребуется простая таблица:
create table ips (
  ip int unsigned,
  created timestamp default now(),
  updated timestamp, --заполняется в триггере BEFORE UPDATE, NEW.updated = now();
  locked boolean, --IP "заблокирован"
  verified timestamp, -- IP последний раз проверен заданный в момент времени
  primary key(ip)
);

Раз в сутки таблицу можно очищать от левых записей:
delete from ips where updated < yesterday() and locked = false;

Цель метода — выявление признаков поведения краулера путем анализа активности.
Краулер постарается каким-либо способом составить список интересующих его ресурсов, затем по очереди загружать страницу каждого ресурса(товара).

Для фиксации доступа к интересующим ресурсам создадим еще одну таблицу:
create table ipstats (
  ipstats_id bigserial, -- PK
  ip int unsigned, 
  created timestamp default now(), -- время доступа
  delta integer, -- интервал в миллисекундах между текущим и предыдущим доступом - надо вычислить в триггере при заполнении
  primary key(ipstats_id)
);


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

Скорей всего краулер


Основной и в нашем случае единственный критерий — это временная равномерность запросов:
( select count(*) from ipstats where ipstats.ip = (ip) and abs(ipstats.delta - (select avg(ipstats2.delta) from ipstats as ipstats2 where ipstats2.ip = (ip))) < 10000 ) >= 0.8 * (select count(*) from ipstats where ip = (ip)); 


Проще говоря — если отклонение от среднего значения дельты у 80% запросов меньше 10 секунд (может быть другая константа).
Если так — «блокируем IP»:
update ips set locked = true where ip = (ip); 

табличку ipstats для ip чистим и до «разболкировки» IP статистику по нему не ведем:
delete from ipstats where ip = (ip);

Есть ли риск, что попадут под раздачу реальные пользователи? Безусловно, есть — но ничего страшного, введут капчу и будут дальше работать.

Точно не краулер


1.
( select count(*) from ipstats where ip = (ip) and delta > 180000 ) > 0.1 * ( select count(*) from ipstats where ip = (ip) ); 

другими словами, если количество запросов с дельтой > 180 сек составляет более 10% от общего числа запросов.

2.
(select avg(delta) from ipstats where ip = (ip)) > 60000 

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

Если одно из этих условий сработало — чистим ipstats по ip:

delete from ipstats where ip = (ip)


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

комментарии (38)

+1
mattheus ,  
При достаточно высокой посещаемости сайта табличка ipstats быстро разрастется до неимоверных размеров, а операции над ней будут сильно нагружать базу.
Поэтому стоит например не хранить в таблице весь лог активности ip адреса, а сразу (в одной записи) методом скользящего среднего накапливать среднюю delta между запросами и аналогично накапливать значения delta для интересующих диапазонов (delta < 180000)
–3
+1 –4
eugenebartosh ,   * (был изменён)
да, спасибо за предложение. вообще это извечная проблема, и у кого реально большая посещаемость — вместо таблички в транзакционной БД можно применить какое-нибудь NoSQL с аналитической надстройкой
0
Bkmz ,  
capped collection в mongodb могут подойти.
+1
+2 –1
alyt ,  
Спасибо за материал. Пригодится. Действительно, тема попыток «увести» контент, за который честно уплочено — всегда актуальная. Особенно, если из-за такого вот увода попасть под санкции поисковиков. И доказывать, что это не по твоей вине. И выйдет же, что то ли он украл, то ли у него… но осадок остался.
+2
drakmail ,  
Распознавание капчи сейчас очень просто проводится в автоматическом режиме и стоит пару копеек за капчу, я бы не стал на неё сильно полагаться.
+1
drakmail ,  
Да и прокси сейчас очень дешевы, либо совсем бесплатны.
+1
eugenebartosh ,  
можно легко определить работу через прокси через поля
CLIENT_IP
FORWARDED
FORWARDED_FOR
FORWARDED_FOR_IP
HTTP_CLIENT_IP
HTTP_FORWARDED
HTTP_FORWARDED_FOR
HTTP_FORWARDED_FOR_IP
HTTP_PC_REMOTE_ADDR
HTTP_PROXY_CONNECTION'
HTTP_VIA
HTTP_X_FORWARDED
HTTP_X_FORWARDED_FOR
HTTP_X_FORWARDED_FOR_IP
HTTP_X_IMFORWARDS
HTTP_XROXY_CONNECTION
X_FORWARDED
X_FORWARDED_FOR
И ужесточить в этом случае критерии принятия решений в пользу краулера
+1
drakmail ,  
Верно, про это не стоит забывать.

Кстати говоря, уже сейчас стоит задумываться о том, что ipv6 всё чаще встречается на серверах, тут и прокси никаких не надо
0
mlu ,  
Для ipv6 у многих банится сразу /64, поэтому там не всё так радужно, хоть и лучше, чем с ipv4 :)
0
mlu ,  
Через socks4/5 и через http-прокси с методом connect этих полей можно и не увидеть.
0
Claud ,  
Халявные прокси медленные, живут недолго и в большинстве своем не анонимны. Дешевые прокси? Это где такие, или вы про те же напарсенные листы халявных? Если нет, то мне таки интересно где таки и так чтобы не солянка их разных стран.
0
drakmail ,  
Как-то приходилось работать со списком проксей от hideme, качество нормальное (более или менее), цена — очень низкая. Для парсинга всегда можно запустить потоков в 30-60, с отсечением тех прокси, которые не достаточно анонимны или задержка слишком большая. Если страница подгружается динамически полностью или частично — всегда можно использовать phantomjs или подобные движки, которые полностью эмулируют браузер. Для распознавания капчи тоже есть много разных дешевых сервисов.

Единственное, что сейчас сложно обойти — подтверждение по телефону. Но это и для владельца ресурса требует некоторых затрат. Так что тут всё очень не просто :)
0
Claud ,  
Кончено phantom — хорошо хотя с ним тоже есть вопросы но да вы и сами их наверняка знаете и главное я не пойму как он относится к вопросу про прокси :) капчи и всякие antigate тоже не пойму каким макаром идут сюда.

А вот «69 серверов в 34 странах» как то не очень. Из мне известных я знаю только один где можно купить под 1 000 русских прокси из которых не все живое. Правда последнее время меня это не интресует по этому может ситуация и сменилась, но верится в это с трудом. По тому что ip стоят денег и не кто вам их на шару давать не будет, по этому дешевые нормальные прокси в достаточных количествах это миф, или ботнет.
0
drakmail ,  
По качеству — похожи на ботнет :) Про 34 страны и всё такое — это VPN, прокси у них примерно 2000 есть — по адресу /proxy-list/, при покупке премиума дают доступ к их списку в виде csv. Процентов 80 из них работают хорошо (ну, раньше так было по крайней мере), некоторыми крупными сайтами не детектируются.

Про фантом и капчи — это так, небольшой оффтопик для треда комментариев, но по теме поста.
0
+1 –1
cher11 ,  
Статья интересная, но очень бы хотелось увидеть нормальное оформление запросов, хотя бы в тегах source. Об неправильном знаке в запросе на «точно не краулер» написал в ЛС.
Ну и раз уж на то пошло, было бы неплохо привести запросы, которые возвращают полноценные списки потенциально опасных IP, а не просто проверяют один заданный.
+1
eugenebartosh ,  
ok, спасибо еще раз, сейчас поработаем
+1
eugenebartosh ,   * (был изменён)
теги source оформил.
а списки опасных IP по контексту задачи нам нигде не понадобились — везде мы берем IP посетителя и от него разворачиваем действия и аналитику.
если же список понадобился — легко выбрать из ips тех, что уже «заблокированы», а из ipstats выбирать нет необходимости — там данные в процессе наблюдения, по которым мы пока не смогли принять решение.
+3
DeusModus ,  
Окай.
А если через phantomjs(webkit с API)?
+1
zenn ,  
Мне кажется, что ваше решение при достаточно хорошей посещаемости ресурса скорей выльется ему боком в качестве избыточной нагрузки. Тут скорей нужно идти в сторону превентивного анализа клиента — хорошим примером является тот же cloudflare в агрессивном режиме защиты. Да и решение должно быть скорей на стороне основного проксирующего сервера (nginx к примеру) а не по факту в основном теле кода приложения.
0
+1 –1
eugenebartosh ,   * (был изменён)
если реально высокая посещаемость (1M уников в день, скажем) — лучше применить NoSQL по похожему принципу, Hadoop например.
превентивный анализ клиента конечно вариант, но для клиента напряжно, и с индексацией поисковиками надо отдельно продумывать решение, рисковать индексацией…
+1
eugenebartosh ,   * (был изменён)
расчет для посещаемости 100 тыс уников в день:
— ips: 100.000 x 60 байт (~ размер записи с индексами) = 6Mb
— ipstats: 100.000 x 45 байт x 8 (примерное среднее количество просмотров на пользователя) = 36Mb
таблички при интенсивной работе окажутся целиком в памяти, а для сайта с посещаемостью 100 тыс потребление лишних 40-50Mb не должно стать заметным.
Если под миллион уников в день — тогда уже наверно надо думать над альтернативами
+1
spmbt ,  
Если статья посвящена борьбе с вредными пауками, то почему же на КДПВ изображён полезный, подобный поисковым ботам? Или вы как раз с ними боретесь?
+1
eugenebartosh ,  
внешний вид паука может быть обманчив )
+1
valemak ,  
Возможно, стоило взять фото из этой серии

+1
izac ,  
Да думаю по тематике эта картинка была бы лучше
+4
valemak ,  
Спасибо, отличная статья. Я как раз зарабатываю парсингом, материалы по антипарсингу читаю всегда с огромным интересом и удовольствием.

За пару-тройку лет данной деятельности, всё больше убеждаюсь, что текст, в принципе, от пауков не спрячешь. Картинки (которые обычно представляют очень высокую ценность) защитить намного проще с помощью водяных знаков. Обычно это и является косвенной, но эффективной защитой текста — парсят, в основном, интернет-магазины и описания продукции без фото мало кого интересует. Другой эффективной защиты от экспроприации контента (без критических осложнений для гугл-ботов и клиентов) лично я не вижу.
+1
eugenebartosh ,  
Кроме текста и картинок бывает нужно защитить еще кое-что — ну к примеру, CAD-чертежи. Да, раз они выложены открыто — значит не являются секретными, но пакетная их выгрузка с сайта приведёт к дикому трафику — весит каждый немало, а несколько гиг или десятков трафика как бы и жалко, особенно конкуренту :)
+1
eugenebartosh ,  
а чем пользуетесь? cURL? Если не секрет :-) Донбасу привет!
0
valemak ,  
Иногда cURL, но, если честно, он очень редко когда нужен. 95% сайтов по поводу защиты контента почему-то не парятся.
+1
Anisotropic ,  
>с дельтой > 180 сек составляет более 10% от общего числа запросов.
когда делал парсер одного «новостного» сайта, сделал таймаут в одну секунду между запросами. Вашу защиту это бы обошло, а время парсинга не сильно бы увеличилось.
+4
ustasby ,  
вывод, не боритесь с ветряными мельницами
0
dikkini ,  
Немного не понимаю. Разве задача: Понять что тебя кроулят, не сводится к 10-1000 запросам в секунду, логика отслеживания данного поведения; плюс отслеживание поведения быстрой смены адреса (прокси и т.д.); и так далее, отслеживание определенных парадигм поведения, дико сделать код немного само-обучаемым. Не так все? :-)
0
eugenebartosh ,  
ну исходим из того, что краулят и парсят одновременно — после скачивания ресурса надо рабобрать html. найти картинки, их тоже скачать, + задержка (если сайт начнет тормозить — краулер сам себя считайте сдал злому админу). а сделать код самообучаемым + самомодифицирующимся — это следующая серия (Битва Роботов:)
0
maxru ,  
Слабовато. От любителей спасёт, конечно, но от адресного парсинга — нет.

P.S. Эти ваши капчи и проверки на равномерность частоты запросов обходятся достаточно тривиально — дивизия проксей и сервис по разгадыванию капч него афроафриканцами (если с кириллицей, то будет подороже).
0
rozhik ,  
watermark — да эффективно!..
> Используйте watermark на основе полупрозрачной картинки, желательно с тонким полностью непрозрачным контуром.
Тонкий контур отлично выводится (если извесна его маска), как и всё остальное не аляповатое. Так что искажение картинки watermark — на порядок эффективнее.

Война снаряда и брони
>… если краулер создается программистом адресно под сайт…
Если так то — это война времени и бюджетов. А её лучше переносить в юридическую плоскость.

Поисковики
> Обычно такие боты имеют определенную сигнатуру user-agent в http-заголовке — например Googlebot.
На эти заголовки нужно просто забить, IP lists на много точнее. Далее текст так-же не эффективен.

> IP-адрес клиента — единственная объективная информация
Высказывание верно. Но идея добавляет много нагрузки на сайт, и при этом слишком легко обходится через прокси. При этом отрезает многих пользователей аксельраторов и анонимайзеров.

Я бы посоветовал использовать механизмы, которые видны в google books для текста (если IP не в списке поисковиков) — это потребует OCR на стороне кравлера,
Искажаемые водяные знаки для графики. Это значительно ухудшит качество восстановленных картинок.
Использование нескольких хитростей с куками и джаваскриптом может заставить испозьзовать эмуляторы браузеров.

О статье в целом — есть не плохие идеи, но отношение затрат на вэб сервере к затратам на кравлере слишком большое.
0
OpenMinded ,  
Возможно в некоторых ситуациях поможет создание удобного платного API. Раз уж на данные есть спрос, то почему бы не воспользоваться этим.
0
joger ,  
иногда имеет смысл не блокировать бота, а отдавать ему изменённую страницу. так владелец бота может гораздо дольше оставаться в неизвестности, что его вычислили. но опять же: захотят — спарсят
0
Claud ,  
Мысли в слух.

Сам не проверял сейчас такое не требуется, но:

Помнится мне, что в webmaster tools у google был раздел для индексации скрытого контента. Там вроде бы надо cookie им предоставить. У yandex наверняка есть тоже что-нибудь по этому поводу. Имея маркер поисковика мы можем уже не опираться на user-agent. Значит можем накрутить проверку на наличие js у юзера. Дальше можно усложнить жизнь ботам через различные определения движка браузера, чтобы отсеять клоунов, которые на phantom представляются как firefox.

Как результат, парсинг для большинства заказчиков будет подороже, а наш грошовый отечественный искатель фрилансеров удавится за лишние 100р. :)

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