PHP и автоматизированный сбор данных

Содержание:

Статья ниже подводит промежуточный итог серии материалов по миру дата-харвестинга при помощи языков программирования. Сайт прокси сервер Dexodata с системой гибких настроек уже изучил потенциал Python, JavaScript и языка Java. Теперь время поработать с PHP (в будущем мы планируем затронуть и другие тематики).

Существует мнение, что эта опция устарела. Зачем раскрывать ее для читателей, которым нужна аренда прокси для работы с собственноручно написанной программой? Причины есть. PHP является серверным скриптовым языком, появившимся в 1995 году. В этом качестве он до сих пор обслуживает потребности веб-девелоперов и развивается (последняя версия PHP — 8.4):  

  • В интернет-мире не так много людей, не знающих, что такой WordPress. Эта CMS работает на PHP и поддерживает около 40% сайтов в мире. Не все они популярны. 40% страниц не равняются 40% трафика или 40% данных. Но доля весома; 
  • Помимо управления контентом, в 80% случаев PHP обеспечивает серверные потребности сайтов (когда этот факт можно установить);
  • На PHP до сих пор появляются современные продукты. Пользователи, прибегающие к нашим лучшим резидентным и мобильным прокси для работы в социальных сетях, могут не знать, но Slack построен на PHP. Да, это не соцсеть, но сходные элементы там есть.

Как язык, изначально «предрасположенный» для интернета, PHP является рабочим вариантом для сценариев низкой и средней сложности. С его помощью реально:

  1. Инициировать запросы;
  2. Забирать информацию;
  3. Сохранять данные в подходящем формате, как то CSV, JSON или XML.

Итог: PHP является ресурсом для парсинга, поскольку он способен взаимодействовать с сайтами, понимать и обрабатывать HTML, а также собирать информацию о странице с помощью ряда функций и библиотек. Он не такой продвинутый и гибкий, и его не следует выбирать для динамического контента. И все же он является жизнеспособной альтернативой. Мы говорим об этом как эксперты в сфере веб-скрейпинга.

Основы и требования к дата-харвестингу с PHP

Прежде чем двигаться дальше, стоит удостовериться в следующих моментах:

  1. HTTP-запросы. Здесь мы имеем в виду протокол, применяемый серверами для отправки и получения информации. Чтобы принять данные о сайте, пользователь должен направить HTTP-запрос на сервер, отвечающий за размещение этой страницы. В PHP есть такие встроенные функции, как cURL, file_get_contents(), а также fopen(), позволяющие направлять HTTP-запросы и «снимать» информацию со страниц;
  2. Парсинг HTML. В качестве языка, предназначенного для «конструирования» веб-страниц, PHP предлагает такие встроенные функции, как DOMDocument и SimpleXMLElement, позволяющие анализировать HTML для получения данных;
  3. Регулярные выражения служат инструментом для сопоставления шаблонов и совершения манипуляций с текстовым контентом. Опять же, и тут PHP предлагает встроенные функции, например, preg_match() вместе с preg_replace(), которые позволяют использовать регулярные выражения для скрейпинга.

Этих понятий будет достаточно, если пользователи намерены создать в PHP простую программу для передачи HTTP-запросов на сайт, «ухватывания» HTML и анализа кода.

 

Автоматизированный сбор данных через PHP: Пробный сценарий

 

Предположим, необходимо извлечь сведения, содержащиеся на сайте e-commerce в виде списка товаров (ниже показывается простой случай, в последующих разделах будут более сложные задачи). Нужно получить наименование, стоимость и изображение каждого предмета и сохранить эту информацию в виде CSV. Вот самый прямолинейный путь.

<?php

$url = 'https://sample.com/items';

$html = file_get_contents($url);

$dom = new DOMDocument();

$dom->loadHTML($html);

$items = $dom->getElementsByTagName('article');

$data = [];

foreach ($items as $item) { // Fixed variable name from $items to $item

    $name = $item->getElementsByTagName('h2')[0]->textContent;

    $price = $item->getElementsByTagName('span')[0]->textContent;

    $image = $item->getElementsByTagName('img')[0]->getAttribute('src');

    $data[] = [$name, $price, $image];

}

$fp = fopen('items.csv', 'w');

foreach ($data as $fields) {

    fputcsv($fp, $fields);

}

fclose($fp);

?>

Мы сначала передаем HTTP-запрос на сайт с помощью file_get_contents(), затем переключаемся на синтаксический анализ содержимого HTML с DOMDocument и получаем соответствующую информацию при помощи метода getElementsByTagName(). Затем можно сохранить полученные данные в массиве для записи в CSV-файл через fputcsv()

 

Эволюция. Библиотеки и инструменты PHP

1. Goutte

 

Goutte служит полнофункциональной библиотекой в рамках работы с PHP. Ее способны освоить новички, и все благодаря понятному объектно-ориентированному характеру. Что касается других преимуществ, то это комьюнити внушительных размеров, обширная документация и, наконец, скорость. Из-за того, что Goutte полагается на HTTP 1.1, браузеру требуется только одно соединение с сервером. После этого одно и то же соединение будет использоваться для всех последующих запросов. Есть и недостатки. Например, могут возникнуть непреодолимые препятствия в отношении динамического контента.

Попробуем Goutte, чтобы сделать клик на нужном сайте (как и при прочих подобных манипуляциях, не забывайте о резидентных прокси от Dexodata, а также о наших мобильных адресах и серверным IP от 3,65 USD за 1 Гб).

Для начала установим Composer. Так можно будет управлять зависимостями и вообще работать с библиотеками. Даем вводную в терминале:

composer require fabpot/goutte

Потом создаем PHP-скрипт и называем, как хотим. Открываем его и вносим код для начала работы с Goutte:

require 'vendor/autoload.php';

use Goutte\Client;

$client = new Client();

Goutte мы запустили! Ставим этот код в конце файла, чтобы ухватить URL через функцию client->request().

$url = "http://instance.com/";

$crawler = $client->request('GET', $url);

 

// Click on the "More information..." link

$link = $crawler->selectLink('More information…')->link();

$crawler = $client->click($link);

Вот так мы и отправились к нужной странице и дали команду на клик.

 

2. Simple HTML DOM

 

Будучи парсером, эта программа предназначена для работы с любым документом HTML. Плюс в том, что его легко освоить. Он не строго завязан на внешние материалы, связанные с JS, которые нужно было бы подгружать отдельно да старта работы. Другие достоинства включают скорость (пользователям не нужно загружать в память целый сайт, поэтому можно одновременно обрабатывать несколько HTML-страниц без ущерба для производительности) и простоту установки (нет необходимости ставить дополнительное ПО или библиотеки, достаточно PHP).

Ограничения тоже есть. Доступ к компонентам страницы будет ограничен: можно выполнять манипуляции со структурой, но содержимое останется вне зоны доступа. И вообще надо неплохо знать HTML. Но перейдем к практике.

Идем на страницу с документацией Simple HTML DOM. Копируем simple_html_dom.php. Вставляем в активный проект. Генерируем файл PHP (имена опять неважны), пусть будет webscrapingstuff.php. Теперь открываем файл и «вооружаем» проект библиотекой Simple HTML DOM:

include('simple_html_dom_php');

Дальше добавляем код, который направит нас к странице:

$html = file_get_html('http://instance.com/')

file_get_html, как часть Simple HTM DOM, способна извлечь HTML со страницы с заданным URL. Затем она подгружает DOM-объект для хранения переменной $html.

Допустим, нам требуется главный заголовок. Чтобы получить H1, применяем код:

echo $html->find('h1', 0)->plaintext;

Открываем итоговый файл и находим его там.

 

3. PHP Scraper

 

Как экосистема для этичного дата-харвестинга, Dexodata не может обойти вниманием PHP Scraper. Для пользователей этот вариант открывает канал для скрейпинга посредством PHP-скриптов. Такой подход не подразумевает необходимости писать много кода. Все, что нужно, — это применение таких незамысловатых команд, как find_element_by_class или find_element_by_id. Их хватит для сбора элементов с сайта при вводе id и класса.

