W erze big data, umiejętność efektywnego zbierania i analizy informacji staje się kluczowa dla sukcesu wielu projektów online. Jako programista PHP, miałem okazję zgłębić temat scrapingu i wykorzystać go do tworzenia dynamicznych, data-driven aplikacji. W związku z tym, w niniejszym artykule podzielę się moimi doświadczeniami z różnymi bibliotekami do scrapingu, prezentując ich zastosowania w różnych kontekstach.
Przegląd najlepszych bibliotek do scrapingu w PHP
1. Guzzle + Simple HTML DOM
Ta kombinacja okazała się niezwykle efektywna w wielu projektach. Z jednej strony, Guzzle zapewnia solidną obsługę żądań HTTP, podczas gdy z drugiej strony, Simple HTML DOM oferuje intuicyjny sposób parsowania HTML. (To moje ulubione połączenie, ale dlaczego to w dalszej części artykułu)
Zalety:
- Po pierwsze, wysoka wydajność Guzzle przy obsłudze żądań
- Ponadto, łatwość użycia Simple HTML DOM do ekstrakcji danych
- Co więcej, dobra integracja z różnymi frameworkami PHP
Wady:
- Jednakże, Simple HTML DOM może być mniej wydajny przy bardzo dużych dokumentach HTML
- Dodatkowo, konieczność zarządzania dwoma oddzielnymi bibliotekami
Przykładowe użycie w kontekście Laravel:
use GuzzleHttp\Client;
class NewsScraperService
{
public function scrapeLatestNews()
{
$client = new Client();
$response = $client->request('GET', 'https://example-news-site.com');
$html = str_get_html($response->getBody()->getContents());
$articles = $html->find('article.news-item');
foreach ($articles as $article) {
$title = $article->find('h2.title', 0)->plaintext;
$summary = $article->find('p.summary', 0)->plaintext;
// Zapisz do bazy danych lub przetwórz dane
News::create([
'title' => $title,
'summary' => $summary,
'source_url' => 'https://example-news-site.com'
]);
}
}
}
2. Symfony DomCrawler + Goutte
Dla bardziej zaawansowanych projektów, połączenie Symfony DomCrawler z Goutte oferowało potężne narzędzia do scrapingu. Jednakże, ważne jest, aby zaznaczyć, że od 1 kwietnia 2023 roku biblioteka Goutte (dużo osób dalej z niej korzysta, dlatego wspominam) została oznaczona jako przestarzała (deprecated) i zarchiwizowana przez właściciela.
Uwaga: Zgodnie z informacją w repozytorium Goutte, od wersji 4 biblioteka ta stała się prostym proxy do klasy HttpBrowser z komponentu Symfony BrowserKit. W związku z tym, przy nowych projektach zaleca się bezpośrednie użycie Symfony\Component\BrowserKit\HttpBrowser
zamiast Goutte\Client
.
Mimo to, warto omówić zalety i wady tego połączenia, które przez lata było popularnym wyborem wśród programistów PHP:
Zalety:
- Przede wszystkim, zaawansowane selektory CSS i XPath w DomCrawler
- Co więcej, Goutte zapewniał wygodny interfejs do obsługi sesji i formularzy
- Ponadto, była częścią ekosystemu Symfony, co gwarantowało wysoką jakość kodu
Wady:
- Niemniej jednak, może być nadmiarowe dla prostszych projektów
- Dodatkowo, wymaga głębszej znajomości Symfony dla pełnego wykorzystania możliwości
- Wreszcie, ze względu na przestarzałość Goutte, konieczne jest przejście na nowsze rozwiązania
Przykładowe użycie (historyczne, nie zalecane do nowych projektów):
use Goutte\Client;
use Symfony\Component\DomCrawler\Crawler;
$client = new Client();
$crawler = $client->request('GET', 'https://example-e-commerce.com/products');
$products = [];
$crawler->filter('.product-item')->each(function (Crawler $node) use (&$products) {
$products[] = [
'name' => $node->filter('.product-name')->text(),
'price' => $node->filter('.product-price')->text(),
'rating' => $node->filter('.product-rating')->attr('data-rating')
];
});
// Teraz możesz przetworzyć lub zapisać $products
Rekomendowana alternatywa:
Dla nowych projektów zaleca się korzystanie bezpośrednio z komponentów Symfony, w szczególności z Symfony\Component\BrowserKit\HttpBrowser
. Oto przykład, jak można dostosować powyższy kod:
use Symfony\Component\BrowserKit\HttpBrowser;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\DomCrawler\Crawler;
$browser = new HttpBrowser(HttpClient::create());
$crawler = $browser->request('GET', 'https://example-e-commerce.com/products');
$products = [];
$crawler->filter('.product-item')->each(function (Crawler $node) use (&$products) {
$products[] = [
'name' => $node->filter('.product-name')->text(),
'price' => $node->filter('.product-price')->text(),
'rating' => $node->filter('.product-rating')->attr('data-rating')
];
});
// Przetwarzanie $products pozostaje bez zmian
Ta zmiana zapewni, że Twój kod pozostanie aktualny i będzie korzystał z aktywnie wspieranych komponentów Symfony.
3. PHP-scraper
PHP-scraper to stosunkowo nowa biblioteka, która zyskuje popularność dzięki swojej prostocie i efektywności.
Zalety:
- Po pierwsze, intuicyjny interfejs API
- Następnie, wbudowana obsługa JavaScript
- Wreszcie, dobra wydajność
Wady:
- Jednakże, mniejsza społeczność w porównaniu do bardziej ugruntowanych bibliotek
- Ponadto, może wymagać dodatkowych konfiguracji w niektórych środowiskach
Przykładowe użycie z Slim Framework:
use Scraper\Scraper;
$app->get('/scrape-weather', function ($request, $response) {
$scraper = new Scraper();
$webpage = $scraper->go('https://example-weather-site.com');
$temperature = $webpage->querySelector('.current-temperature')->text();
$conditions = $webpage->querySelector('.weather-conditions')->text();
return $response->withJson([
'temperature' => $temperature,
'conditions' => $conditions
]);
});
Biblioteki do scrapingu w dużym stoponiu potrafią pomóc w automatyzacji wielu zadań tak samo jak Deployer o którym szerzej napisałem tutaj.
Praktyczne zastosowanie: Automatyzacja scrapingu dla WordPress
W jednym z moich projektów wykorzystałem kombinację Guzzle i Simple HTML DOM do stworzenia automatycznego systemu generowania postów w WordPress. Poniższy przykład jest uproszczoną wersją tego rozwiązania, pokazującą ogólną koncepcję, ale nie jest dokładną kopią kodu użytego w projekcie firmowym:
- Konfiguracja cron joba:
Najpierw utworzyłem niestandardowy cron job w WordPress, który uruchamiał się raz dziennie:
function setup_scraper_cron() {
if (!wp_next_scheduled('daily_data_scrape')) {
wp_schedule_event(time(), 'daily', 'daily_data_scrape');
}
}
add_action('wp', 'setup_scraper_cron');
add_action('daily_data_scrape', 'perform_daily_scrape');
- Funkcja scrapingu:
Następnie, główna funkcja scrapingu wyglądała podobnie do tej:
function perform_daily_scrape() {
require_once 'simple_html_dom.php';
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://source-website.com/data');
$html = str_get_html($response->getBody()->getContents());
$data_points = $html->find('div.data-point');
foreach ($data_points as $point) {
$title = $point->find('h2', 0)->plaintext;
$value = $point->find('span.value', 0)->plaintext;
$date = date('Y-m-d');
$post_id = wp_insert_post([
'post_title' => "Dane z dnia $date: $title",
'post_content' => "Wartość: $value",
'post_status' => 'publish',
'post_type' => 'scraped_data'
]);
if ($post_id) {
update_post_meta($post_id, 'data_value', $value);
update_post_meta($post_id, 'data_date', $date);
}
}
}
- Custom Fields:
Następnie, każdy zescrapowany punkt danych był zapisywany jako custom field:
update_post_meta($post_id, 'data_value', $value);
update_post_meta($post_id, 'data_date', $date);
- Dashboard z wykresami:
Wreszcie, na froncie stworzyłem prosty dashboard wykorzystujący Chart.js do wizualizacji danych:
function display_data_dashboard() {
$args = [
'post_type' => 'scraped_data',
'posts_per_page' => -1,
'orderby' => 'date',
'order' => 'ASC',
];
$query = new WP_Query($args);
$labels = [];
$data = [];
if ($query->have_posts()) {
while ($query->have_posts()) {
$query->the_post();
$labels[] = get_post_meta(get_the_ID(), 'data_date', true);
$data[] = get_post_meta(get_the_ID(), 'data_value', true);
}
}
wp_reset_postdata();
// Generowanie wykresu za pomocą Chart.js
echo '<canvas id="dataChart"></canvas>';
?>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
var ctx = document.getElementById('dataChart').getContext('2d');
var chart = new Chart(ctx, {
type: 'line',
data: {
labels: <?php echo json_encode($labels); ?>,
datasets: [{
label: 'Wartość danych',
data: <?php echo json_encode($data); ?>,
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
}]
}
});
</script>
<?php
}
Wnioski
Podsumowując, wybór odpowiedniej biblioteki do scrapingu zależy od specyfiki projektu. Z jednej strony, dla prostszych zadań, kombinacja Guzzle i Simple HTML DOM sprawdza się doskonale. Z drugiej strony, przy bardziej zaawansowanych projektach warto rozważyć Symfony DomCrawler z Goutte.
Co więcej, kluczowe jest odpowiedzialne podejście do scrapingu. W związku z tym, zawsze należy respektować politykę robotów strony źródłowej i nie przeciążać serwerów zbyt częstymi żądaniami.
Automatyzacja procesu scrapingu otwiera wiele możliwości – od generowania codziennych raportów po tworzenie dynamicznych dashboardów. Jednakże, pamiętajmy o optymalizacji bazy danych i regularnym czyszczeniu starych danych, aby utrzymać wydajność aplikacji.
FAQ
Przy prawidłowej implementacji, sam scraping nie powinien znacząco wpływać na wydajność. Niemniej jednak, kluczowe jest odpowiednie zarządzanie zebranymi danymi i optymalizacja zapytań do bazy danych.
To zależy od ilości danych i polityki strony źródłowej. Ogólnie rzecz biorąc, dla większości przypadków, raz dziennie jest wystarczające. W związku z tym, unikaj zbyt częstego scrapingu, aby nie przeciążać serwerów źródłowych.
Dla stron z dynamiczną zawartością JavaScript, może być konieczne użycie bardziej zaawansowanych narzędzi. W takim przypadku, rozważ użycie narzędzi takich jak Puppeteer lub, jeśli to możliwe, skorzystaj z dostępnego API.
Przede wszystkim, wykorzystaj odpowiednie mechanizmy zabezpieczeń swojego frameworka, takie jak kontrola dostępu i autoryzacja. Ponadto, rozważ szyfrowanie wrażliwych danych przed zapisem do bazy.
Oczywiście, możesz zaimplementować logikę, która sprawdza, czy rekord o danych kryteriach już istnieje, i aktualizuje go zamiast tworzyć nowy. Na przykład, w przypadku WordPress, użyj funkcji wp_update_post()
zamiast wp_insert_post()
.