Пишем php парсер сайтов с нуля
Содержание:
- Шаг 2 – Основы HTML парсинга
- 10. Мэрилин Монро
- Написание класса парсера
- Private Tunnel
- Системный реестр
- Генри Форд — Моя жизнь, мои достижения
- Version 2.0.0-alpha1 (2015-07-14)
- Camel naming convertions
- Custom Parsers/Formatters
- Licensing
- Running Parse Server
- Сервис яндекс картинки. Инструкция по поиску и скачиванию картинок
- Парсеры сайтов по способу доступа к интерфейсу
- Tips
- Enable result caching
- Делаем запросы
- About WhichBrowser
- Реализация парсера на PHP
- Режим навигации
- Google Фото – лучшее бесплатное хранилище для фото и видео
- Разбор файла в PHP — выводы
Шаг 2 – Основы HTML парсинга
Эта библиотека, очень проста в использовании, но все же,
необходимо разобрать некоторые основы, перед тем как ее использовать.
Загрузка HTML
$html = new simple_html_dom(); // Load from a string $html->load('<html><body><p>Hello World!</p><p>We're here</p></body></html>'); // Load a file $html->load_file('http://sitear.ru/');
Все просто, вы можете создать объект, загружая HTML из
строки. Или, загрузить HTML код из файла. Загрузить файл вы можете по URL адресу, или
с вашей локальной файловой системы (сервера).
Важно помнить: Метод
load_file(), работает на использовании PHP функции file_get_contents. Если в
вашем файле php.ini, параметр allow_url_fopen
не установлен как true,
вы не сможете получать HTML файлы по удаленному адресу
Но, вы сможете загрузить эти
файлы, используя библиотеку CURL. Далее, прочитать содержимое, используя метод load().
Получение доступа к HTML DOM объектам
Предположим у нас уже есть DOM объект,
структурой, как на картинке выше. Вы можете начать работать с ним, используя
метод find(), и создавая коллекции. Коллекции – это группы объектов, найденные
с помощью селекторов – синтаксис в чем-то схож с jQuery.
<html> <body> <p>Hello World!</p> <p>We're Here.</p> </body> </html>
Используя этот пример HTML кода, мы узнаем, как получить доступ
к информации заключенной во втором параграфе (p). Также, мы изменим полученную
информацию и выведем результат на дисплей.
// создание объекта парсера и получение HTML include('simple_html_dom.php'); $html = new simple_html_dom(); $html->load("<html><body><p>Hello World!</p><p>We're here</p></body></html>"); // получение массивов параграфов $element = $html->find("p"); // изменение информации внутри параграфа $element->innertext .= " and we're here to stay."; // вывод echo $html->save();
Как видите реализовать PHP парсинг документа HTML, очень даже просто, используя simple HTML DOM библиотеку.
В принципе, в этом куске PHP кода, все можно понять интуитивно, но если вы в чем-то
сомневаетесь, мы рассмотрим код.
Линия 2-4:
подключаем библиотеку, создаем объект класса и загружаем HTML код из
строки.
Линия 7: С
помощью данной строки, находим все <p> теги в HTML коде,
и сохраняем в переменной в виде массива. Первый параграф будет иметь индекс 0,
остальные параграфы будут индексированы соответственно 1,2,3…
Линия 10:
Получаем содержимое второго параграфа в нашей коллекции. Его индекс будет 1.
Также мы вносим изменения в текст с помощью атрибута innertext. Атрибут innertext, меняет все содержимое внутри
указанного тега. Также мы сможем
изменить сам тег с помощью атрибута outertext.
Давайте добавим еще одну строку PHP кода, с помощью которой мы назначим
класс стиля нашему параграфу.
$element->class = "class_name"; echo $html->save();
Результатом выполнения нашего кода будет следующий HTML документ:
<html> <body> <p>Hello World!</p> <p class="class_name">We're here and we're here to stay.</p> </body> </html>
Другие селекторы
Ниже приведены другие примеры селекторов. Если вы
использовали jQuery, то
в библиотеке simple html dom синтаксис немножко схожий.
// получить первый элемент с id="foo" $single = $html->find('#foo', 0); // получает при парсинге все элементы с классом class="foo" $collection = $html->find('.foo'); // получает все теги <a> при парсинге htmlдокумента $collection = $html->find('a'); // получает все теги <a>, которые помещены в тег <h1> $collection = $html->find('h1 a'); // получает все изображения с title='himom' $collection = $html->find('img');
Использование первого селектора при php парсинге html документа,
очень простое и понятное. Его уникальность в том что он возвращает только один html элемент,
в отличии от других, которые возвращают массив (коллекцию). Вторым параметром (0),
мы указываем, что нам необходим только первый элемент нашей коллекции. Надеюсь,
вам понятны все варианты селекторов библиотеки simple HTML DOM, если вы чего-то не
поняли, попробуйте метод научного эксперимента. Если даже он не помог,
обратитесь в комментарии к статье.
Документация библиотеки
simple HTML DOM
Полнейшую документацию по использованию библиотеки simple HTML DOM вы
сможете найти по этому адресу:
http://simplehtmldom.sourceforge.net/manual.htm
Просто предоставлю вам иллюстрацию, которая показывает
возможные свойства выбранного HTML DOM элемента.
10. Мэрилин Монро
Написание класса парсера
Теперь пришло время собрать все части спаршеных данных вместе. Логина по выполнению запроса может быть помещена в отдельную функцию/класс, а так же, нужно добавить более гибкую функциональность, добавив возможность указания разных URL-адресов. Для этого, создадим класс
Класс принимает объект как зависимость в свой конструктор. Функциональность этого класса достаточно проста: в нём существует 2 метода: — который принимает массив URL-адресов на фильмы, и , который возвращает массив всех спаршенных данных о фильмах.
Теперь, наконец-то, можем попробовать этот парсер в действии:
В этом коде мы создали объект парсера, передали его методу URL-адреса для парсинга, после чего, запустили обработчик цикла событий. События будут обрабатываться до тех пор, пока все запросы не будут выполнены, и результаты, в которых нуждаемся, не спаршены с HTML-разметки. Как результат, эти запросы выполняются параллельно, потому, итоговое время выполнения скрипта будет равно самому медленному из наших запросов.
Результат будет выглядеть примерно так:
Дальше вы можете продолжать писать парсер как угодно: разделив его на несколько файлов, добавить запись результатов в базу данных. Главная цель этой статьи была показать, как выполнять асинхронные запросы в PHP и обрабатывать ответ с помощью DOM-парсинга.
Private Tunnel
Системный реестр
Чуть сложнее и дольше сменить адрес адаптера вариантом 2 — в системном реестре. Как и диспетчер устройств, открыть его можно несколькими способами. Например, введя команду «regedit» в окне, вызываемом по Win+R.
Откроется редактор реестра, в котором прописаны все параметры уставленной ОС. Работать надо внимательно и аккуратно. Сделать твик (так называют изменение реестра) нужно в ветке HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}.
Тут прописаны все устройства, которые нашла ОС при установке. Их реальные имена, адреса, драйвера и прочая информация. Комбинация цифр в конце отвечает за сетевые устройства в системе.
Надо последовательно проверить папки 0000, 0001… 0003. Параметр «DriverDesc» укажет реальное название сетевой карты.
Найдя раздел нужной, переходим к параметру NetworkAddress.
Значение, указанное в нём, подменяет собой аппаратный адрес устройства. Иногда параметр отсутствует, но его можно создать. На запрос реестра о типе указать «строковый». Указывая его значение, изменяем МАС-адрес компьютера.
После этого нужно перезагрузить сетевой адаптер.
Генри Форд — Моя жизнь, мои достижения
Автобиография великого изобретателя и создателя известнейшей компании в мире. В ней он поделится своим мнением о бизнесе и принципами, которыми он руководствовался при его создании.
Он расскажет о своей непростой жизни, взлетах и падениях, судебных разбирательствах, в которых ему приходилось участвовать, о полном разорении и получении огромного состояния. В книге приведены примеры многих сложных производственных процессов, которые разработал и запустил в собственный бизнес сам автор.
Генри Форд был не только гениальным изобретателем, но и талантливым менеджером. Его секреты управления используются во многих компаниях и по сей день. Эта книга послужит настоящим учебником для начинающих бизнесменов.
Купить электронную книгу в ЛитРес
Купить аудиокнигу в ЛитРес
Купить бумажную книгу в Лабиринте
Version 2.0.0-alpha1 (2015-07-14)
A more detailed description of backwards incompatible changes can be found in the
upgrading guide.
Removed
- Removed support for running on PHP 5.3. It is however still possible to parse PHP 5.2 and PHP 5.3
code while running on a newer version. - Removed legacy class name aliases. This includes the old non-namespaced class names and the old
names for classes that were renamed for PHP 7 compatibility. - Removed support for legacy node format. All nodes must have a method now.
Added
- Added support for remaining PHP 7 features that were not present in 1.x:
- Group use declarations. These are represented using nodes. Furthermore a
attribute was added to to handle mixed group use declarations. - Uniform variable syntax.
- Generalized yield operator.
- Scalar type declarations. These are presented using , , and
as the type. The PHP 5 parser also accepts these, however they’ll be instances there. - Unicode escape sequences.
- Group use declarations. These are represented using nodes. Furthermore a
- Added class, which should be used to create parser instances.
- Added which concatenates two names.
- Added which takes a subslice of a name.
Changed
-
is now an interface, implemented by , and
. The parser will try multiple parsers, until one succeeds. - Token constants are now defined on rather than .
- The , , and methods are
deprecated in favor of and . - The no longer clones nodes by default. The old behavior can be restored by
passing to the constructor. - The constructor for nodes no longer has a default value. E.g. should now
be written as .
This changelog only includes changes from the 2.0 series. For older changes see the
1.x series changelog and the
0.9 series changelog.
Camel naming convertions
You can also call methods with W3C STANDARD camel naming convertions.
Method | Mapping |
---|---|
array $e->getAllAttributes () |
array $e->attr |
string $e->getAttribute ( $name ) |
string $e->attribute |
void $e->setAttribute ( $name, $value ) |
void $value = $e->attribute |
bool $e->hasAttribute ( $name ) |
bool isset($e->attribute) |
void $e->removeAttribute ( $name ) |
void $e->attribute = null |
element $e->getElementById ( $id ) |
mixed $e->find ( «#$id», 0 ) |
mixed $e->getElementsById ( $id ) |
mixed $e->find ( «#$id» ) |
element $e->getElementByTagName ($name ) |
mixed $e->find ( $name, 0 ) |
mixed $e->getElementsByTagName ( $name ) |
mixed $e->find ( $name ) |
element $e->parentNode () |
element $e->parent () |
mixed $e->childNodes ( ) |
mixed $e->children ( ) |
element $e->firstChild () |
element $e->first_child () |
element $e->lastChild () |
element $e->last_child () |
element $e->nextSibling () |
element $e->next_sibling () |
element $e->previousSibling () |
element $e->prev_sibling () |
Author: S.C. Chen (me578022@gmail.com)
Original idea is from Jose Solorzano’s HTML Parser for PHP 4.
Contributions by: Contributions by: Yousuke Kumakura, Vadim Voituk, Antcs
Custom Parsers/Formatters
You can make your own custom parsers/formatters by implementing FormatInterface, the below example demostrates the use of a custom parser/formatter.
use Nathanmac\Utilities\Parser\Formats\FormatInterface; /** * Custom Formatter */ class CustomFormatter implements FormatInterface { /** * Parse Payload Data * * @param string $payload * * @return array * * @throws ParserException */ public function parse($payload) { $payload; // Raw payload data $output = // Process raw payload data to array return $output; // return array parsed data } }
Using the CustomFormatter
use Acme\Formatters\CustomFormatter; $parser = new Parser(); $parsed = $parser->parse('RAW PAYLOAD DATA', new CustomFormatter());
Autodetecting the CustomFormatter
use Acme\Formatters\CustomFormatter; $parser = new Parser(); $parser->registerFormat('application/x-custom-format', 'Acme\Formatters\CustomFormatter'); $parser->payload('application/x-custom-format');
Licensing
Running Parse Server
Before you start make sure you have installed:
Locally
$ npm install -g parse-server mongodb-runner $ mongodb-runner start $ parse-server --appId APPLICATION_ID --masterKey MASTER_KEY --databaseURI mongodb://localhost/test
Note: If installation with fails due to permission problems (),
Inside a Docker container
$ git clone https://github.com/parse-community/parse-server $ cd parse-server $ docker build --tag parse-server . $ docker run --name my-mongo -d mongo
Running the Parse Server Image
$ docker run --name my-parse-server -v config-vol:/parse-server/config -p 1337:1337 --link my-mongo:mongo -d parse-server --appId APPLICATION_ID --masterKey MASTER_KEY --databaseURI mongodb://mongo/test
Note:
You can use any arbitrary string as your application id and master key. These will be used by your clients to authenticate with the Parse Server.
That’s it! You are now running a standalone version of Parse Server on your machine.
Using a remote MongoDB? Pass the parameter when starting . Learn more about configuring Parse Server . For a full list of available options, run .
Saving your first object
$ curl -X POST \ -H "X-Parse-Application-Id: APPLICATION_ID" \ -H "Content-Type: application/json" \ -d '{"score":1337,"playerName":"Sean Plott","cheatMode":false}' \ http://localhost:1337/parse/classes/GameScore
You should get a response similar to this:
{ "objectId": "2ntvSpRGIK", "createdAt": "2016-03-11T23:51:48.050Z" }
You can now retrieve this object directly (make sure to replace with the actual you received when the object was created):
$ curl -X GET \ -H "X-Parse-Application-Id: APPLICATION_ID" \ http://localhost:1337/parse/classes/GameScore/2ntvSpRGIK
// Response { "objectId": "2ntvSpRGIK", "score": 1337, "playerName": "Sean Plott", "cheatMode": false, "updatedAt": "2016-03-11T23:51:48.050Z", "createdAt": "2016-03-11T23:51:48.050Z" }
Keeping tracks of individual object ids is not ideal, however. In most cases you will want to run a query over the collection, like so:
$ curl -X GET \ -H "X-Parse-Application-Id: APPLICATION_ID" \ http://localhost:1337/parse/classes/GameScore
// The response will provide all the matching objects within the `results` array: { "results": }
Сервис яндекс картинки. Инструкция по поиску и скачиванию картинок
Парсеры сайтов по способу доступа к интерфейсу
Облачные парсеры
Облачные сервисы не требуют установки на ПК. Все данные хранятся на серверах разработчиков, вы скачиваете только результат парсинга. Доступ к программному обеспечению осуществляется через веб-интерфейс или по API.
Примеры облачных парсеров с англоязычным интерфейсом:
- http://import.io/,
- Mozenda (есть также ПО для установки на компьютер),
- Octoparce,
- ParseHub.
Примеры облачных парсеров с русскоязычным интерфейсом:
- Xmldatafeed,
- Диггернаут,
- Catalogloader.
У всех сервисов есть бесплатная версия, которая ограничена или периодом использования, или количеством страниц для сканирования.
Программы-парсеры
ПO для парсинга устанавливается на компьютер. В подавляющем большинстве случаев такие парсеры совместимы с ОС Windows. Обладателям mac OS можно запускать их с виртуальных машин. Некоторые программы могут работать со съемных носителей.
Примеры парсеров-программ:
- ParserOK,
- Datacol,
- SEO-парсеры — Screaming Frog, ComparseR, Netpeak Spider и другие.
Tips
Provide clean input strings
If your input string consists of more than just the name and
directly related bits like salutations, suffixes etc.,
any additional parts can easily confuse the parser.
It is therefore recommended to pre-process any non-clean input
to isolate the name before passing it to the parser.
Multi-pass parsing
We have not played with this, but you may be able to improve results
by chaining several parses in sequence. E.g.
$parser = new Parser(); $name = $parser->parse($input); $name = $parser->parse((string) $name); ...
You can even compose your new input string from individual parts of
a previous pass.
Dealing with names in different languages
The parser is primarily built around the patterns of english names
but tries to be compatible with names in other languages. Problems
occur with different salutations, last name prefixes, suffixes etc.
or in some cases even with the parsing order.
To solve problems with salutations, last name prefixes and suffixes
you can create a separate language definition file and inject it when
instantiating the parser, see ‘Setting Languages’ above and compare
the existing language files as examples.
To deal with parsing order you may want to reformat the input string,
e.g. by simply splitting it into words and reversing their order.
You can even let the parser run over the original string and then over
the reversed string and then pick the best results from either of the
two resulting name objects. E.g. the salutation from the one and the
lastname from the other.
The name parser has no in-built language detection. However, you may
already ask the user for their nationality in the same form. If you
do that you may want to narrow the language definition files passed
into the parser to the given language and maybe a fallback like english.
You can also use this information to prepare the input string as outlined
above.
Alternatively, Patrick Schur as a PHP language detection library
that seems to deliver astonishing results. It won’t give you much luck if
you run it over the the name input string only, but if you have any more
text from the person in their actual language, you could use this to detect
the language and then proceed as above.
Gender detection
Gender detection is outside the scope of this project.
Detecting the gender from a name often requires large lists of first
name to gender mappings.
However, you can use this parser to extract salutation, first name and
nick names from the input string and then use these to implement gender
detection using another package (e.g. this one) or service.
Having fun with normalisation
Writing different language files can not only be useful for parsing,
but you can remap the normalised versions of salutations, prefixes and suffixes
to transform them into something totally different.
E.g. you could map to and then output
the parts in appropriate order to build a pipeline that automatically transforms
e.g. into .
Of course, this is a silly and rather contrived example, but you get the
gist.
Of course this can also be used in more useful ways, e.g. to spell out
abbreviated titles, like as etc. .
Enable result caching
WhichBrowser supports PSR-6 compatible cache adapters for caching results between requests. Using a cache is especially useful if you use WhichBrowser on every page of your website and a user visits multiple pages. During the first visit the headers will be parsed and the result will be cached. Upon further visits, the cached results will be used, which is much faster than having to parse the headers again and again.
For example, if you want to enable a memcached based cache you need to install an extra composer package:
And change the call to WhichBrowser/Parser as follows:
$client = new \Memcached(); $client->addServer('localhost', 11211); $pool = new \Cache\Adapter\Memcached\MemcachedCachePool($client); $result = new WhichBrowser\Parser(getallheaders(), );
or
$client = new \Memcached(); $client->addServer('localhost', 11211); $pool = new \Cache\Adapter\Memcached\MemcachedCachePool($client); $result = new WhichBrowser\Parser(); $result->setCache($pool); $result->analyse(getallheaders());
You can also specify after how many seconds a cached result should be discarded. The default value is 900 seconds or 15 minutes. If you think WhichBrowser uses too much memory for caching, you should lower this value. You can do this by setting the option or passing it as a second parameter to the function.
Делаем запросы
Интерфейс класса достаточно простой и прямолинейный. Имена методов соответствуют HTTP-методам, которые он выполняет: GET-метод соответствует методу get(), GET-post(), PUT-put() и т.д. И каждый из этих методов возвращает Promise (если вы знакомы с JavaScript, или ранее работали с ReactPHP, то это не должно вызвать у вас вопросов). Если вы не знаете, что это, то на даном этапе объяснения не имеют большого смысла, дальше будет пример, после которого всё станет понятно.
Для текущей задачи нам будет достаточно одного метода :
В коде выше будет описана анонимная функция, которая после успешного запроса выведет HTML-разметку на экран. Эта функция принимает ответ экземпляра . В этой функции мы можем описать обработчик ответа, который вернёт из этого промиса (Promise) распарсенную информацию, без лишнего HTML-кода.
Как вы можете заметить, алгоритм парсинга достаточно прост:
- Делаем запрос и получаем промис.
- Пишем обработчик этого промиса.
- Парсим нужную информацию внутри этого обработчика.
- Если нужно, повторяем первый шаг.
About WhichBrowser
But why almost completely useless and not completely useless?
Well, there is always an exception to the rule. There are valid reasons to do browser sniffing: to improve the user experience or to gather intelligence about which browsers are used on your website. My website is html5test.com and I wanted to know which score belongs to which browser. And to do that you need a browser sniffing library.
Why is it extremely complicated?
Because everybody lies. Seriously, there is not a single browser that is completely truthful. Almost all browsers say they are Netscape 5 and almost all WebKit browsers say they are based on Gecko. Even Internet Explorer 11 now no longer claims to be IE at all, but instead an unnamed browser that is like Gecko. And it gets worse. That is why it is complicated.
What kind of information does it give?
You get a nice object which has information about the browser, rendering engine, os and device. It gives you names and versions and even device manufacturer and model. And WhichBrowser is pretty tenacious. It gives you info that others don’t. For example:
Android is never mentioned
Despite the useragent header claiming to be a Series40 device, we know it’s actually running the Asha Platform and we also know that OviBrowser has been renamed to Nokia Xpress.
The useragent header looks like Opera 11.10 on Linux, but we know it’s Opera Mini. We can even figure out the real operating system and device model from other headers.
Реализация парсера на PHP
Пожалуй, самый ответственный момент в нашем случае — это поиск донора, то есть сайта, на котором будет появляться интересующая нас информация. Сайт должен работать бесперебойно, выполнять свои обязанности по обновлению информации исправно и текст должен быть открытый (то есть, открыв исходный код страницы в браузере — мы должны видеть там интересующую нас информацию).
Когда сайт-донор и нужная нам страница для парсинга найдена, запоминаем ее урл и переходим к следующему этапу. Создаем в блокноте текстовый файл, например parser.php и помещаем в него следующий код:
<?php //откуда будем парсить информацию $content = file_get_contents('полный урл страницы с http:// с которого будем вырезать информацию'); // Определяем позицию строки, до которой нужно все отрезать $pos = strpos($content, 'здесь кусок кода/текста который размещен перед нужным текстом'); //Отрезаем все, что идет до нужной нам позиции $content = substr($content, $pos); // Точно таким же образом находим позицию конечной строки $pos = strpos($content, 'здесь кусок кода/текста который стоит в конце нужного нам текста'); // Отрезаем нужное количество символов от нулевого $content = substr($content, 0, $pos); //если в тексте встречается текст, который нам не нужен, вырезаем его $content = str_replace('текст который нужно вырезать','', $content); // выводим спарсенный текст. echo $content; echo "вставляем сюда завершающий код"; ?>
Итак, какие-то 8 строчек кода и сторонний контент автоматически публикуется на нашем блоге. Красным цветом в коде обозначены места, которые вы обязательно должны отредактировать, зеленым цветом — при необходимости. Если необходимости нет, то можно просто удалить эти строчки или запретить им обрабатываться (я про строчки в которых зеленый текст) — ставим перед строчкой два слеша — //
Нужны пояснения к кускам кода/текста, которые обрамляют нужный нам текст? Тут все просто, мы должны указать начальную и конечную позицию в тексте, который нужно парсить. Открываем исходную страницу на сайте-доноре и ищем нужный нам текст. Как правило, он будет начинаться с какой нибудь html-разметки, что-то типа этого — <td><p><strong><em> и заканчиваться такой же абракадаброй — например, </td><td> </td><td> </td></tr>. Копируем эти символы в начальную и конечную позиции (2 и 3 красная строчки). Помним, наш скрипт спарсит текст, который находится между этими позициями на сайте.
parser.php готов. Копируем его в корень своего сайта и запускаем в браузере http://мой сайт/parser.php. Если вы все сделали правильно, вы увидите вырезанный/спарсенный кусок текста. Разумеется, он индексируется и не содержит никаких следов того, что вы его забрали с другого сайта.
Режим навигации
Маршруты — это хорошо, но главная фишка навигатора всё же в навигации. Чтобы она появилась, необходимо написать следующее:
CPNavigationSession — класс, с помощью которого можно отображать некоторые UI-элементы, необходимые только в режиме навигации.
Чтобы отобразить манёвр, необходимо:
После чего на экране магнитолы мы получим вот это:
Чтобы обновлять метраж до манёвра, необходимо:
Google Фото – лучшее бесплатное хранилище для фото и видео
Разбор файла в PHP — выводы
Как я уже и говорил в начале, мои опыты не являются безупречными и опираться исключительно на полученные в их ходе результаты не стоит, т.к., несмотря на быстродействие file_get_contents() в моей ситуации, бывают случаи, когда намного удобнее и эффективнее использовать другие приведённые мною PHP парсеры файлов.
Кроме того, не стоит забывать, что PHP сам по себе является синхронным языком программирования, т.е. все серверные операции происходят последовательно без возможности настройки их параллельного выполнения, в том числе, и на разных ядрах серверного процессора.
Следовательно, на время выполнения операций, прописанных в PHP коде, может влиять целый ряд факторов, среди которых основным является нагруженность ядра в момент работы PHP приложения.
Я это особенно ощутил во время проведения опытов, когда один и тот же PHP парсер файла отработал за 9, затем за 12, а потом снова за 9 секунд на трёх последовательных итерациях из-за банального запуска проводника Windows во время второго случая, который, естественно, тоже требует серверных ресурсов.
Учитывая данные особенности, я проводил эксперименты практически одновременно, друг за другом, при одинаковом комплекте запущенных программ, чтобы не распылять ресурсы серверного железа.
Поэтому в дальнейшем, при проведении подобных экспериментов с PHP конструкциями действуйте аналогичным образом, т.к. это, по сути, единственный способ привести эксперименты к равным условиям.
Если же вы будете работать с асинхронными серверными языками (C#, Java) или технологиями (Node.js, например), то, по возможности, для экспериментов создавайте отдельный поток, который будет работать на выделенном ядре процессора.
Ну, а если найти полностью незадействованное ядро не получится (что при уровне современного ПО не удивительно), то вы хотя бы сможете найти самое слабонагруженное или, хотя бы, со статической нагрузкой, которая не меняется во времени.
Надеюсь, что мои наблюдения и рекомендации будут вам полезны, равно как и мои сегодняшние эксперименты с PHP парсерами файлов.
Подытоживая, хочу сказать, что приведённые в статье фрагменты кода могут использоваться не только для парсинга текстовых файлов в PHP, но и отлично подойдут для других форматов, например, для разбора CSV файлов дампа базы данных MySQL.
До новых встреч!