Как сделать парсер на php

Добавил пользователь Алексей Ф.
Обновлено: 05.10.2024

Рассмотрим создание парсера на PHP с использованием языка запросов xPath.

Листинг файла index.php.

Данный файл располагаем в корне сайта. Здесь же создаем директорию с именем data. В этот каталог будут сохраняться все данные, собранные парсером.

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

Итак, при помощи встроенной в PHP функции libxml_use_internal_errors(true) мы включаем обработку ошибок пользователем.

Для удобства работы объявлено несколько важных переменных:

Функция function curlGetContents($pageUrl, $baseUrl, $pauseTime = 4, $retry = true) читает содержимое удаленной страницы в строку. В ней нет ничего особенного. Используется встроенная функция curl_setopt(). Более детально можно прочитать здесь.

Кстати, если происходит ошибка, то предпринимается еще одна попытка получения данных с сайта. Если же все-таки не получается получить данные по определенной ссылке, то создается файл с ошибками errors.txt.

В строке $doc = new DOMDocument(); происходит инициализация экземпляра класса DOMDocument, который представляет все содержимое HTML- или XML-документа. А с помощью метода loadHTML() мы загружаем HTML из строки. Далее в строке new DOMXpath($doc) создаем экземпляр класса DOMXPath, который поддерживает язык запросов XPath 1.0. Подробнее про все методы и их особенности можно ознакомиться по ссылкам выше.

Переменная $startPage содержит номер начальной страницы, т.е. самой первой страницы на сайте. Аналогично, переменная $endPage содержит номер самой последней возможной страницы на сайте (имеется в виду номер страницы, который доступен в контексте по адресу, указанному в переменной $url).

Далее операция получения данных с сайта работает до тех пор, пока не будет достигнута последняя страница.

Внутри цикла while каждую итерацию создается новый экземпляр DOMDocument, загружается HTML из строки и создается новый экземпляр класса DOMXpath. Большинство участков кода имеют комментарии и пояснения.

Обратим внимание на функцию function parseNumberLastPage(DOMXpath $xPath) — извлекает последний номер страницы. В данном случае анализируются ссылки на странице в самом низу сайта и получаем последнюю страницу.

Для получения информации о режиссере пришлось создать отдельную функцию function parseGetProducer(DOMXpath $xPath). Все это связано с тем, что в некоторых записях (анонсах аниме) отсутствовал режиссер. В результате происходило смещение и информация не правильно собиралась с сайта. В ней сначала анализируется страница и собирается массив с информацией о режиссере (если режиссера нет, то заполняется пустой строкой). Затем происходит сравнение, в котором проверяется, если узел содержит автора с пустой строкой, то аналогично заполняем пустой строкой, иначе берем информацию о режиссере из другого массива.

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

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

Отмечу, что это лишь демонстрация возможностей работы с xPath в PHP. Я не стал заморачиваться с проверкой различных ошибок, т.к. задача была в максимально короткий срок получить необходимые данные. Вы же в свою очередь можете доработать скрипт под себя и даже сделать его еще лучше. Таким образом можно парсить практически любой сайт. Для этого достаточно ознакомиться с синтаксисом xPath.

Полезные ссылки

Парсер сайта на PHP (исходный код на GitHub)

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

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

Парсер на php - раз плюнуть!

Скажу сразу, что вам потребуется знание основ программирования на php. В противном случае почитайте теории. Я не буду рассказывать азы, а сразу полезу показывать всё на практике.

Шаг 1 - PHP Simple HTML DOM Parser

Для парсинга сайтов мы будем использовать простецкую библиотечку под названием PHP Simple HTML DOM Parser, которую вы сможете скачать на сайте разработчика. Данный класс поможет вам работать с DOM-моделью страницы (дерево документа). Т.е. главная идея нашей будущей программы будет состоять из следующих пунктов:

  1. Скачиваем нужную страницу сайта
  2. Разбираем её по элементы (div, table, img и прочее)
  3. В соответствии с логикой получим определённые данные.

Давайте же начнём написание нашего php парсера сайтов.

Для начала подключим нашу библиотеку с помощью следующей строки кода:

Шаг 2 - Скачиваем страничку

На этом этапе мы смогли подключить файл к проекту и теперь пришла пора скачать страничку для парсинга.

