Для комфортного парсинга мы приобретем отдельный VDS сервер и настроем его параметры под работу с потреблением большого количество памяти.

Первым делом настраиваем php.ini. Устанавливаем безлимитное выполнение скрипта, что бы наш процесс парсинга не завершился раньше времени: max_execution_time = 0 и max_input_time = -1. Снимаем ограничение памяти: memory_limit = -1 Устанавливаем allow_url_fopen = On, это позволит нам открывать удаленные url.

Создаем директорию для нашего проекта: mkdir /var/www/8991.ru/

Инициализируем compaser в созданной директории: compaser init, далее по инструкции и устанавливаем composer в наш проект composer install

Создаем в проекте директории App и tmp. Создаем файл с вспомогательными функциями для отладки helpers.php, функции позволят сделать дамп данных болле читабельным как для браузера так и для консоли:

<?php
function xprint( $param, $title = 'Отладочная информация' )
{
    ini_set( 'xdebug.var_display_max_depth', 50 );
    ini_set( 'xdebug.var_display_max_children', 25600 );
    ini_set( 'xdebug.var_display_max_data', 9999999999 );
    if ( PHP_SAPI == 'cli' )
    {
        echo "\n---------------[ $title ]---------------\n";
        echo print_r( $param, true );
        echo "\n-------------------------------------------\n";
    }
    else
    {
        ?>
        <style>
            .xprint-wrapper {
                padding: 10px;
                margin-bottom: 25px;
                color: black;
                background: #f6f6f6;
                position: relative;
                top: 18px;
                border: 1px solid gray;
                font-size: 11px;
                font-family: InputMono, Monospace;
                width: 80%;
            }
            .xprint-title {
                padding-top: 1px;
                color: #000;
                background: #ddd;
                position: relative;
                top: -18px;
                width: 170px;
                height: 15px;
                text-align: center;
                border: 1px solid gray;
                font-family: InputMono, Monospace;
            }
        </style>
        <div class="xprint-wrapper">
        <div class="xprint-title"><?= $title ?></div>
        <pre style="color:#000;"><?= htmlspecialchars( print_r( $param, true ) ) ?></pre>
        </div><?php
    }
}
function xd( $val, $title = null )
{
    xprint( $val, $title );
    die();
}

Заполняем автозагрузчик композера (файл composer.json):

{
    "name": "pars/8991.ru",
    "description": "Tutorial on cURL",
    "license": "MIT",
    "authors": [
        {
            "name": "exfriend",
            "email": "i@zlad.tk"
        }
    ],
    "minimum-stability": "dev",
    "require": {},
    "autoload": {
        "psr-4": {
            "App\\": "App/"
        },
        "files": [
            "helpers.php"
        ]
    }
}

В index.php подключаем наш автозагрузчик композера:

<?php require_once 'vendor/autoload.php';

Устанавливаем phpQuery, это аналог jQuery только для работы в консоле: composer require coderockr/php-query

phpQuery

Подготовительные работы закончились, теперь можно приступать к парсингу данных. Получим погоду с сайт yandex.ru/pogoda/

Если у вас был опыт работы в jQuery то разобраться с phpQuery вам не составит труда.

Первым делом мы получаем весь html со страницы Яндекс.Погоды: file_get_contents("http://yandex.ru/pogoda/novosibirsk");

Далее нам нужно передать полученный код обработчику phpQuery:

phpQuery::newDocument($html);

После прохода по элементам выгрузим документ что бы освободить память:

phpQuery::unloadDocuments();

В итоги мы получаем объект с DOM деревом сайта, с помощью методов phpQuery мы извлекаем нужные нам данные, все команды аналогичны JQuery. Для примера я «выдернул» со страницы погоду на сегодня использовав несколько методов:

$html = file_get_contents("http://yandex.ru/pogoda/novosibirsk");

phpQuery::newDocument($html);

$title = pq('.fact__time-yesterday-wrap')->html();
$winde = pq('.term.term_orient_v.fact__wind-speed > .term__value')->attr('aria-label');
$time = pq('ul.swiper-wrapper')->children('.fact__hour.swiper-slide');

echo $title;

echo "<p>$winde</p>";

foreach($time as $li){
$ls = pq($li);

echo "<div class='main'>";
echo $ls->find('.fact__hour-elem')->html();
echo "</div>";
}
phpQuery::unloadDocuments();

Добавил пару строчек стилей что бы информация была более читаема:

<style>
body{
background: #4791e8;
color: white;
}
.main {
margin: 10px 3px;
}
.fact__hour-label, .icon_color_flat {
float: left;
height: 18px;
}
</style>

Результат:

cURL

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

При установке на сервер LAMP вы скорее получили и этот инструмент, если нет, то в интернете есть описание его установки на любое ПО, тут мы на этом вопросе не будем останавливаться.

Напишем небольшую функцию для передачи в заголовке все необходимые параметры в том числе cookie и POST данные на нужный нам ресурс. В данной функции мы инициализируем запрос на нужный url, в заголовок добавляем браузер, нужные cookie и при необходимости POST-данные:

