Pull to refresh

Comments 44

Не много оффтоп:
Нам препод рассказывал что можно парсить страницы с помощью недетерменированых конечных автоматов, как просто парсить например xml/html и строить его дерево например с их помощью я знаю, а вот как проанализировать например веб-страницу на наличие нужно информации?
Ну, про НКА стоит думать на этапе написания чего-то вроде cheerio или, например, lxml, а на этапе скрейпинга просто используется готовый парсер по принципу чёрного ящика.

Анализ наличия нужной информации — это довольно абстрактная тема, так как признаки нужности могут быть очень разными. Что-то можно проверить простенькой регуляркой, а что-то нужно прогонять через мощный синтаксический анализатор на НКА.

Спасибо за статью. Недавно я тоже написал две на такую же тему, с чуть более узким предметом:


https://habrahabr.ru/post/274475/
https://habrahabr.ru/post/277513/


Там разбираются многие темы, о которых вы упомянули.


Сделаю несколько заметок.


  1. Использование JSDOM (в консольных скриптах) или NW.js (в GUI) помогает отказаться от сразу нескольких модулей, поскольку берёт на себя и HTTP, и парсинг DOM, и разрешение ссылок, и правильную обработку кодировок.


  2. В качестве предосторожности от сбоев можно вести два лога: лог данных и лог успешно сохранённых адресов. Если чистовой файл результата нужен в виде единого объекта JSON, в конце работы лог данных можно переконвертировать, если достаточно простого несортированного результата — лог данных и будет таким результатом. Лог адресов можно загружать после прерывания, чтобы, во-первых, проверять по нему ссылки не грузить их по второму разу, и чтобы можно было начинать с последнего успешного адреса.


  3. Если данные нужно сохранять в последовательном порядке (как в случае со словарями), параллельность выполнения будет мешать, если данных нужно сохранить очень много. В моей первой статье я описываю сохранение словаря размером в 2 гигабайта — строить такую структуру в памяти и потом её сортировать очень рискованно. Писать её в файл и потом его сортировать тоже может быть небезопасно (не у всех машины позволят такое провернуть, да и Node нужно будет запускать с ключами увеличения памяти).


  4. Также при асинхронной параллельности нужно учитывать политику сайта для ботов. Иногда её можно подсмотреть в robots.txt, а иногда приходится выяснять экспериментально. Многие сайты забанят особо ретивый скрипт после первых десятков запросов. Тогда приходится даже вставлять искусственные таймауты в однопоточную работу.
Статьи хорошие, да )

1. Никто не мешает собрать несколько модулей в один и использовать его и для всей последовательности задач. Проблема в том, что универсальные модули часто не учитывают много частных случаев, так что постоянно приходится лезть под капот, поэтому стоит иметь представление о том, как там всё устроено. Я и сам использую для скрейпинга собственный модуль, который «берёт на себя и HTTP, и парсинг DOM, и разрешение ссылок, и правильную обработку кодировок» и много чего ещё делает, но часто попадаются сайты, которые он не в состоянии обработать. Приходится к дедлайну всё делать на коленке из специализированных модулей, а потом, в спокойной остановке, допиливать универсальный модуль на будущее. Так он и развивается.

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

3. Упорядочить можно уже готовые данные. Надо просто предусмотреть соответствующие поля заранее. Огромные структуры скрейпить в память — сизифов труд, да. В идеальном мире стоит писать в БД и проверять ошибки при перезапусках. Но в реальном мире заказчики иногда просят сохранение в читабельные файлы (например, им лень или трудно поднять БД на своей стороне), так что приходится выкручиваться, и не только с сортировкой. Про скрейпинг больших объёмов тоже будет статья, да.

4. Всякое бывает. Некоторые сайты честно просят не пинать их ботами чаще, чем раз в Х секунд, например. Теоретически, можно скрейпить параллельно через несколько прокси, и блокировки не будет, но можно, например, положить сервер нагрузкой, что как минимум не этично. Надо смотреть по ситуации. Конкретно в случае с Ferra.ru я запрашивал у главреда разрешение на использование скрейпера.