В нашей библе есть две функции для получения удалённой страницы сайта. Вот эти функции

    str_get_htm() - получает в качестве параметров обычную строку. Это полезно, если вы стянули страничку с помощью CURL или метода file_get_contents. Пример использования:

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

Шаг 3 - Ищем нужные элементы на странице

После получения DOM-модели мы можем приступить непосредственно к поиску нужного элемента-блока в полученном коде.

Большая часть функций поиска использует метод find(selector, [index]). Если не указывать индекс, то функция возвратит массив всех полученных элементов. В противном случае метод вернёт элемент с номером [index].

Давайте же приведу вам первый пример. Спарсим мою страничку и найдём все картинки.

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

Шаг 4 - Параметры поиска

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

Также вы можете производить поиск по наличию атрибутов у искомого элемента. Пример:

Замечу, что у каждого вложенного тега так же есть возможность поиска!

Есть много вариантов поиска по атрибутам. Перечислять не стану, для более полного руководства прошу пройти на сайт разработчиков :)

Обычный текст, без тегов и прочего, можно искать так find('text'). Комментарии аналогично find('comment').

Шаг 5 - Поля элементов

Каждый найденный элемент имеет несколько структур:

  1. $seo->tag Прочитает или запишет имя тега искомого элемента.
  2. $seo->outertext Прочитает или запишет всю HTML-структуру элемента с ним включительно.
  3. $seo->innertext Прочитает или запишет внутреннюю HTML-структуру элемента.
  4. $seo->plaintext Прочитает или запишет обычный текст в элементе. Запись в данное поле ничего не поменяет, хоть возможность изменения как бы присутствует.

Эта возможность очень просто позволяет бегать по DOM-дереву и перебирать его в зависимости от ваших нужд.

Если вы захотите затереть какой-либо элемент из дерева, то просто обнулить значение outertext, т.е. $div->outertext = "" ; Можно поэксперементировать с удалением элементов.

P.S. Я обнаружил проблему с кодировками при очистке и всяческими манипуляциями с полем innertext . Пришлось использовать outertext и затем с помощью функции strip_tags удалял ненужные теги.

Шаг 6 - Дочерние элементы

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

  1. $seo->children ( [int $index] ) Возвращает N-ый дочерний элемент, иначе возвращает массив, состоящий из всех дочерних элементов.
  2. $seo->parent() Возвращает родительский элемент искомого элемента.
  3. $seo->first_child() Возвращает первый дочерний элемент искомого элемента, или NULL, если результат пустой
  4. $seo->last_child() Возвращает последний дочерний элемент искомого элемента, или null, если результат пустой
  5. $seo->next_sibling() Возвращает следующий родственный элемент искомого элемента, или null, если результат пустой
  6. $seo->prev_sibling() Возвращает предыдущий родственный элемент искомого элемента, или null, если результат пустой

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

Шаг 7 - Практика

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

parser-fajla-php

Приветствую вас, друзья! 🙂

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

Ничего в этом сложного нет, но иногда глаза разбегаются от обилия вариантов, предоставляемых средствами серверных языков. Если говорить конкретно о PHP, на котором я сейчас программирую, то с помощью его функций можно считывать содержимое файлов и построчно, и целиком в строку, и в массив, причём для последнего варианта существует ещё несколько способов… Вот такие пироги 🙂

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

Создаём PHP парсер файла — начальные условия

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

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

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

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

Должен сказать, он получился весьма увесистым: 352 Кбайта и 8223 строки текста, в каждой из которых содержался идентификатор пользователя и его телефон в формате id_пользователя:номер_телефона.

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

Мой проект был реализован на PHP фреймворке Yii, следовательно в дальнейших примерах кода вы встретите элементы его API для работы с БД, в частности, поэтому не пугайтесь 🙂

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

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

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

Чтение файла в PHP построчно с помощью fgets()

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

В итоге, PHP парсер файла, реализующий данный алгоритм, у меня принял следующий вид:

Немного расшифрую свою писанину, если у кого-то возникнут сложности в понимании.

В самом начале, переменной $filename присваивается значение имени файла, который будет парситься, с полным путём к нему. Далее следуют PHP проверка существования файла и читаем ли он с помощью функций file_exists() и is_readable() соответственно.

Если файл открыть получилось, то мы проходимся по всем его строкам в цикле, пока файл не закончится, и, если строка не пустая, разделяем её по символу двоеточия функцией explode().