function request($url, $post = null, $cookiefile = 'tmp/cookie.txt'){

    $ch = curl_init($url);

    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    //    curl_setopt($ch, CURLOPT_HEADER, true);  /* активируем
    curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ");
    curl_setopt($ch, CURLOPT_COOKIEJAR, __DIR__.DIRECTORY_SEPARATOR.$cookiefile);
    curl_setopt($ch, CURLOPT_COOKIEFILE, __DIR__.DIRECTORY_SEPARATOR.$cookiefile);
    //curl_setopt($ch, CURLOPT_COOKIESESSION, true);

    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

    if($post){
        curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
    }

    $html = curl_exec($ch);
    curl_close($ch); 

    return $html;
}

На примере сайта lichess.org попробуем авторизоваться через cURL. Для начала изучим процесс авторизации. Заходим с браузера Chrome на страницу авторизации https://lichess.org/login, отправляем запрос на авторизацию с неверным паролем:

Переходи в консоль браузера, вкладка Network, в информации о странице видим url куда необходимо отправить POST запрос с нужными параметрами,

Опускаемся вниз страницы и копируем нужны параметры запроса, данные параметры добавим в массив $post и передадим 2-м параметром нашей функции:

$post = [
    'token' => '',
    'password' => '*******',
    'username' => 'malashko'
];
$html = request('https://lichess.org/login?referrer=%2F', $post);

echo $html;

Запускаем из браузера наш скрипт и получаем и получаем html страницу авторизованного пользователя:

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

Многопоточность

Для парсинга больших объемов данных требуется многократное подключение к удаленному ресурсу. Количество таких подключений часто измеряется тысячами. Выполнение таких запросов через cURL займет очень продолжительное время. Для таких запросов используется инструмент multicurl. На github’e есть отличный проект для работы cURL позволяющий использовать многопоточность и разбивать патоки на пакеты: https://github.com/php-curl-class/php-curl-class

Устанавлеваем его через composer: composer require php-curl-class/php-curl-class, подключаем модуль в нашем проекте:

use \Curl\MultiCurl;

// Requests in parallel with callback functions.
$multi_curl = new MultiCurl();

$multi_curl->success(function($instance) {
    echo 'call to "' . $instance->url . '" was successful.' . "\n";
    echo 'response:' . "\n";
    print_r($instance->response);
});
$multi_curl->error(function($instance) {
    echo 'call to "' . $instance->url . '" was unsuccessful.' . "\n";
    echo 'error code: ' . $instance->errorCode . "\n";
    echo 'error message: ' . $instance->errorMessage . "\n";
});
$multi_curl->complete(function($instance) {
    echo 'call completed' . "\n";
});

for($i=1; $i<100; $i++){
    $urls = 'http://httpbin.org/get?i='.$i;
    $multi_curl->addGet($urls);
}

$multi_curl->start(); // Blocks until all items in the queue have been processed.

Многопоточность нам пригодится в первую очередь при использовании прокси. Большинство современных сайтов блокируют через чур активные ip на стороне сервера. Это необходимо для того, что бы снизить нагрузку от автоматических запросов роботов. Что бы наш парсинг не был заблокирован нам необходимо заменить свой ip при запросе к ресурсу с которого мы намерены спарсить данные. Это решается с помощью прокси серверов через которые будет проходить наш запрос. Самые легкодоступные это «прозрачные прокси», они нам не подойдут, так как в заголовке они передают информацию о том что это прокси и показывают наш реальный IP, такие запросы скорее всего заблокируются, «анонимные прокси» уведомляют что являются прокси, но не передают наш реальный IP, и наконец «элитные прокси» которые полностью анонимны, они полностью имитируют пользователя. Есть специальные сайты которые предоставляют бесплатно прокси адреса.

Пример сайта с прокси адресами

Теперь мы можем пользуясь нашими знаниями спарсить IP с помощью cURL, и подставлять адреса в пакеты при парсинге нужного нам ресурса. Делается это с помощью параметра CURLOPT_PROXY, пример через библиотеку php-curl-class:

$curl->setProxy('someproxy.com', '9999', 'username', 'password');
$curl->setProxyTunnel(); </p>

Отладка запросов

Используя описанные выше инструкции мы уже можем парсить простые веб-ресурсы. Но на серьезных проектах мы будем сталкиваться с проблемой когда с браузера мы без проблем получаем нужную нам информацию, а cURL не получает нужные данные.

Такие проблемы чаще всего связаны с использованием на сайтах Ajax или с более строкой фильтрацией пользователей по заголовку.

В таких случаях требуется отладка запросов.

Один из вариантов это использование программы Burp Suite. Настроив на локальном ПО отдачу запросов с ip 127.0.0.1:8080 и установив сертификат Burp Suite , который можно получить перейдя с браузера на 127.0.0.1:8080 при запущенным Burp Suite мы можем перехватывать все запросы отправляемые в браузер и получаемые ответы.

Проанализировав наши запросы и полученные ответы мы уже сможем настроить cURL максимально похожем на запрос пользователя с нужными параметрами для получения информации из контента подгруженного AJAX’ом.

Тут уже начинается более индивидуальная работа с каждым сайтом. Методом проб и ошибок у вас получится составить релевантный запрос и получить необходимые данные.

Удачи в работе!

Malashko
miv-men@mail.ru

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

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