Короче, по каждому из четырёх пунктов стоит ответить полноценной статьёй :)
Делал подобное в 2001 на Java через HTTPS (JSSE) с паролями, сессией и cookies и «страницей подробностей» на каждую строчку…

Не знал, что это называется «Web scraping»…
Ух ты! В 2001 году это был тот ещё экстрим.

С терминологией в этой области до сих пор бардак. Возможно решусь сделать словарик отдельной статьёй. Пока в поисковике приходится десятки терминов пробивать, чтобы ничего не упустить. Термин «Web scraping» более-менее устоялся на англоязычных биржах фриланса.
Многие сайты борются с этим. Из своего опыта, хорошие инструменты PhantomJS и SlimerJS (и обертка над ними в виде CasperJS). Но и эти инструменты не идеальны, так как существуют достаточно простые методы для детектирования headles браузеров. После этого остается только использовать настоящие браузеры например при помощи WebdriverIO

Есть ещё проекты NW.js и Electron, связывающие Node.js и Chromium.

Мне кажется у них немного иное предназначение, хотя могу ошибаться, использовал только Atom построенный на базе Electron

Да, у них очень широкое предназначение, но в том числе и такое. Я использовал NW.js для копирования сетевого словаря, довольно удобно и надёжно.

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

ну и защититься наверно от такого сложнее, так как происходит полноценная эмуляция браузера

Ну, htmlunit — это обычный headless. Как PhantomJS только на Java. Защититься от него можно так же легко/трудно, как от PhantomJS. Вот тут я эту тему немного раскрыл, если что.

все собирался у Вас спросить но стеснялся ) а не попадалась ли Вам (может свой опыт есть) инфа по детекту фантома (да и вообще версии движка хрома) по оптимизациям кода? Типа берем два куска кода, один оптимизируемый, другой нет, гоняем, замеряем. Если версия выше такой-то — один из семплов соптимизируется и будет быстрее другого, если ниже — то оба сопоставимо. И на разных наборах оптимизаций определять версию движка. Чет искал такое — не нашел, а идея очевидная. Возможно неправильно терминологию на английском в запросах использую, не верю что таких исследований не было.

Честно говоря, лучшая инфа, что мне попадалась по этой теме — это те две ссылки, которые kirill3333 привёл в комментариях к этой статье (выше и ниже соответственно). Главное, что стоит знать, на мой взгляд, если разработчик сайта захочет детектировать робота — значит он детектирует и гарантированных способов ему помешать нет. Просто как пример того, насколько далеко можно зайти в этом направлении посмотрите технологии на сайте areyouahuman.com. И это ещё не предел.

areyouahuman.com

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

так как происходит полноценная эмуляция браузера

Полноценная эмуляция броузера — это selenium на полноценном хроме или файрфоксе. А фантом все таки огрызок, и если это не специальная сборка, то вычислить его можно.

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

А какие методы детектирования headless-браузеров имеете в виду вы? анализ нагрузки, генерируемой с одинаковыми cookies с одинакового IP?
Да, бывают сайты, где только Selenium, только хардкор. К счастью, их мало :)

Борются часто не со скрейпингом, а, например, с ботами-коментаторами, с накруткой, с DDoSами, с попытками взлома и так далее, а скрейпинг попадает под раздачу. Но иногда такая хитрая защита означает настойчивую просьбу не скрейпить, и тогда это уже вопрос морали и закона. Но технически через Selenium скрейпится всё, что можно скрейпить вручную.

Я недавно натолкнулся на подобную особенность. Сначала долго ломал голову себе и докучал создателям jsdom, почему модуль не видит ссылку на следующую страницу. Весь код и структура идентичны в браузере и скрипте, за исключением ссылки на следующую страницу. Потом меня осенило, я переопределил userAgent в скрипте, и ссылка появилась.

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

используется JQuery. Это довольно надёжный признак того, что cheerio с таким сайтом спарвится
Полагаю взаимосвязи тут нет и быть не может, принципы работы jQuery и cheerio абсолютно разные, несмотря на схожий интерфейс использования и язык реализации.
— needle отправляет некий минимальный набор заголовков. Без этого никак, да.

— Я имел в виду, что если html достаточно «семантический» для того, чтобы использовать CSS-селекторы в jQuery, то эти же селекторы можно использовать и в cheerio. И то и другое — DOM-парсер в своей основе, так что если на странице используют jQuery, то она под него оптимизирована и cheerio на ней будет хорошо работать. Просто иногда бывает жуткая мешанина из div вообще без атрибутов и сориентироваться можно только по текстовым данным — тогда любые DOM-парсеры справляются плохо (а иногда и регулярки подводят).
Есть же готовый инструмент, screen-scraper.com
Бесплатная версия для основных задач такого рода вполне подходит.
Есть довольно много подобных готовых инструментов. Проблема в том, что они подходят только основные задачи.

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

Я и сам использую универсальный модуль собственного сочинения и написание скрейпера для простого сайта у меня занимает от получаса, но бывают случаи, когда приходится вспоминать знания низкоуровневых основ. Это происходит всё реже, так как модуль непрерывно совершенствуется после таких случаев, но это всегда будет происходить, так как веб — это очень терпимая к нарушениям и ошибкам среда, где универсальные решения крайне сложно делать. Это я ещё не говорю о том, что даже начинающий хакер легко напишет защиту от сервисов уровня screen-scraper.com.
Я же не утверждал, что это панацея :)
Всего лишь инструмент.
Без понимания именно тех низкоуровневых основ — в любом случае не получится сделать что-то сложное.

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

Защиту от него написать — ну тут не знаю… Он же шлет себе http-запросы… Я же их сам и редактирую, если надо. Как сайт определит, что я не из браузера захожу?

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

Защита пишется влёгкую. Конечно, это типичная «битва брони и снаряда», и пока одни люди совершенствуют защиты — другие люди совершенствуют методы их обхода. Анализ заголовков браузера — это в последние годы и защитой-то не считается. Сейчас сайт определит, что вы зашли не из браузера, так как в ваших http-запросах не будет тех хитрых зашифрованных данных, которые в браузере вычислились бы подключённым js-скриптом, крайне трудным для реверс-инжиниринга. В запрос можно подставить всё — куки, заголовки, данные форм и так далее — однако все эти данные откуда-то нужно получить, а они не обязательно хранятся в http-ответах в открытом виде. Есть и другие способы защиты, вплоть до анализа частоты запросов. Это серьёзно. Чтобы понять, насколько это серьёзно, надо осознать, что большая часть защит пишется не от скрейперов, а от профессиональных взломщиков банковских онлайн-клиентов, а уж потом используется и против скрейперов тоже.

Кстати, насчёт защиты вычислением. Хотя этикет и требует, чтобы основные данные на страницах были доступны и при отключении JavaScript, часто бывает, что данные почти целиком генерируются скриптами. В таком случае мало скачать код и распарсить его регулярками или dom-парсером. Нужен инструмент, который сможет выполнить скрипты в правильном контексте с поддержкой всех нужных стандартов, и построить дерево динамически. Тут как раз и понадобятся средства вроде jsdom или интерфейсов к браузерным движкам.

В большинстве таких случаев данные не генерятся, а запрашиваются по ajax или как-то ещё, но в открытом виде. Или генерятся каким-то очевидным образом. Если данные трудно получить без таких вещей как PhantomJS, то владелец сайта точно против того, чтобы его скрейпили. И стоит задуматься, что у него могут быть не только хорошие хакеры, но и хорошие юристы. В любом случае, весь код страницы открыт для реверс-инжиниринга, вопрос только в трудоёмкости.

Для тяжелых случаев вполне подойдет selenium, странно что его не упомянули.