Но и без проблем не обойдется. PHP Scraper может не соответствовать каждому возможному варианту использования. Его использование «заточено» под сайты на Apache или Nginx и настроенные через активированный по умолчанию mod_rewrite. PHP Scraper — не вариант, если надо получать информацию из API или анализировать сложные HTML-блоки.

Двигаемся дальше.

Надо установить Composer, как это было сделано в разделе с Goutte. После чего ставим там код для активации:

composer require spekulatius/phpscraper

Установка позади, автоматический подгрузчик Composer подхватит наш пакет. Если пользователь работает с VanillaPHP, то подгрузчки надо интегрировать в скрипт, вот так:

require 'vendor/autoload.php'

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

$web = new \spekulatius\phpscraper();

Указываем программе направление в виде URL:

$web->go(' [http://instance.com/](http://instance.com/) ');

Вносим этот раздел кода, ответственный за «выцепление» каждой ссылки на сайте и вывода их количества.

// Print the number of links.

echo "This page contains " . count($web->links) . " links.\n\n";

 

// Loop through the links

foreach ($web->links as $link) {

echo " - " . $link . "\n";

}

 

/**

* This code will print out:

*

* This page contains 1 link.

*

* - https://www.iana.org/domains/instance

*/

Не забывайте: мы тут используем выдуманные адреса.

 

4. cURL для сбора данных с PHP

 

Рассмотрев три примера, выделим в отдельный раздел, возможно, самую известную опцию — cURL. Это библиотека и инструмент для работы с командной строкой позволяет отправлять и получать файлы через HTTP и FTP. При его использовании можно применять геотаргетированные прокси от Dexodata (в том числе те, которые предоставляются в рамках бесплатного пробного периода), передавать данные через SSL-соединения и многое другое. Протестируем этот вариант на практике.

Создаем PHP-скрипт и используем предложенный код:

// Initialize curl

$ch = curl_init();

// URL for Scraping

curl_setopt($ch, CURLOPT_URL,

'https://example.com');

// Retun Transfer True

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$output = curl_exec($ch);

// Closing cURL

curl_close($ch);

Определение CURLOPT_RETURNTRANSFER как TRUE предоставляет нам страницу в качестве строки. А значит, наш код может забрать информацию для дальнейших действий.

Взглянуть на эти данные можно через echo с переменной для хранения данных:

echo $output

Следующий шаг — разместить информацию в документе DOM, что позволит нам подвергнуть ее скрейпингу.

$dom = new DOMDocument;

$dom->loadHTML($output);

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

$tags = $dom->getElementsByTagName('a');

for($I=0; $I < $tags->length; $I++){

$link = $tags->item($i);

echo " - " . $link . "\n";

}

Как экосистема геотаргетированных прокси, призываем не забывать о защите от обнаружения со стороны администраторов при помощи адресов, которые мы предоставляем (например, наши резидентные IP от интернет-провайдеров или мобильные прокси с ротацией). Dexodata подскажет, как использовать их в cURL. Просто следуйте синтаксису:

curl --proxy <proxy-ip>:<proxy-port> <url>

В <<proxy-ip>> ставим адрес, а секция <<proxy-port>> предназначена для номера порта. На выходе должно получится нечто вроде:

curl --proxy 193.191.56.12:7070 -k https://example.com

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

 

Создаем скрейпер на PHP: пример

 

Еще раз представим себе сайт e-commerce со списком товаров, которые мы хотим вытянуть с помощью PHP, уже на более сложном уровне. Вот набор действий от Dexodata.

Стартуйте с загрузки всего HTML-кода за счет возможностей PHP и cURL:

// initialize the cURL request 

$curl = curl_init(); 

// set the URL to reach with a GET HTTP request 

curl_setopt($curl, CURLOPT_URL, "https://scrapeme.live/shop/"); 

// get the data returned by the cURL request as a string 

curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); 

// make the cURL request follow eventual redirects, 

// and reach the final page of interest 

curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); 

// execute the cURL request and 

// get the HTML of the page as a string 

$html = curl_exec($curl); 

// release the cURL resources 

curl_close($curl);

Теперь материал, связанный с HTML, находится в нашем распоряжении в качестве переменной $html. Следующий этап — загрузить $html в HtmlDomParser с функцией str_get_html():

require_once __DIR__ . "../../vendor/autoload.php"; 

use voku\helper\HtmlDomParser; 

$curl = curl_init(); 

curl_setopt($curl, CURLOPT_URL, "https://scrapeme.live/shop/"); 

curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); 

curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); 

$html = curl_exec($curl); 

curl_close($curl); 

// initialize HtmlDomParser 

$htmlDomParser = HtmlDomParser::str_get_html($html);

Теперь можно применить HtmlDomParser для работы с DOM для инициирования скрейпинга. Чтобы это сделать, «вытягиваем» полный список всех ссылок, связанных с пагинацией. Это даст нам возможность просканировать весь сайт магазина. Щелкните правой кнопкой мыши на компонент HTML, связанный с пагинацией, и выберите «Inspect». После этого DevTools покажут нам элемент, связанный с DOM.

Как собирать данные в Сети с PHP и арендой прокси

В рамках нашего сценария можно ожидать, что .page-numbers, в качестве CSS-класса, указывают на связанный с пагинацией HTML. Однако CSS-класс не обязательно будет однозначно идентифицировать нужный нам компонент. Сразу несколько элементов могут быть связаны с одним и тем же классом.

Поэтому, если есть намерение применить селектор CSS для «захвата» компонентов в DOM, придется применять класс CSS в сочетании с другими селекторами. В частности, пользователи могут применить помощник HtmlDomParser с .page-numbers в качестве селектора CSS, чтобы выбрать все HTML-компоненты, связанные с нумерацией страниц. Пройдемся по сайту, чтобы получить все необходимые URL-адреса из атрибута href.

Смотрите:

// retrieve the HTML pagination elements with 

// the ".page-numbers a" CSS selector 

$paginationElements = $htmlDomParser->find(".page-numbers a"); 

$paginationLinks = []; 

