простой парсер на php
Как я html-парсер на php писал, и что из этого вышло. Вводная часть
Сегодня я хочу рассказать, как написать html парсер, а также с какими проблемами я столкнулся, разрабатывая подобный парсер на php. А проблем было много. И в первой части я расскажу о проектировании парсера, и о возникших проблемах, ведь html парсер отличается от парсера привычных всем языков программирования.
Введение
Я старался написать текст этой статьи максимально понятно, чтобы любой, кто даже не знаком с общим устройством парсеров мог понять то, как работает html парсер.
Здесь и далее в статье я буду называть документ, содержащий html просто «Документ».
Dom дерево, находящееся в элементе, будет называться «Подмассив».
Что должен делать парсер?
Давайте сначала определимся, что должен делать парсер, чтобы в будущем отталкиваться от этого при разработке. А именно, парсер должен:
Впрочем, это мелочи. Основного функционала вполне хватит, чтобы поломать голову пару ночей напролет.
Но тут есть проблема, с которой я столкнулся сразу же: Html — это не просто язык, это язык гипертекста. У такого языка свой синтаксис, и обычный парсер не подойдет.
Разделяй и властвуй
Для начала, нужно разделить работу парсера на два этапа:
Для описания первого этапа я нарисовал схему, которая наглядно показывает, как обрабатываются данные на первом этапе:
Парсинг и обработка веб-страницы на PHP: выбираем лучшую библиотеку
Задача спарсить и обработать необходимую информацию со стороннего сайта встает перед веб-разработчиком довольно часто и по самым разнообразным причинам: таким образом можно заполнять свой проект контентом, динамически подгружать какую-то информацию и так далее.
В таких случаях перед программистом встает вопрос: какую из десятков библиотек выбрать? В этой статье мы постарались рассмотреть самые популярные варианты и выбрать из них лучший.
Регулярные выражения
Даже не смотря на то, что «регулярки» — это первое, что приходит на ум, использовать их для настоящих проектов не стоит.
Да, с простыми задачами регулярные выражения справляются лучше всех, но его использование значительно затрудняется, когда нужно спарсить большой и сложный кусок HTML-кода, который, к тому же, не всегда соответствует какому-то определенному шаблону и вообще может содержать синтаксические ошибки.
Вместо «допиливания» своего регулярного выражения при каждом малейшем изменении кода рекомендуем использовать инструменты ниже — это и проще, и удобнее, и надежнее.
XPath и DOM
DOM и XPath не являются библиотеками в привычном смысле этого слова, это стандартные модули, которые встроены в PHP начиная с пятой версии. Именно отсутствие необходимости использовать сторонние решения делает их одними из лучших инструментов для парсинга HTML страниц.
На первый взгляд может показаться, что низкий порог входа — это не о них, некоторые места и вправду являются весьма сложными. Но это только на первый взгляд: стоит только немного разобраться с синтаксисом и базовыми принципами, как XPath тут же станет для вас инструментом для парсинга номер один.
Вот, например, код с использованием DOM и XPath, который ищет в разметке все теги и модифицирует их атрибуты src :
Тем не менее, данный вариант не лишен минусов — для парсинга используется движок, в первую очередь предназначенный для работы с XML, а XML и HTML хоть и являются очень похожими языками, но всё же различаются. Из этого вытекают специфические требования к разметке: например, все HTML теги должны быть закрыты.
Simple HTML DOM
Simple HTML DOM — PHP-библиотека, позволяющая парсить HTML-код с помощью удобных jQuery-подобных селекторов.
Она лишена главного недостатка XPath — библиотека умеет работать даже с невалидным HTML-кодом, что значительно упрощает работу. Вы также забудете о проблемах с кодировкой: все преобразования выполняются автоматически.
Как и JQuery, Simple HTML DOM умеет искать и фильтровать вложенные элементы, обращаться к их атрибутам и даже выбирать отдельные логические элементы кода, например, комментарии.
Несмотря на не самую высокую производительность, по сравнению с другими вариантами, Simple HTML DOM имеет самое большое русскоязычное комьюнити и наибольшую распространенность в рунете — для новичков это делает написание кода с её использованием значительно проще.
phpQuery
Как и Simple HTML DOM, phpQuery является PHP вариантом JQuery, но на этот раз более похожим на своего «старшего javascript-брата».
Портировано почти всё, что есть в JS-фреймворке: поддержка селекторов, атрибутов, манипуляций, обхода, плагинов, событий (в том числе имитации кликов и т.д.) и даже AJAX. Использовать можно как через PHP, так и через командную строку в виде отдельного приложения.
Более того, согласно нашим бенчмаркам, phpQuery оказался в 8 (!) раз быстрее Simple HTML DOM.
Вот небольшой пример на phpQuery, в котором происходит обработка заранее выбранных элементов списка ( li ):
Подробную документацию и больше примеров найдете на официальной странице в Google Code.
htmlSQL
htmlSQL — экспериментальная PHP библиотека, позволяющая манипулировать HTML-разметкой посредством SQL-подобных запросов.
Простейший пример, извлекающий атрибуты href и title всех ссылок (элементы a ) с классом list :
Как и с обычными mysql_ функциями, воспользовавшись методами fetch_array() или fetch_objects(), мы можем получить результат выполнения данного запроса в виде привычного ассоциативного массива или объекта.
Стоит также упомянуть о высоком быстродействии htmlSQL: часто она справляется в несколько раз быстрее phpQuery или того же Simple HTML DOM.
Тем не менее, для сложных задач вам может не хватить функциональности, а разработка библиотеки давно прекращена. Но даже несмотря на это, она всё ещё представляет интерес для веб-разработчиков: в ряде случаев значительно удобнее использовать язык SQL вместо CSS-селекторов. Особенно когда вы не знаете, что такое CSS-селекторы 😉
Вывод
В своем мини-исследовании мы пришли к выводу, что в большинстве случаев для парсинга лучше использовать библиотеку phpQuery: она быстрая, функциональная и современная.
С другой стороны, для совсем простых задач логично было бы использовать стандартные модули PHP, такие как XPath, DOM или, на крайний случай, регулярные выражения.
Что-то ещё?
Для PHP существуют ещё десятки разнообразных библиотек и инструментов для парсинга, но в этой статье мы рассмотрели только самые интересные, функциональные и производительные.
Подробнее о других способах парсинга средствами PHP можно прочитать в соответствующей теме на StackOverflow.
Если вы не используете PHP, то можете ознакомится с кратким списком похожих инструментов для других языков программирования:
Ассемблер: AsmXml.
Хинт для программистов: если зарегистрируетесь на соревнования Huawei Cup, то бесплатно получите доступ к онлайн-школе для участников. Можно прокачаться по разным навыкам и выиграть призы в самом соревновании.
Перейти к регистрации
Парсинг сайтов регулярными выражениями PHP
Парсинг сайтов
Для того, чтобы спарсить страницу сайта (то есть разобрать ее HTML код), ее для начала следует получить. А затем уже полученный код можно разобрать с помощью регулярных выражений и, либо каким-то образом его проанализировать, либо сохранить в базу данных, либо и то, и другое.
Получение страниц сайтов с помощью file_get_contents
Итак, давайте для примера получим главную страницу моего сайта и выведем ее на экран (сделайте это):
Давайте теперь выведем не страницу сайта, а ее исходный код. Запишем его в переменную $str и выведем на экран с помощью var_dump:
Должна быть включена директива allow_url_fopen http://php.net/manual/ru/filesystem.configuration.php#ini.allow-url-fopen
Парсинг с помощью регулярных выражений
Подводные камни
Таким образом я рекомендую вам всегда работать с этими двумя модификаторами, вот так:
Попробуем разобрать теги
Пусть мы каким-то образом (например, через file_get_contents) получили HTML код сайта. Вот он:
Итак, получим содержимое тега (в переменной $str хранится HTML код, который мы разбираем):
В общем-то ничего сложного нет, только обратите внимание на то, что как уголки тегов, так и слеш от закрывающего тега экранировать не надо (последнее верно, если ограничителем регулярки является не слеш /, а, например, решетка #, как у нас сейчас).
Итак, перепишем регулярку с учетом атрибутов:
Поправим эту проблему и вернемся к дальнейшему обсуждению:
Получение блока по id
Давайте рассмотрим следующий код:
Напишем регулярку, которая получит содержимое блока с id, равным content.
Итак, попытка номер один (не совсем корректная):
Давайте поправим нашу регулярку:
Обратите внимание на то, что вокруг равно пробелы могут быть, а могут и не быть, поэтому там стоит оператор повторения звездочка *.
Обратите внимание, что после
А вот регулярка title\s*?=\s*?([«\’])(.+?)\1 будет корректно обрабатывать
Проблема вложенных блоков
Наша регулярка вытянет только
Вытягиваем заданные блоки
Получение href ссылок
Ссылки из блока
Получение элементов по классу
Кодировка документа
В этом случае следует воспользоваться функцией iconv, которая перекодирует текст из устаревшего windows-1251 в современный utf-8:
Что вам делать дальше:
Приступайте к решению задач по следующей ссылке: задачи к уроку.
Парсер на PHP-коленке или как я свою музыкальную коллекцию пополнял
А началось все с чего? А началось все одним домашним, зимним, субботним вечером… Ну и конечно же с проблемы, для которой искалось решение)
На днях, по своей же глупости, я потерял навеки всю свою коллекцию музыки (Я — DJ, музыкант). Было очень жалко, ведь коллекция была идеально рассортирована, проанализирована на битрейт, тональность и т.д.
Смирился, думаю ладно, буду заново качать все треки. Качать буду с сайта promodj.com
Почему «промоднище», а не какой нибудь soundcloud? Первая причина — я сижу на этом сайте гораздо чаще, чем на остальных музыкальных порталах. Вторая причина — там есть очень удобный поиск с фильтрами а-ля «Топовое за январь 2017 с качеством 320kbps, длиной не больше 10 минут и не является мэшапом».
Как вы сами понимаете, совсем скоро мне настое… надоело нажимать руками кнопочку «Скачать». И тут и началось самое интересное).
Задача первая: определить регулярку для ссылки!
Про то, как посмотреть исходный код элемента страницы, я говорить не буду. Не думаю что здесь есть люди настолько глупенькие.
Сообственно DIV для каждой композиции выглядит вот так:
А вот код для даного DIV’а:
Интересует нас, на первый взгляд, данная строка:
Именно под эту строку я и начал разрабатывать регулярное выражение, которое будет вырезать ссылку на аудио-файл. Но это оказалось неверное решение!
Кроме листа запроса с треками, на promodj.com так же есть реклама музыкальных треков. И в каждой такой рекламе точно такой же ссылкой выводится кнопка «Скачать». Это значит, что кроме нужных мне треков, так же будут скачиваться композиции из рекламы.
Вначале я даже хотел плюнуть, ну и хрен с ним, будет у меня в подарок еще овердохера рекламных треков. Но, посчитав, сколько лишнего рекламного мусора у меня будет, я резко отказался от этой идеи.
Далее меня напрягло то, как называется класс ссылки. «bigdownloadbutton», возможно это и разработчик сайта так красиво все называет в свой жизни, а возможно есть кнопочка поменьше…
Так и есть! Вспомнив, про маленькую, неприметную кнопку загрузки под треком, я начал искать ее код для парсинга. Вот он:
Судя по названия класса, сразу понятно, что этот элемент изначально предназначался как счетчик скачиваний. Но нас интересует другое — внутри него есть ссылка. На всякий случай проверил как визуально, так и с помощью поиска в коде, есть ли еще элементы с таким классом на странице. Нету! Идеально.
Пара-тройка минут ушла на составление простейшей регулярки:
С помощью данной регулярки мы получим все ссылки на странице, находящиеся внутри SPAN’а «downloads_count». Отлично! Второй этап.
Задача вторая: сгенерировать ссылки на страницы для парсинга
Так как пополнять свою коллекцию я хотел треками разных жанров, да еще и только топовых, я сконструировал для себя точную цель.
Для каждого, из интересных мне стилей, парсить первые 2 страницы, выдаваемых по запросам с фильтрами: «Сортировка по рейтингу, за 2017 год, за каждый месяц, с качеством не менее 320kbps, длинной трека не более 10 минут, не является мэшапом» (Мэшапы это стремно, фу, хочу авторскую музыку!).
Теперь мне необходимо было сгенерировать несколько ссылок на страницы по заданным критериям.
При обычном запросе из браузера мы имеем следующий URL и параметры:
Догадаться, что к чему, не сложно. Тем более все параметры передаются с помощью метода GET. Дело остается за малым — сгенерировать несколько урлов по моему запросу! Я решил не заниматься извращением и не печатать все эти урлы вручную.
Мы же прогеры! Давайте напишем скрипт, который нам сгенерирует эти URL’ы. Да без б:
И на выходе мы получаем аккуратненькие ссылочки для парсинга!
Задача третья: парсинг ссылок
Ну-с, ссылки страниц для парсинга у нас есть. Регулярку мы уже сделали. Давайте парсить господа!
А как парсить? Чем парсить? Конечно же чистым PHP! Мы ведь у мамы КУЛХАЦКЕРЫ и ТЫЖПРОГРАММИСТЫ!
Переходим во все тяжкие. Поиск подстроки по регулярному выражению в PHP реализовать достаточно просто. Для этого есть функция preg_match_all(). Но нам нужно для начала получить HTML код страницы, что бы его парсить.
И нет, мы не будем использовать DOM, и даже не будем использовать для этого Curl. Мы будем юзать функцию стандартного PHP — file_get_contents()! Вдруг кто не знал, с помощью данной функции можно читать не только локальный текстовый файл, но и скомпилированый сервером HTML код, если подать в аргументе URL!
ЦЕЛЫХ 8 СТРОК занимает наш цикл парсинга с учетом форматирования. ЦЕЛЫХ 8 СТРОК, КАРЛ.
Объяснить, что здесь к чему? Ну на всякий случай обьясню. В цикл кидаем массив URL’ов, сгенерированных ранее, затем для каждого URL’а получаем его HTML код с помощью file_get_contents(). Далее имеем строку с регулярным выражением, полученным на первом шаге.
«АЛИЛУЯ!» случайно проорал я вслух. А потом пошел курить и думать, что делать дальше то…
Задача четвертая: скачивание множества файлов
По простейшим расчетам, на выходе я получал 11(кол-во стилей для поиска)*20(кол-во результатов на странице поиска)*12(месяцы)*2(страниц поиска) = 5,280 аудиофайлов. Плюс нужно учитывать, что загрузка каждой страницы для парсинга так же занимает время, да и сама работа регулярки тоже занимает время.
Сначала было решение все таки использовать Curl для скачивания файлов. Но минуту спустя я снова улыбался от радости).
Есть прекрасная программа — Download Master (не реклама)! Последний раз я ее видел в далеком 2010 году, времена, когда я лишь познавал uTorrent.
Фишка программы в том, что она может принимать на вход список URL файлов для закачки.
Вторая проблема. Если я сейчас возьму все ссылки разом, пихну их в Download Master и уйду пить чай/курить/спать, то в итоге у меня вся музыка будет в одной папке. Ну то есть не рассортированая по стилям.
Решение простое и логическое — буду парсить по очереди каждый стиль и кидать в Download Master. Только я начал копировать полученные URL’ы, как вежливый загрузчик предложил мне начать их загрузку!
Более того, сразу же спросил, куда сохранить эти файлы и применять ли те же настройки для всех остальных файлов в списке!
Ну и теперь я наконец то расплылся в улыбке и пошел пить чай/курить/спать!)
Если кому-нибудь это будет необходимо — вот полный PHP код для парсера:
UPD: Если кому-нибудь интересно — на выходе я получил 3350+ композиций общим весом 36,5 Гб. Не думаю, что справился бы просто руками))
Парсер на PHP – это просто
Вебмастеры часто сталкиваются с такой проблемой, когда нужно взять с какого-либо сайта определенную информацию и перенести ее на другой. Можно сначала сохранить информацию на промежуточный носитель, а уже с него загрузить куда-либо, но подобный подход не всегда удобен. В некоторых случаях гораздо быстрее залить парсер на сам сайт, поддерживающий PHP и запустить его удаленно, чтобы он автоматически спарсил информацию и загрузил ее в базу данных ресурса.
Среди уже готовых решений имеются популярные вроде Content Downloader и ZennoPoster, они конечно очень удобны и понятны любому человеку, даже незнакомому с программированием, однако имеют некоторые минусы. К примеру, они платные и не обладают достаточной гибкостью, которую можно вдохнуть в обычный php скрипт. Тем более, что разработка сложного парсера на них нисколько не уступает по времени написанию аналога на php.
Еще есть такая бесплатная вещь как iMacros – скриптовый язык, который может эмулировать действия пользователя в браузере, но тоже не везде такой подход работает лучшим образом.
Многие думают, что программирование, и уж тем более написание парсеров, – очень сложное занятие. На самом деле php – один из самых простых языков, изучить который можно на достаточном уровне за пару недель или месяц.
Парсеры тоже просты в написании, именно поэтому начинающие программисты пишут именно их, чтобы освоить язык.
Первое, что приходит на ум человеку, который решил написать подобный скрипт, — нужно использовать функции для работы со строками (strpos, substr и аналогичные) или регулярные выражения. Это совершенно верно, однако есть один нюанс. Если парсеров нужно будет писать много, то придется разрабатывать свою библиотеку, чтобы не переписывать сто раз одни и те же конструкции, но на это уйдет тонна времени, а учитывая то, что уже существуют аналогичные библиотеки, такое занятие и вовсе оказывается бессмысленным.
Идеальным вариантом для новичка станет изучение библиотеки PHP Simple HTML DOM Parser. Как можно догадаться из названия, она очень проста в освоении. Рассмотрим базовый код:
Более подробно можно посмотреть на сайте:
simplehtmldom.sourceforge.net
Библиотека, как уже было сказано выше, очень проста и лучше всего подходит для начинающего программиста, плюс ко всему она работает достаточно быстро и не сильно требовательна к ресурсам сервера.
Есть у этой библиотеки один минус – далеко не все страницы ей оказываются по зубам. Если какой-либо элемент не отображается, но точно известно, что он там есть, лучше воспользоваться библиотекой DOM (Document Object Model). Она хороша во всем, кроме скорости разработки и понятности.