AJAX PHP парсер товаров интернет магазина

AJAX парсер без перезагрузки страниц

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

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

AJAX запросы будем отправлять с помощью JS библиотеки jQuery. Чтобы не таскать за собой лишние файлы подключим её с серверов Google.

В проекте будет 2 файла: index.html c HTML и JS кодом, и ajax-parser.php для формирования и отправки запросов на загрузку страниц с помощью расширения cURL. Специально, что в этом проекте, что в предыдущих, не делю код на разные файлы. Считаю так удобнее в процессе первого знакомства воспринимать общие алгоритмы работы парсеров.

Код index.html:

<!doctype html>
<html>
<head>
	<title>Парсер информации на Gearbest.com</title>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
	<!--meta name="robots" content="noindex,nofollow"-->
	<script src="//ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js" type="text/javascript"></script>
</head>
<body>
<style>
.wrapper {
	max-width: 600px;
	margin: 0 auto;
}
h1 {
	text-align: center;
}
.action_form {
	max-width: 560px;
	margin: 0 auto;
}
.action_form input,
.action_form textarea {
	width: 100%;
}
.action_form textarea {
	min-height: 100px;
}
input[type="text"] {
	font-size: 1em;
	min-height: 36px;
	box-sizing: border-box;
}
input[type="submit"],
input[type="button"] {
	padding: 8px 12px;
	margin: 12px auto;
	font-size: 1.2em;
	font-weight: 400;
	line-height: 1.2em;
	text-decoration: none;
	display: inline-block;
	cursor: pointer;
	border: 2px solid #007700;
	border-radius: 2px;
	background-color: transparent;
	color: #007700;
}
input[type="submit"]:hover,
input[type="button"]:hover {
	background-color: #009900;
	color: #fff;
}
.result {
	border: 1px dotted #000;
	width: 100%;
	height: auto;
	overflow-y: auto;
	margin: 0px auto;
	padding: 10px;
}
.copyright {
	text-align: center;
}
.copyright a {
	color: #000;
}
.copyright a:hover {
	text-decoration: none;
}
.red {
	color: #770000;
}
.green {
	color: #007700;
}
</style>
<div class="wrapper">
	<h1>Парсер цены товара на Gearbest.com</h1>
	<form id="action_form" class="action_form" action="ajax-parser.php" method="post">
		<textarea type="text" id="gearbest_urls" name="gearbest_urls"></textarea>
		<input type="button" id="btn_action_price" name="btn_action_price" value="Цены" />
	</form>
	<div id="result" class="result"></div>
	<div id="errors_block" class="errors_block"></div>
	<div class="copyright">&copy Идея и реализация - <a href="https://seorubl.ru/" target="_blank" title="Записки Предприимчивого Человека" rel="generator">ПЧ</a> // 22.04.2017 г.</div>
</div>
<script type="text/javascript">
	jQuery(document).ready(function($) {
		$('#btn_action_price').click(function() {
			var gearbest_urls = $("#gearbest_urls").val().split('\n');
			var action_url = $("#action_form").attr('action');
			if(gearbest_urls) {
				$('#result').html('');
				$('#errors_block').html('');
				gearbest_urls.forEach(function(gearbest_url, i, arr) {
					var send_data = {gearbest_url: gearbest_url};
					$.ajax({
						type: "POST",
						url:  action_url,
						data: send_data,
						success: function(msg){
							msg = JSON.parse(msg);
							// Показываем результат парсинга
							$('#result').html($('#result').html() + "<p>[" + (i+1) + "] " + "<span>" + msg.price_list.price + "</span> <span>" + msg.price_list.currency + "</span> - <span>" + gearbest_url + "</span></p>").hide().fadeIn('slow');
							msg.errors.forEach(function(error, j, errors_arr) {
								$('#errors_block').html($('#errors_block').html() + "<p>[" + (i+1) + "] " + msg.errors + "</p>");
							});
						}					   
					});
				});
			} else {
				$('#errors_block').html($('#errors_block').html() + "<p>Введите адреса товаров</p>").hide().fadeIn('slow');
			}
			return true;
		});
	});
</script>
</body>
</html>

Код ajax-parser.php

<?php
Error_Reporting(E_ALL & ~E_NOTICE);
mb_internal_encoding("UTF-8");
set_time_limit(0);  // Попытка установить своё время выполнения скрипта
 
/* --- 1 --- Инициализируем переменные для запроса */
    $time_start = time();
    $gearbest_url = "";
    $charset = "UTF-8"; // Исходная кодировка страницы
    $uni_name = date("d-m-Y-H-i-s", time());
/* --- 1.1 --- Переопределяем переменные на основе GET или POST параметров */
    if(isset($_REQUEST['gearbest_url'])) {
        $gearbest_url = trim($_REQUEST['gearbest_url']);
    }
