Часто возникают задачи, требующие получения данных со страниц сайта, которые показываются только залогиненным пользователям. В этом случае нам нужно написать парсер с автоматической авторизацией.
Информация о том, авторизован пользователь или нет, часто хранится в Cookies. Составить правильный запрос и обработать файлы Cookies нам поможет библиотека cURL.
Предварительно нужно понять, как работает механизм авторизации на сайте, какие передаются переменные после отправки данных формы входа. Эти данные можно проанализировать с помощью инструментов разработчика в браузере.
В качестве примера разберём парсер баланса баллов в интернет-магазине Gearbest. В html коде формы входа видим название полей ввода логина и пароля, а также адрес страницы-обработчика: /m-users-a-act_sign.htm.
Далее попытаемся авторизоваться и отследить какие запросы отправляются на сервер. Введя правильные логин и пароль мне не удалось успеть увидеть нужные запросы, так как происходит несколько редиректов. А сам запрос авторизации отправляется по AJAX.
Поэтому решил посмотреть, что происходит при вводе неверных данных. В этом случае удалось отследить нужные запросы и структуру переменных.
На следующем шаге смотрим код страницы личного кабинета и прикидываем, как спарсить данные баланса бонусных баллов.
На основе полученной информации можно написать 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">© Идея и реализация - <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 для парсинга сайтов.
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.