PHP парсер с авторизацией

PHP парсер с авторизацией

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

Информация о том, авторизован пользователь или нет, часто хранится в Cookies. Составить правильный запрос и обработать файлы Cookies нам поможет библиотека cURL.

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

Форма входа Gearbest

В качестве примера разберём парсер баланса баллов в интернет-магазине Gearbest. В html коде формы входа видим название полей ввода логина и пароля, а также адрес страницы-обработчика: /m-users-a-act_sign.htm.

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

Поэтому решил посмотреть, что происходит при вводе неверных данных. В этом случае удалось отследить нужные запросы и структуру переменных.

POST запрос авторизации на сайте Gearbest

На следующем шаге смотрим код страницы личного кабинета и прикидываем, как спарсить данные баланса бонусных баллов.

HTML код личного кабинета gearbest.com

На основе полученной информации можно написать PHP парсер баланса баллов с авторизацией. Я решил дописать ряд функций для парсера из предыдущего примера. Для составления POST запросов используем функции библиотеки cURL.

Полученный в результате код парсера с авторизацией:

<?php
Error_Reporting(E_ALL & ~E_NOTICE);
mb_internal_encoding("UTF-8");
set_time_limit(0);	// Попытка установить своё время выполнения скрипта

/* --- 1 --- Инициализируем переменные для запроса */
	$time_start = time();
	$error = array();
	$error_page = array();
	$action = 0;
	$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']);
	if(isset($_REQUEST['action']))
		$action = $_REQUEST['action'];

/* --- 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.2.2 --- Авторизация при помощи cURL */
function curl_login($page_url, $base_url, $user_data) {
	/*
	$page_url - адрес страницы-источника
	$base_url - адрес страницы для поля REFERER
	$user_data - данные для авторизации
	*/
	$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_POSTFIELDS,  http_build_query($user_data));  
    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']);
	}
	$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();
	$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['error_page'] = $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 = "/(id=\"unit_price\"\s*data-orgp=\"(.*)\")|(class=\"my_shop_price new_shop_price\"\s*data-orgp=\"(.*?)\"\s*id=\"unit_price\")/Us";
		preg_match($regexp, $page, $buffer);
		$res_arr['price_list']['price'] = $buffer[2] ? $buffer[2] : $buffer[4];
		$regexp = "/bizhong=\"(.*)\">/Us";
		$buffer = array();
		preg_match($regexp, $page, $buffer);
		$res_arr['price_list']['currency'] = $buffer[1];
		$res_arr['error'] = array();
	} else {
		$res_arr['price'] = 0;
		$res_arr['currency'] = 'nodata';
		$res_arr['error'][] = 'Ошибка загрузки страницы';
	}
	return $res_arr;
}
/* --- 1.3.2 --- Авторизация на сайте */
function get_gearbest_login($user_data) {
	/*
	$gearbest_url - адрес страницы авторизации на Gearbest
	*/
	$res_arr = array();
	$base_url = "https://www.gearbest.com";
	$gearbest_login_url = 'https://login.gearbest.com/m-users-a-act_sign.htm';
	$response_arr = curl_login($gearbest_login_url, $base_url, $user_data);
	$page = $response_arr['html'];
	$page_code = $response_arr['code'];
	$res_arr['error_page'] = $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);
		}
		$res_arr['error'] = array();
		$res_arr['login_status'] = $page;
	} else {
		$res_arr['error'][] = 'Ошибка загрузки страницы';
	}
	return $res_arr;
}
/* --- 1.3.3 --- Парсинг баллов */
function get_gearbest_point($gearbest_url) {
	/*
	$gearbest_url - адрес страницы личного кабинета на Gearbest
	*/
	$res_arr = array();
	$res_arr['points'] = 0;
	$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['error_page'] = $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 = "/<div class=\"pointsWrap\">\s+My Points:\s<span>(\d+)<\/span>/Us";
		preg_match($regexp, $page, $buffer);
		$res_arr['points'] = $buffer[1];
		$res_arr['error'] = array();
	} else {
		$res_arr['points'] = 0;
		$res_arr['error'][] = 'Ошибка получения баланса баллов';
	}
	return $res_arr;
}
/* --- 1.4 --- Вывод данных в HTML */
/* --- 1.4.1 --- Вывод полученых цен */
function price_list_html($price_list) {
	echo '<p>Цена: ' . $price_list['price'] . ' <span class=\"currency\">' . $price_list['currency'] . '</span></p>';
}
/* --- 1.4.2 --- Вывод статуса авторизации */
function login_status_html($login_status) {
	echo '<p>Статус авторизации: ' . $login_status . '</p>';
}
/* --- 1.4.3 --- Вывод баланса баллов */
function points_balans_html($points) {
	echo '<p>Ваша баллы: ' . $points . '</p>';
}
/* --- 1.4.4 --- Вывод ошибок */
function error_list_html($error) {
	if (!empty($error)) {
		echo "<p>Во время обработки запроса произошли следующие ошибки:</p>\n";
		echo "<ul>\n";
		foreach($error as $error_row) {
			echo "<li>" . $error_row . "</li>\n";
		}
		echo "</ul>\n";
		echo "<p>Статус: <span class=\"red\">FAIL</span></p>\n";
	} else {
		echo "<p>Статус: <span class=\"green\">OK</span></p>\n";
	}
}
/* --- 1.4.5 --- Вывод ошибок загрузки страниц */
function error_page_list_html($error_page) {
	if (!empty($error_page)) {
		echo "<ul>\n";
		foreach($error_page as $error_row) {
			echo "<li>[" . $error_row[0] . "] " . $error_row[1] . " - " . $error_row[2] . "</li>\n";
		}
		echo "</ul>\n";
	}
}
/* --- 1.4.7 --- Вывод времени работы скрипта */
function run_time_html($time_start) {
	if(!empty($time_start))
		echo "<!--p>Время работы: " . (time() - $time_start) . "</p-->\n";
}
/* --- 2 --- Получение контента из сайта Gearbest */
	if($action == 1) { // Показать цену товара
		if(!empty($gearbest_url)) {
			$gearbest_url = trim($gearbest_url);
			$din_url =  $gearbest_url;
			$res_arr = get_gearbest_price($din_url);
			$price_list = $res_arr['price_list'];
			$error_page = $res_arr['error_page'];
			$error = array_merge($error, $res_arr['error']);
		} else {
			$error[] = "Не задан адрес страницы с товаром";
		}
	}
	if($action == 2) {	// Показать баланс баллов
		$user_data = array('email'=>'yourmail@domen.ru', 'password'=>'password');	// Данные для авторизации
		$res_arr = get_gearbest_login($user_data);
		$info_arr['login'] = $res_arr['login_status'];
		$error = array_merge($error, $res_arr['error']);
		$gearbest_lk_url = 'https://user.gearbest.com/my-favorites.htm';
		$res_arr = get_gearbest_point($gearbest_lk_url);
		$info_arr['points'] = $res_arr['points'];
		$error = array_merge($error, $res_arr['error']);
	}
/* --- 3 --- Вывод результатов работы парсера */
?>
<!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 {
	width: 100%;
}
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 class="action_form" action="" method="post">
		<input type="hidden" id="action" name="action" value="0" />
		<input type="text" name="gearbest_url" value ="<?php if(!empty($gearbest_url)) echo $gearbest_url; ?>" placeholder="URL страницы товара" />
		<input type="button" id="btn_action_price" name="btn_action_price" value="Цена" />
		<input type="button" id="btn_action_points" name="btn_action_points" value="Баллы" />
	</form>
	<div class="result">
<?php
		if($action == 1 && !empty($price_list)) {
			price_list_html($price_list);
		}
?>
<?php
		if($action == 2 && !empty($info_arr)) {
			login_status_html($info_arr['login']);
			points_balans_html($info_arr['points']);
		}
?>
	</div>
	<div class="errors_block">
<?php
	error_page_list_html($error_page);
	error_list_html($error);
	run_time_html($time_start);
?>
	</div>
	<div class="copyright">&copy Идея и реализация - <a href="https://seorubl.ru/" target="_blank" title="Записки Предприимчивого Человека" rel="generator">ПЧ</a> // 21.04.2017 г.</div>
</div>
<script type="text/javascript">
	jQuery(document).ready(function($) {
		$('#btn_action_price').click(function() {
			$('#action').val('1');
			$('.action_form').trigger("submit");
		});
		$('#btn_action_points').click(function() {
			$('#action').val('2');
			$('.action_form').trigger("submit");
		});
	});
</script>
</body>
</html>

Для того, чтобы парсер смог авторизоваться, надо ввести свои данные в переменную $user_data.

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

Пример файла Cookies, сгенерированного cURL:

# Netscape HTTP Cookie File
# http://curl.haxx.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.

.gearbest.com	TRUE	/	FALSE	0	G_SESSIONID	etn15bafqhfiecpjkk3ppdlnv5
.gearbest.com	TRUE	/	FALSE	1495394177	ip_country_code	ru
.gearbest.com	TRUE	/	FALSE	1492802177	client_ru_np	deleted
.gearbest.com	TRUE	/	FALSE	1493406977	WEBF-email	seorubl%40ya.ru
.gearbest.com	TRUE	/	FALSE	1493406977	WEBF-user_id	12478626
.gearbest.com	TRUE	/	FALSE	1523906177	WEBF-dan_num	1
login.gearbest.com	FALSE	/	FALSE	1495394177	avatar	https%3A%2F%2Ficss1.gearbest.com%2Fimagecache%2FGB2%2Fimages%2Fdomeimg%2Fscrollimg.gif
.gearbest.com	TRUE	/	FALSE	1492802177	WEBF-firstname	deleted
.gearbest.com	TRUE	/	FALSE	1492802177	WEBF-lastname	deleted
.gearbest.com	TRUE	/	FALSE	1493406977	usertype	0
.gearbest.com	TRUE	/	FALSE	0	ORIGINDC	2
.gearbest.com	TRUE	/	FALSE	0	Servernode2	node2

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

Далее выводим статус попытки авторизации и бонусный баланс.

Возможные варианты ответов от сервера gearbest.com при попытке авторизации:

  • Successfully sign;
  • The email does not exist, please register;
  • Your email/password is incorrect. Please try again;
  • needCode.

Кстати, если вначале авторизоваться и спарсить баланс баллов, то дальше при парсинге цен на товары, статус авторизованного пользователя будет сохраняться.

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

One thought on “PHP парсер с авторизацией”
  1. I like what you guys are up too. Such smart work and reporting! Keep up the superb works guys I have incorporated you guys to my blogroll. I think it will improve the value of my website.

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

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