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

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

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

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

Разделение потоков

Пусть скрипт, который осуществляет парсинг, называется parser.php. Мы можем запускать его с разными GET параметрами, разделяя разные потоки. Например: parser.php?num=1, parser.php?num=2 и так далее.

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

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

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

Самое простое, что можно сделать, это разделить потоки по главному меню сайта: каждый пункт - отдельный поток.

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

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

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

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

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

Особенность браузеров

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

Это связано с устройством браузеров - они разрешают одновременно для одного сайта обрабатывать 6-10 запросов и ничего с этим не сделать. Пока эти запросы не будут выполнены - остальные ожидают.

Где это может вылезти - к примеру у вас на сайте 20 CSS файлов. В этом случае время загрузки существенно увеличится, так как они будут грузится по 6-10 файлов, а остальные будут ожидать. Поэтому на реальных сайтах CSS файлы сливают в один, а картинки иконок сливают в спрайты - ноги растут отсюда.

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

Автоматически запускаем потоки

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

Если попытаться, например, обратиться к 50 страницам через PHP, например, через file_get_contents или через CURL, то 50 потоков запустить не получится, так как PHP скрипт будет ждать окончания загрузки file_get_contents.

Нужно нечто асинхронное, например сокеты или AJAX.

Давайте откроем 50 потоков с помощью AJAX. Будем каждый поток запускать с таймаутом так, чтобы каждый поток запускался на секунду позже предыдущего - в этом случае мы обойдем ограничение на открытие 6-10 страниц в браузере:

var count = 50;
var url = 'http://paser.php';

for (var i = 1; i <= count; i++) {
	var queryUrl = url + '?num='+i;
	$.ajax({
		url: queryUrl,
		timeout: 1000*i, //задержка обязательна
	});
}

Как это работает: первый поток имеет задержку timeout: 1000 милисекунд - одну секунду. Если страница не ответит за это время (а она не ответит, так как парсинг длится дольше), то загрузка оборвется. Второй поток имеет задержку timeout: 1000*2 = 2000 - 2 секунды. Ну и так далее.

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

Настройки сервера

Тут будет информация о добавлении дополнительных ресурсов PHP серверу при парсинге на локальном компьютере. Добавлю попозже.

Многопоточные запросы CURL

Изучите это, это и это.

Запуск нескольких процессов на PHP

Добавлю попозже.

Настоящие потоки на PHP

Изучите это: настоящие потоки, модуль php, еще.