foreach ($paginationElements as $paginationElement) { 

// populate the paginationLinks set with the URL 

// extracted from the href attribute of the HTML pagination element 

$paginationLink = $paginationElement->getAttribute("href"); 

// avoid duplicates in the list of URLs 

if (!in_array($paginationLink, $paginationLinks)) { 

$paginationLinks[] = $paginationLink; 

 

// print the paginationLinks array 

print_r($paginationLinks);

Обратите внимание, что find() позволяет ухватывать компоненты DOM на базе CSS селектора. В этом гипотетическом сценарии от Dexodata компоненты, связанные с пагинацией, представлены по два раза на каждой странице. Нужно применить специальный трюк для решения проблемы дублирования компонентов в свете массива $paginationLinks.

Вот что мы увидим, если попытаемся запустить наш скрипт:

Array ( 

[0] => https://scrapeme.live/shop/page/2/ 

[1] => https://scrapeme.live/shop/page/3/ 

[2] => https://scrapeme.live/shop/page/4/ 

[3] => https://scrapeme.live/shop/page/46/ 

[4] => https://scrapeme.live/shop/page/47/ 

[5] => https://scrapeme.live/shop/page/50/ 

)

Обратите внимание, что все адреса имеют одинаковую структуру. Все они отмечены конечной цифрой, отвечающей за пагинацию. Вот как ее извлечь:

// remove all non-numeric characters in the last element of 

// the $paginationLinks array to retrieve the highest pagination number 

$highestPaginationNumber = preg_replace("/\D/", "", end($paginationLinks));

Допустим, показатель $highestPaginationNumber будет «50».

А теперь соберем данные о каждом отдельном продукте. Делаем правый клик по элементу и активируем DevTools, а затем жмем на «Inspect». Можно ожидать, что товар будет «сделан» из HTML-элемента li.product, включающего URL, изображение, заголовок и ценник. Такая информация исходит из «a», «mg», «h2» и «span» элементов HTML.

Захватываем информацию через HtmlDomParser:

$productDataLit = array(); 

// retrieve the list of products on the page 

$productElements = $htmlDomParser->find("li.product"); 

foreach ($productElements as $productElement) { 

// extract the product data 

$url = $productElement->findOne("a")->getAttribute("href"); 

$image = $productElement->findOne("img")->getAttribute("src"); 

$name = $productElement->findOne("h2")->text; 

$price = $productElement->findOne(".price span")->text; 

// transform the product data into an associative array 

$productData = array( 

"url" => $url, 

"image" => $image, 

"name" => $name, 

"price" => $price 

); 

$productDataList[] = $productData; 

}

На этой стадии наша логика способна собрать все фрагменты информации об элементе с каждой страницы и впоследствии сохранить ее как массив $productDataList. Применим ее ко всем страницам.

// iterate over all "/shop/page/X" pages and retrieve all product data 

for ($paginationNumber = 1; $paginationNumber <= $highestPaginationNumber; $paginationNumber++) { 

$curl = curl_init(); 

curl_setopt($curl, CURLOPT_URL, "https://scrapeme.live/shop/page/$paginationNumber/"); 

curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); 

$pageHtml = curl_exec($curl); 

curl_close($curl); 

$paginationHtmlDomParser = HtmlDomParser::str_get_html($pageHtml); 

// scraping logic... 

}

Миссия выполнена.

 

Итоговые замечания

 

Завершая этот обзор, приведем несколько итоговых замечаний, важных при скрейпинге. Постарайтесь не быть замеченными при харвестинге данных, для этого используйте прокси (иначе IP может попасть под ограничения). Вот еще один ремайндер о том, как это можно сделать при работе с cURL.

curl_setopt($curl, CURLOPT_PROXY, "<PROXY_URL>"); 

curl_setopt($curl, CURLOPT_PROXYPORT, "<PROXY_PORT>"); 

curl_setopt($curl, CURLOPT_PROXYTYPE, "<PROXY_TYPE>");

1. Дополнительный уровень защиты может также дать HTTP-заголовок пользовательского агента. По определению cURL всегда будет устанавливать что-то вроде curl/XX.YY.ZZ. Идентифицировать пользователя с таким заголовком и понять, что речь идет о скрейпинге, не составит большого труда. Вместо этого попробуйте здесь «поиграть» с заголовком.

curl_setopt($curl, CURLOPT_USERAGENT, "<USER_AGENT_STRING>");

В итоге должно получиться нечто вроде этого примера из интернета:

curl_setopt($curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.6) Gecko/20070725 Firefox/2.0.0.6")

2. Еще одна ремарка от Dexodata касается динамического контента. Мы уже подчеркивали, что обычный запрос GET с cURL вам с ним не поможет, потребуются дополнительные методы. Вернитесь к нашему гайду, посвященному JS, и изучите тему headless-браузеров. Это поможет создать парсер, взаимодействующий с сайтом как реальный пользователь и извлекающий динамический контент. 

3. Наше третье замечание посвящено параллельному парсингу. Многопоточность в PHP — тема сложная, но подчас неизбежная. Вот вариант решения этой проблемы от Dexodata. Наша концепция заключается в том, чтобы скрипт мог выполняться на нескольких страницах, применяя параметры HTTP GET не ко всему сайту, а к его частям. 

Модифицируйте скрипт, чтобы он не охватывал сайт целиком, а сосредоточился на более мелких параллельных фрагментах. Вот как можно установить границы таких «кусочков»: 

$from = null; 

$to = null; 

if (isset($_GET["from"]) && is_numeric($_GET["from"])) { 

$from = $_GET["from"]; 

if (isset($_GET["to"]) && is_numeric($_GET["to"])) { 

$to = $_GET["to"]; 

if (is_null($from) || is_null($to) || $from > $to) { 

die("Invalid from and to parameters!"); 

// scrape only the pagination pages whose number goes 

// from "$from" to "$to" 

for ($paginationNumber = $from; $paginationNumber <= $to; $paginationNumber++) { 

// scraping logic... 

// write the data scraped to a database/file

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

https://instance.com/scripts/scrapeme.live/scrape-products.php?from=1&to=5 

https://instance.com/scripts/scrapeme.live/scrape-products.php?from=6&to=10 

И так далее.

Вот и все. Благодарим за то, что прошли этот путь с Dexodata, этичной экосистемой прокси, применяющей стандарты KYC и AML для гарантии вашей безопасности.

Назад


Сбор данных - проще с Dexodata

Попробовать сейчас Написать в Отдел продаж