Затем проверяем, что id пользователя и его телефон не пустые, ищем пользователя в БД по айдишнику и, если таковой существует, то обновляем ему номер телефона, убрав из значения номера предварительно символы переноса и начала новой строки.

Ну, и ещё я использовал PHP функции strtolower() и strtoupper() для проверки существования в БД пользователя с идентификаторами, которые могли быть прописаны в различных регистрах, т.к. они в моём случае состояли из символов и цифр.

PHP парсинг файла в массив с помощью file()

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

Код данного варианта PHP парсера файла получился следующий:

Как видите, от предыдущего способа чтения файла в PHP данный отличается только своим началом, где файл открывается и сразу же считывается функцией file() вместо связки fopen() + fgets(), как ранее.

Далее код такой же.

PHP чтение файла в переменную с помощью fread()

Ещё одной функцией PHP для разбора файла является fread(), с помощью которой можно читать различные фрагменты файла указанной длины. Чтобы прочитать файл в PHP целиком, в качестве размера фрагмента я указал размер файла, полученный с помощью функции filesize():

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

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

А дальше всё идёт по накатанной 🙂

Создаём PHP парсер файла на базе file_get_contents()

Ну, и напоследок, я решил реализовать PHP парсинг файла с помощью функции file_get_contents(), которая, как раз и предназначена для чтения файла целиком в строку, т.е. работает, практически, как fread($fp, filesize($filename)).

За тем лишь исключением, что file_get_contents() самостоятельно открывает файл и считывает его, в то время как для использования fread() нужно было предварительно открыть файл через fopen() и получить его указатель для дальнейшего использования.

В целом, код PHP парсера файла на базе file_get_contents() будет практически как и в предыдущем случае:

На этом всё. Пришло время подвести итоги производительности всех перечисленных вариантов и выяснить, какой же PHP парсер файла оказался самым оптимальным для дальнейшего использования.

Какой способ обработки файлов в PHP является оптимальным?

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

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

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

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

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

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

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

Экспериментfgets()file()fread()file_get_contents()
19,1479,72210,5392,008
28,9509,0069,4951,733
38,8218,8459,2071,642
48,7178,8768,9311,758
59,0109,0918,7031,635
69,1108,6409,7121,633
79,0749,6269,131,645
88,8869,2049,0481,701
98,6678,9189,4381,713
108,8529,1979,5371,567
Среднее8,9239,1139,3741,704

Как видите, помимо значений времени выполнения скрипта в каждом из 10 экспериментов, я решил подсчитать среднюю температуру по больнице 🙂

А именно, арифметическое среднее время работы каждого PHP парсера файла, чтобы можно было выявить лидера.

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

Все остальные варианты PHP парсеров файлов работают примерно с одинаковой скоростью.

Почему именно он обогнал своих конкурентов я, если честно, не имею ни малейшего понятия. Могу лишь предположить, что операция чтения файла в строку с помощью file_get_contents() требует меньше ресурсов, чем формирование готового массива строк с помощью file().

А превосходство над fgets() и fread() можно списать на то, что перед их использованием требуется открытие файла с помощью fopen(), на что требуется время.

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

Разбор файла в PHP — выводы

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

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

Следовательно, на время выполнения операций, прописанных в PHP коде, может влиять целый ряд факторов, среди которых основным является нагруженность ядра в момент работы PHP приложения.

Я это особенно ощутил во время проведения опытов, когда один и тот же PHP парсер файла отработал за 9, затем за 12, а потом снова за 9 секунд на трёх последовательных итерациях из-за банального запуска проводника Windows во время второго случая, который, естественно, тоже требует серверных ресурсов.

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

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

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

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

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

Пишите ваши отзывы, как положительные, так и отрицательные в комментариях под статьёй — мне необходимо любое ваше мнение для дальнейшего развития 🙂

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


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

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

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

После генерации кода получится примерно такая строка:

Это и будет наш результат парсинга с помощью PHP. Код будут полезен пользователям, которые имеют свой онлайн кинотеатр и ищут способ украсть ссылки на видео uppod или не знают, как написать парсер на php.

Второй пример показывает, как небольшим кодом php вытащить необходимый тест со страницы.

Результатом будет выведенный заголовок страницы:

Таких парсеров на php можно написать большое количество, с различными настройками, но эти два можно назвать универсальными. В них вам придётся изменить несколько строк, и они будут работать. А если нужны качественные фотографии с Shutterstock почти бесплатно — читайте мою статью об этом.

Читайте также: