на чем пишут парсеры

Как спарсить любой сайт?

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

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

Чтобы спарсить данные с вебсайта, пробуйте подходы именно в таком порядке:

Найдите официальное API,

Найдите XHR запросы в консоли разработчика вашего браузера,

Найдите сырые JSON в html странице,

Отрендерите код страницы через автоматизацию браузера,

Совет профессионалов: не начинайте с BS4/Scrapy

Крутые вебсайты с крутыми продактами делают тонну A/B тестов, чтобы повышать конверсии, вовлеченности и другие бизнес-метрики. Для нас это значит одно: элементы на вебстранице будут меняться и переставляться. В идеальном мире, наш написанный парсер не должен требовать доработки каждую неделю из-за изменений на сайте.

Приходим к выводу, что не надо извлекать данные из HTML тегов раньше времени: разметка страницы может сильно поменяться, а CSS-селекторы и XPath могут не помочь. Используйте другие методы, о которых ниже. ⬇️

Используйте официальный API

Поищите XHR запросы в консоли разработчика

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

В итоге, даже не имея официального API, можно воспользоваться красивым и удобным закрытым API. ☺️

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

Алгорим действий такой:

Открывайте вебстраницу, которую хотите спарсить

Открывайте вкладку Network и кликайте на фильтр XHR запросов

Обновляйте страницу, чтобы в логах стали появляться запросы

Найдите запрос, который запрашивает данные, которые вам нужны

Копируйте запрос как cURL и переносите его в свой язык программирования для дальнейшей автоматизации.

Кнопка, которую я искал месяцы

Поищите JSON в HTML коде страницы

Как было удобно с XHR запросами, да? Ощущение, что ты используешь официальное API. 🤗 Приходит много данных, ты все сохраняешь в базу. Ты счастлив. Ты бог парсинга.

Но тут надо парсить другой сайт, а там нет нужных GET/POST запросов! Ну вот нет и все. И ты думаешь: неужели расчехлять XPath/CSS-selectors? 🙅‍♀️ Нет! 🙅‍♂️

Чтобы страница хорошо проиндексировалась поисковиками, необходимо, чтобы в HTML коде уже содержалась вся полезная информация: поисковики не рендерят Javascript, довольствуясь только HTML. А значит, где-то в коде должны быть все данные.

Современные SSR-движки (server-side-rendering) оставляют внизу страницы JSON со всеми данные, добавленный бекендом при генерации страницы. Стоп, это же и есть ответ API, который нам нужен! 😱😱😱

Вот несколько примеров, где такой клад может быть зарыт (не баньте, плиз):

Красивый JSON на главной странице Habr.com. Почти официальный API! Надеюсь, меня не забанят. И наш любимый (у парсеров) Linkedin!

Алгоритм действий такой:

В dev tools берете самый первый запрос, где браузер запрашивает HTML страницу (не код текущий уже отрендеренной страницы, а именно ответ GET запроса).

Внизу ищите длинную длинную строчку с данными.

Вырезаете JSON из HTML любыми костылямии (я использую html.find(«=<") ).

Отрендерите JS через Headless Browsers

Если коротко, то есть инструменты, которые позволяют управлять браузером: открывать страницы, вводить текст, скроллить, кликать. Конечно же, это все было сделано для того, чтобы автоматизировать тесты веб интерфейса. I’m something of a web QA myself.

После того, как вы открыли страницу, чуть подождали (пока JS сделает все свои 100500 запросов), можно смотреть на HTML страницу опять и поискать там тот заветный JSON со всеми данными.

Для масштабируемости и простоты, я советую использовать удалённые браузерные кластеры (remote Selenium grid).

Вот так я подключаюсь к Selenoid из своего кода: по факту нужно просто указать адрес запущенного Selenoid, но я еще зачем-то передаю кучу параметров бразеру, вдруг вы тоже захотите. На выходе этой функции у меня обычный Selenium driver, который я использую также, как если бы я запускал браузер локально (через файлик chromedriver).

Парсите HTML теги

Если случилось чудо и у сайта нет ни официального API, ни вкусных XHR запросов, ни жирного JSON внизу HTML, если рендеринг браузерами вам тоже не помог, то остается последний, самый нудный и неблагодарный метод. Да, это взять и начать парсить HTML разметку страницы. То есть, например, из Cool website достать ссылку. Это можно делать как простыми регулярными выражениями, так и через более умные инструменты (в питоне это BeautifulSoup4 и Scrapy) и фильтры (XPath, CSS-selectors).

Мой единственный совет: постараться минимизировать число фильтров и условий, чтобы меньше переобучаться на текущей структуре HTML страницы, которая может измениться в следующем A/B тесте.

Подписывайтесь на мой Телеграм канал, где я рассказываю свои истории из парсинга и сливаю датасеты.

Источник

Написание парсера с нуля: так ли страшен черт?

Очевидно, что парсер нужно было переписать на C#, но при мысли о написании парсера с нуля вдруг находилась дюжина других срочных дел. Таким образом таск перекидывался и откладывался практически полгода и казался непосильным, а в итоге был сделан за 4 дня. Под катом я расскажу об удобном способе, позволившим реализовать парсер достаточно сложной грамматики без использования сторонних библиотек и не тронуться умом, а также о том, как это позволило улучшить язык LENS.

Но обо всем по порядку.

Первый блин

Как было сказано выше, в качестве ядра парсера мы использовали библиотеку FParsec. Причины данного выбора скорее исторические, нежели объективные: понравился легковесный синтаксис, хотелось поупражняться в использовании F#, и автор библиотеки очень оперативно отвечал на несколько вопросов по email.

Главным недостатком этой библиотеки для нашего проекта оказались внешние зависимости:

Другой проблемой было отображение ошибок. Лаконичная запись грамматики на местном DSL при некорректно введенной программе выдавала нечитаемую ошибку c перечислением ожидаемых лексем:

Хотя кастомная обработка ошибок и возможна, DSL для нее явно не предназначен. Описание грамматики уродливо распухает и становится абсолютно неподдерживаемым.

Еще одним неприятным моментом была скорость работы. При «холодном старте» компиляция любого, даже самого простого скрипта занимала на моей машине примерно 350-380 миллисекунд. Судя по тому, что повторный запуск такого же скрипта занимал уже всего-то 5-10 миллисекунд, задержка была вызвана JIT-компиляцией.

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

Немного теории

Сферический парсер в вакууме представляет собой функцию, которая принимает исходный код, а возвращает некое промежуточное представление, по которому удобно будет сгенерировать код для используемой виртуальной машины или процессора. Чаще всего это представление имеет древовидную структуру и называется абстрактным синтаксическим деревом — АСД (в иностранной литературе — abstract syntactic tree, AST).

Итак, на входе мы имеем строку. Набор символов. Работать с ней в таком виде напрямую не слишком удобно — приходится учитывать пробелы, переносы строк и комментарии. Для упрощения себе жизни разработчики парсеров обычно разделяют разбор на несколько проходов, каждый из которых выполняет какую-то одну простую задачу и передает результат своей работы следующему:

Лексический анализатор

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

Синтаксический анализатор

Тут пытливый читатель спросит:
— Как это, просто вызываются по порядку? А как же опережающие проверки? Например, так:

Признаюсь, свой первый серьезный парсер я написал именно так. Однако это плохая идея!

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

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

Рассмотрим на примере выше. Допустим, у нас есть текст: a.1 = 2 :

С их помощью реализация приведенной выше грамматики становится практически тривиальной:

Атрибут DebuggerStepThrough сильно помогает при отладке. Поскольку все вызовы вложенных правил так или иначе проходят через Attempt и Ensure, без этого атрибута они будут постоянно бросаться в глаза при Step Into и забивать стек вызовов.

Преимущества данного метода:

Операторы и приоритеты

Неоднократно я видел в описаниях грамматик примерно следующие правила, показывающие приоритет операций:

Теперь представим, что у нас есть еще булевы операторы, операторы сравнения, операторы сдвига, бинарные операторы, или какие-нибудь собственные. Сколько правил получается, и сколько всего придется поменять, если вдруг придется добавить новый оператор с приоритетом где-то в середине?

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

Теперь для добавления нового оператора необходимо лишь дописать соответствующую строчку в инициализацию списка приоритетов.

Добавление поддержки унарных префиксных операторов оставляю в качестве тренировки для особо любопытных.

Что нам это дало?

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

Итого, сравнительная таблица результатов:

Параметр FParsec Parser Pure C#
Время парсинга при 1 прогоне 220 ms 90 ms
Время парсинга при дальнейших прогонах 5 ms 6 ms
Размер требуемых библиотек 800 KB + F# Runtime 260 KB

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

Избавившись от головной боли с изменениями в грамматике, мы смогли запилить в LENS несколько приятных вещей:

Цикл for

Используется как для обхода последовательностей, так и для диапазонов:

Композиция функций

С помощью оператора :> можно создавать новые функции, «нанизывая» существующие:

Частичное применение возможно с помощью анонимных функций:

Улучшения синтаксиса

Источник

На каком языке лучше писать парсеры?

Нужно сделать парсер который сайт очень быстро мог обойти, подскажите на каком языке лучше его писать? Думала на php, но вроде как я понимаю это плохая идея, сможете ещё объяснить мне почему php плох для парсеров?

попробуй питон. там несложный синтаксис + есть готовые библиотеки, которые могут что тебе нужно

. но нету полноценных лямбд.

На чём удобно, на том и пиши. Регулярки есть везде, обход dom тоже. Я использовал hxselect (https://www.w3.org/Tools/HTML-XML-utils/) с башем для этого дела, т.к. удобно смотреть выхлоп и вообще с пайпами работать, вместо кодинга с итерациями и всякими вонючими либами.

Нужно сделать парсер который сайт очень быстро мог обойти

Хотела на php, но мне сказали что он работает в 1 поток и из-за этого будет очень медленно парсится сайт.

Не надо парсить HTML/json регэкспами!

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

Думала на php, но вроде как я понимаю это плохая идея, сможете ещё объяснить мне почему php плох для парсеров?

В смысле, «я сама придумала, что он плохой, а вы объясните мне, почему?»

нененене, выше я писала что мне сказали что php плохо подходит, т.к. он медленный и работает в 1 поток

Те загонять в базу и потом sql? И как оно быстрее чем в память.

Это скраппер, а не парсер. Ищем удобную либу для любого языка и вперёд.

На любом языке общего назначения. Что из языков ты знаешь, кроме PHP?

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

но мне сказали что он работает в 1 поток и из-за этого будет очень медленно парсится сайт.

А вы уверены, что тот кого вы собираетесь парсить будет рад нагрузке от вашего парсера в N-потоков?

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

На котором умеешь писать код.

на php, но вроде как я понимаю это плохая идея

PHP мало чем отличается от других языков по основной сути. Его ругают, но код на нём всё же работает.

сможете ещё объяснить мне почему php плох для парсеров?

Это нужно спрашивать у тех, кто заявляет, что «PHP плох для парсеров».

Для PHP рекомендую взять http://simplehtmldom.sourceforge.net/ (хотя я других библиотек и не пробовал вообще-то). Для Python — https://www.crummy.com/software/BeautifulSoup/bs4/doc/, запускать под PyPy.

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

мне сказали что php плохо подходит, т.к. он медленный и работает в 1 поток

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

Парсеры для стандартных форматов вообще писать не нужно, они есть готовые. А PHP плох вообще для всего.

Не важно, что за ЯП или библиотека, главное, чтобы chrome headless.

который сайт очень быстро мог обойти

Какой сайт и насколько быстро

А мог бы уже сделать. Чем тебя нода не устроила? Твоя задача сводится к пониманию структуры представления на сайте и поиску xpath, если искомые элементы типа ссылок «далее» промаркированы, то вообще всё просто.

Бери scrappy, если питона не знаешь. Там по-моему можно без проблем headless браузер прикрутить для джаваскрипта, но процессинг жс это ресурсоёмко будет.

На том, что знаешь. Библиотеки разбора HTML есть везде.

И не называй разбор HTML «парсингом»

Парсилку html в php можно запросто написать, если регекспами парсить.

Я так понимаю, призывалась эта ссылка.

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

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

Мало ли о чем там дегенераты пишут, их там много

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

Самокритично, даже я бы сказал, неожиданно. Хвалю.

Kaitai Struct, про него на ЛОРе были новости. Описываешь формат в декларативном виде и генерируешь парсер под язык, который тебе нравится (если он поддерживается Kaitai Struct).

Как-то много шутников в этой теме,

причём тут парсер и регекспы?

но вообще для парсинга ничего этого не нужно

разбирать html можно даже simplexml’ом

но тебе это тоже не нужно

Няшная растишечка же!

Rust для парсеров идеален.

на том, который умеет работать с регулярными выражениями

Конечно нет, в 99% случаев тебе не придутся использовать регулярки никак. Ну и потом, парсить регулярками html, это такое себе. Даже если они достаточно быстрые, профит будет убит тем что там по 10000 циклов разборов на каждом предложении.

Рекомендую Perl5. Язык был специально создан для обработки данных. Существует множество готовых библиотек и даже программ — для разных форматов, например. Беспрецедентная поддержка UNICODE. Позиционируется как максимально приближенный к естественному (английскому) языку синтаксис, поэтому, помимо всего прочего, множество интуитивно понятных примеров конструкций. Те же регулярные выражения возникли как стандарт именно из развития Perl5, в который они очень гармонично встроены.

Ragel. Дальше можешь использовать любой другой язык для обработки напаршеного.

Не заметил, что там речь именно о сайте 🙁

cheerio тогда возьми.

который сайт очень быстро мог обойти

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

На джаве с нормальной IDE.

сможете ещё объяснить мне почему php плох для парсеров?

Источник

Почему стоит научиться «парсить» сайты, или как написать свой первый парсер на Python

В этой статье я постараюсь понятно рассказать о парсинге данных и его нюансах.

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

Перейдем к этапам парсинга.

И так, рассмотрим первый этап парсинга — Поиск данных.

Так как нужно парсить что-то полезное и интересное давайте попробуем спарсить информацию с сайта work.ua.
Для начала работы, установим 3 библиотеки Python.

pip install beautifulsoup4

Без цифры 4 вы ставите старый BS3, который работает только под Python(2.х).

pip install requests
pip install pandas

Теперь с помощью этих трех библиотек Python, можно проанализировать нашу веб-страницу.

Второй этап парсинга — Извлечение информации.

Попробуем получить структуру html-кода нашего сайта.
Давайте подключим наши новые библиотеки.

И сделаем наш первый get-запрос.

Статус 200 состояния HTTP — означает, что мы получили положительный ответ от сервера. Прекрасно, теперь получим код странички.

Получилось очень много, правда? Давайте попробуем получить названия вакансий на этой страничке. Для этого посмотрим в каком элементе html-кода хранится эта информация.

У нас есть тег h2 с классом «add-bottom-sm», внутри которого содержится тег a. Отлично, теперь получим title элемента a.

Хорошо, мы получили названия вакансий. Давайте спарсим теперь каждую ссылку на вакансию и ее описание. Описание находится в теге p с классом overflow. Ссылка находится все в том же элементе a.

Получаем такой код.

И последний этап парсинга — Сохранение данных.

Давайте соберем всю полученную информацию по страничке и запишем в удобный формат — csv.

После запуска появится файл test.csv — с результатами поиска.

«Кто владеет информацией, тот владеет миром» (Н. Ротшильд).

Источник

Читайте также:  Фейспалм что это перевод
Строительный портал