Каталог решений - Парсер каталогов

Описание

Многие считают, что парсер – нечто сложное и не понятное. Так ли это?

Я не программист, ни по образованию, ни по роду занятий. Но люблю разбираться в разных вопросах. Поэтому решил разобраться и с парсерами. Тем более, что занялся продажей автомобильных масел и фильтров. Рыться в каталогах – долго и муторно. Охота так, чтобы отжал продавец кнопку, а ему показало, что нужно подать покупателю. Поэтому задача запарсить сайт производителя фильтров, записать все в БД, чтобы было доступно локально.

Все, что будет написано ниже, применимо не только к автомобильным фильтрам, а и к компьютерным комплектующим и прочим товарам, новостям, статьям, блогам.

Процесс установки и настройки WAMPили LAMPописывать нет смысла.

 И так, приступим. Для начала надо изучить объект. Открываем страницу каталога. Открываем ее исходник. Любой такой каталог начинается с выбора производителя авто. Исходник содержит производителей и их Id в списке <select>. Это и будет нашей первой таблицей в БД. Создаем ее в PHPMyAdmin, обозвав _car_brand.

Первый столбец brand_id (Primary), второй brand_name. Я эту таблицу заполнил, написав разовую программку, которую тут же стер. Она использовала simple_html_dom и брала данные со страницы каталога. Можно заполнить вручную. Цель этой статьи не в том. Мы решаем дальнейшую проблему, уткнувшись в которую, многие не знают, как поступить, и забрасывают свой парсер.

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

Создаем файлик poisk.php и открываем его в Notepad++. И начинаем кодить:

<?php

// считываем ИД производителя авто

$brandId=$_REQUEST["selBrand"];

// Если еще не выбирали производителя (1й этап)

if ($brandId == 0){

                echo 'ВЫБЕРИТЕ ПРОИЗВОДИТЕЛЯ<form method="POST" action="poisk.php">                                                     <select name="selBrand">';

                $database = 'db_6';

                $link = mysql_pconnect("mysql.ru", "dbu1", "polk");

                mysql_select_db($database) or die("Немогуподключитьсякбазе.");

                mysql_query("set character_set_server='utf8'");

                mysql_query("set names 'utf8'");

// Берем полный список производителей и выводим

                $car_brand = mysql_query("SELECT * FROM _car_brand"); 

// Цикл по строкам. Кому что не ясно, читаем про работу с БД

                while ($brand_id = mysql_fetch_array($car_brand)){

                               $a=$brand_id[0];

                               $b=$brand_id[1];

                               echo '<option value="'.$a.'">'.$b.'</option>';

                }

                echo '</select>                                                                                                                                                                                <input type="submit" id="btnAppSearch" name="btnAppSearch" value="Go"/></form>';

}

Коротко говоря, считали из базы и вывели полный список производителей…

Дальше интересней. Нам нужна вторая таблица в БД `_car_model с моделями авто. В ней поля (`car_model_id`, `car_model_brand_id`, `car_model_name`). Эту таблицу мы будем заполнять не по циклу, а в процессе работы с программой.

// Если производитель уже выбран

else {

// Считываем прилетел ли ИД модели

$selModel=$_REQUEST["selModel"];



                if ($selModel == 0){

// Производитель выбран, но не выбрана модель (2й этап)

                $database = 'db_6';

                $link = mysql_pconnect("mysql.ru", "dbu1", "polk");

                mysql_select_db($database) or die("Немогуподключитьсякбазе.");

                mysql_query("set character_set_server='utf8'");

                mysql_query("setnames 'utf8'");

// Смотрим имя производителя и его ИД

                $car_brand = mysql_query("SELECT * FROM `_car_brand` WHERE `car_brand_id` = '$brandId'");           

                $brand_id = mysql_fetch_array($car_brand);

                $a=$brand_id[0];

                $b=$brand_id[1];

// Выводим имя производителя без возможности его сменить, только через обновление страницы

                echo '<form method="POST" action="poisk.php">ПРОИЗВОДИТЕЛЬ :                                                          <select name="selBrand"><option value="'.$a.'">'.$b.'</option></select>

                <a href="poisk.php">СМЕНИТЬ</a>

                <br>ВЫБЕРИТЕМОДЕЛЬ : ';

                echo '<select name="selModel">';

// Лезем в локальную БД, смотрим, есть ли в ней модели этого производителя

                $car_model = mysql_query("SELECT * FROM `_car_model` WHERE `car_model_brand_id` = '$brandId'");

                $i=0;

// Если в локальной БД есть модели, то выводим их, счетчик их считает...

                while ($model_id = mysql_fetch_array($car_model)){

                                $c=$model_id[0];

                               $d=$model_id[2];

                               echo '<option value="'.$c.'">'.$d.'</option>';

                               $i++;

                }

Тут вроде пока все просто, но, как мы помним, наша таблица с моделями на сей момент пуста… Поэтому и список будет пуст. Придётся лезть за ним на сайт.

// Если счетчик не посчитал, значит локально их нет...

                if ($i<1){

// Лезем тогда на сайт за моделями

Как уже говорил, в исходнике страницы мы ничего не увидим, поэтому запускаем встроенные в браузер средства отладки. Я пользовался оперой. Поэтому правый клик => посмотреть код элемента. Переходим на вкладку Network, где переходим в под-вкладку XHR. Тут то и отображаются «подпольные» сетевые действия, которые нужно внимательно изучить.  Выбираем в списке автомобиль (я выбрал TOYOTA) и кликаем «дальше»… В окне браузера появляется список с моделями, а на нашей вкладке мы видим что добавилась строка get_models/. Выбрав ее, видим много интересного.  Видим адрес, по которому браузер отправлял запрос. Дальше нас интересует информация, которую браузер отправлял на сайт: Request headers. И данные, которые браузер передал: FormData. Snoopy.class — нам в помощь.

                        include ("Snoopy.class.php");

                               $snoopy = new Snoopy;

// Некоторые параметры Снупи знает, их и указываем от реквеста

                               $snoopy->accept = "application/json, text/javascript, */*; q=0.01";

// Некоторые параметры ему не известны, поэтому указываем как RAW

                               $snoopy->rawheaders["Accept-Encoding"] = "gzip, deflate";

                               $snoopy->rawheaders["Accept-Language"] = "ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4";

                               $snoopy->rawheaders["Connection"] = "keep-alive";

                               $snoopy->rawheaders["Content-Type"] = "application/x-www-form-urlencoded";

                               $snoopy->cookies["PHPSESSID"] = '7b60548db34612bc5af6';

                               $snoopy->cookies["ci_session"] = тут очень много всего было…;

                               $snoopy->host = "www.filter.com";

                               $snoopy->rawheaders["Origin"] = "http://www.filter.com";

                               $snoopy->referer = "http://www.filter.com/catalogue";

                               $snoopy->agent = "Mozilla/5.0 (Windows NT 6.1; WOW64)";

                               $snoopy->rawheaders["X-Requested-With"] = "XMLHttpRequest";

// Шапка закончилась, поэтому формируем массив запроса, передаваемый Пост

                               $submit_vars = array();

                               $submit_vars["brandId"] = $brandId;

// Ну а это адрес, куда весь запрос наш полетит

                               $submit_url = "http://www.filter.com/application/get_models/";

Еще раз, для понимания: все эти данные здесь для примера. У каждого сайта они будут свои. У меня, почему то, при передаче Content-Length, такого же, как и в запросе браузера, приходил пустой ответ от сервера. Попробовал не передавать его – работает. Значит параметр не важный для запроса. А вот без этого : $snoopy->rawheaders["X-Requested-With"] = "XMLHttpRequest";

Приходил пустой результат.

С ним сайт отвечает — результат получен!!!

Шлем запрос и получаем результат:

if($snoopy->submit($submit_url,$submit_vars))
       {

// Если удачно залезли

$var = $snoopy->results;

// Тут к нам как раз и прилетел JSON, декодируем в массив

$vars = json_decode($var);

//Который потом разбираем

     foreach($vars as $value){
     $c=$value->app_model_id;
     $d=$value->app_model_name;

// Выводим список моделей

      echo '<option value="'.$c.'">'.$d.'</option>
       ';

// И пишем его в БД

       $sql = mysql_query("INSERT INTO `db_ 6`.`_car_model` (`car_model_id`, `car_model_brand_id`, `car_model_name`) VALUES ('$c', '$a', '$d')");
                                               }
                               }
                }             

       echo '</select>';
       echo '<input type="submit" id="btnAppSearch" name="btnAppSearch" value="Go"/> </form>';

                }

Как можно увидеть, таблица с моделями заполняется при первом выборе производителя. При повторном запросе по этому производителю модели будут прилетать из локальной БД. Ну а дальше:

 

else {

// Оказалось, что выбран и производитель и модель

Проделываем то же самое с get_years, get_eng_vol, дойдя до ягодки get_applications, отправив которой в запросе

                               $submit_vars = array();

                               $submit_vars["modelId"] = $selModel;

                               $submit_vars["year"] = $year;

                               $submit_vars["eng_vol"] = $eng_vol;

Получим в ответ заветный JSON c {Oil;Air;Fuel;Cabin;Trans}

 

http://idemitsu.to.net.ru/poisk.php — вот полученный вариант каталога, который можно встроить в сайт или в окно 1с продавца…

 

Всем удачи и успехов.

has been added to your cart:
Оформление заказа