/* --- 1.2 --- Запросы при помощи cURL */
/* --- 1.2.1 --- Загрузка страницы при помощи cURL */
function curl_get_contents($page_url, $base_url, $pause_time, $retry) {
    /*
    $page_url - адрес страницы-источника
    $base_url - адрес страницы для поля REFERER
    $pause_time - пауза между попытками парсинга
    $retry - 0 - не повторять запрос, 1 - повторить запрос при неудаче
    */
    $error_page = array();
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0");   
    curl_setopt($ch, CURLOPT_COOKIEJAR, str_replace("\\", "/", getcwd()).'/gearbest.txt'); 
    curl_setopt($ch, CURLOPT_COOKIEFILE, str_replace("\\", "/", getcwd()).'/gearbest.txt'); 
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // Автоматом идём по редиректам
    curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, 0); // Не проверять SSL сертификат
    curl_setopt ($ch, CURLOPT_SSL_VERIFYHOST, 0); // Не проверять Host SSL сертификата
    curl_setopt($ch, CURLOPT_URL, $page_url); // Куда отправляем
    curl_setopt($ch, CURLOPT_REFERER, $base_url); // Откуда пришли
    curl_setopt($ch, CURLOPT_HEADER, 0); 
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // Возвращаем, но не выводим на экран результат
    $response['html'] = curl_exec($ch);
    $info = curl_getinfo($ch);
    if($info['http_code'] != 200 && $info['http_code'] != 404) {
        $error_page[] = array(1, $page_url, $info['http_code']);
        if($retry) {
            sleep($pause_time);
            $response['html'] = curl_exec($ch);
            $info = curl_getinfo($ch);
            if($info['http_code'] != 200 && $info['http_code'] != 404)
                $error_page[] = array(2, $page_url, $info['http_code']);
        }
    }
    $response['code'] = $info['http_code'];
    $response['errors'] = $error_page;
    curl_close($ch);
    return $response;
}
/* --- 1.3 --- Функции для Gearbest.com */
/* --- 1.3.1 --- Парсинг цены */
function get_gearbest_price($gearbest_url) {
    /*
    $gearbest_url - адрес страницы товара на Gearbest
    */
    $res_arr = array();
    $res_arr['price_list'] = array();
    $res_arr['errors'] = array();
    $base_url = "https://www.gearbest.com";
    $response_arr = curl_get_contents($gearbest_url, $base_url, 5, 1);
    $page = $response_arr['html'];
    $page_code = $response_arr['code'];
    $res_arr['errors'] = array_merge($res_arr['errors'], $response_arr['errors']);
//file_put_contents('page.txt', $page);
//$page = file_get_contents('page.txt');
    if(!empty($page) && $page_code == 200) {
        if($charset != "UTF-8") {
            $page = iconv("WINDOWS-1251", "UTF-8//IGNORE", $page);
        }
        $buffer = array();
        $regexp = "/<meta property=\"og:price:amount\" content=\"(.*)\"\/>/Us";
		preg_match($regexp, $page, $buffer);

        $res_arr['price_list']['price'] = $buffer[1];
        
		$regexp = "/<meta property=\"og:price:currency\" content=\"(.*)\"\/>/Us";
        $buffer = array();
        preg_match($regexp, $page, $buffer);
		
        $res_arr['price_list']['currency'] = $buffer[1];
    } else {
        $res_arr['price_list']['price'] = 0;
        $res_arr['price_list']['currency'] = 'nodata';
        $res_arr['errors'][] = 'Ошибка загрузки страницы';
    }
    return $res_arr;
}
/* --- 2 --- Получение контента из сайта Gearbest */
if(!empty($gearbest_url)) {
    $gearbest_url = trim($gearbest_url);
    $din_url =  $gearbest_url;
    $res_arr = get_gearbest_price($din_url);
} else {
    $res_arr['error'][] = "Не задан адрес страницы с товаром";
}
/* --- 3 --- Вывод результатов работы парсера в JSON формате*/
echo json_encode($res_arr);
?>

В код скриптов добавил подробные комментарии. Общий алгоритм работы парсера следующий. На странице после нажатия на кнопку цены мы разбиваем список адресов товаров в массив. Дальше в цикле формируем и отправляем с помощью AJAX несколько POST запросов к файлу ajax-parser.php.

AJAX парсер gearbest-com

В этом файле выполняется PHP скрипт, который с помощью функций cURL получает страницу по нужному адресу с сайта gearbest.com и далее из полученного текстового содержимого посредстом регулярных выражений выделяет нужные нам данные. Потом полученные данные добавляются в массив, преобразуются в JSON формат и передаются в ответ AJAX функции. Полученный ответ преобразуется средствами JavaScript из JSON строки в объект и спарсенные данные выводятся в нужные теги.

Я специально добавил в список один некорректный адрес, чтобы посмотреть, как будут выводиться ошибки. Адреса товаров отправляются на обработку в порядке записи. А ответы приходят в порядке их получения. Именно поэтому ошибочный адрес был получен первым, 404 страница формируется сервером gearbest.com быстрее, чем страница товара.

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

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

 

10 thoughts on “AJAX парсер без перезагрузки страниц”
  1. У меня почему-то не парсится. Выдает «null null», подскажи пожалуйста в чем может быть причина.

    1. Разные причины могут быть. Сейчас попробовал сымитировать действия простого пользователя, прямо из статьи скопировал куски кода, создал нужные файлы, выбрал произвольные товары… И парсер нормально работает.
      Если есть большое желание разобраться, то можем связаться по скайпу, разберём возникающие ошибки.

      1. Разобрался, на хосте проблемы, выдает 500 ошибку при обращении к скрипту. На локалке все работает.

  2. Разобрался и с хостингом ) Всё оказалось банально, старая версия PHP, обновил и все гуд.

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

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

  4. Как раз стояла задача сверстать сайт с большим количеством реальных данных. Взял ваш код, настроил урлы и все работает «из коробки». Отличное решение, очень понравилось наглядность процесса!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *