Najlepsze biblioteki do scrapingu w PHP: Porównanie i praktyczne zastosowania

scraping PHP, Guzzle, Simple HTML DOM, WordPress, cron jobs, custom fields, chart.js, automatyzacja, dashboard, web scraping


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 DomCrawlerGoutte 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
    ]);
});

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:

  1. 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');
  1. 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);
           }
       }
   }
  1. 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);
  1. 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

Czy scraping może wpłynąć na wydajność mojej aplikacji?

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.

Jak często mogę uruchamiać scraping?

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.

Czy mogę używać tych metod do scrapingu stron z dynamiczną zawartością (AJAX, React)?

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.

Jak zabezpieczyć moje dane scrapowane przed nieautoryzowanym dostępem?

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.

Czy mogę używać scrapingu do aktualizacji istniejących rekordów zamiast tworzenia nowych?

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().