Знаю это.
Я почему и пишу, пару лет назад как раз и занимался этим. Сейчас — нет.
И тогда как раз скрин-скрейпер мне реально помог. Правда там был enterprise edition, у заказчиков был куплен.
С защитой постоянно сталкивался.
Всё обходилось легко, всякие сессии, cookies и т.д. — влёт, это вообще не вопрос. Эмуляция запроса-ответа, потом следующий запрос с новыми данными. А js я же в ответе получаю, его же и использую.
AJAX — вызывал проблемы, там уже руками приходилось писать, но тоже решаемо.

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

С другой стороны — стандартные задачи они стандартно и решаются… У меня обычно было что-то типа каталога на сайте. Сначала поиск по одному критерию, получаешь несколько страниц списков. По всем страницам пройти, зайти в каждый элемент списка и оттуда взять данные, сохранить в удобоваримой форме.
Доходило до 4-5 дней на один сайт (с паузами, чтобы не блокировали по частым запросам)
Но написание скрипта на каждый сайт (а их больше 500 было) в итоге занимало по полчаса.
Ох, задача древняя как IE8. Видел статью на русском еще с nodejs-v0.4.0. Сейчас даже не могу найти.

Хоть я и фанат nodejs, такие несложные задачи обычно решаю на bash с использованием curl и pup. Если не заморачиваться с кодированием выходной информации, скрипт получается гораздо короче. Очень удобно использовать, например, для проверки баланса на счете или для определения инфы по внешнему ip…
Я бы даже сказал, что задача древняя как Netscape. Но веб развивается, появляются сложности, которых раньше не было. Я занимался скрейпингом ещё до IE8 и берусь утверждать, что скрейпинг «уже не торт».

Тема выбора языка для скрейпинга тянет на отдельную статью. Для проверки баланса и других подобных задач bash более чем достаточно. Тут я согласен. Но если нужно пройтись по десятку другому тысяч ссылок — тут уже стоит использовать… ну, хотя бы perl :)

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

Я имел в виду задача для nodejs древняя.

По поводу выбора языка, на данный момент bash по гибкости не сильно уступает perl. Многие используемые bash`ом инструменты по обработке текстов написаны на perl. А уж проверить баланс или спарсить внешний ip откуда-нибудь — задача вообще одной строчки.
Ну… скрипт на perl или несколько скриптов на perl соединённые через bash — тут слишком тонкая грань. Но по сути я с вами согласен, парсинг нескольких незащищённых страниц — это пустячная задача.
grab.spider на python куда удобнее :-)
а тут получается спагетти из кода. Хотя node.js мне нравится, но для парсинга/скрапинга использую python.

Да, хорошая штука. Для node.js нечто подобное только ещё предстоит написать.


Суть в том, что подобные фреймворки появляются только тогда, когда реальный практик берётся изучать и использовать самые основы, а не выбирает из имеющихся недорешений и костылей к ним. Я на 100% уверен, что lorien умеет скрейпить без фреймворка, просто для него это пройденный этап.

Собственно как я понимаю, этот фреймворк родился в процессе скрейпинга «с нуля» и его архитектура мне очень нравится, за исключением пары моментов, таких как плохая работа через socks прокси, но это больше проблема pycurl (multicurl)
Есть ли какие-то способы решения проблемы «отваливающихся» парсеров? Пример: скрапер готов и передан клиенту, а через неделю на сайте сменились некоторые url/path/query страниц или id/class/xpath у элементов и всё отвалилось. Что обычно предпринимают в таком случае?
Обычно клиент заказывает доработку парсера под эти изменения. Это обычный риск и большинство клиентов это понимают. Некоторые сразу оговаривают условия «сопровождения» скрипта.

Вообще, если только структура html поменялась, то это пустяковая проблема, она быстро решается. Вот если сама архитектура бэкенда меняется или защита усиливается — тогда это практически новый заказ. Это большинство клиентов как минимум способны понять, если их заранее предупредить.
Sign up to leave a comment.

Articles