Pobieranie strony offline w PHP
W tym artykule zbudujemy prostego pająka, który będzie chodził po wybranej stronie i pobierał jej zawartość na dysk do odczytu offline. Skorzystamy przy tym z wiedzy zawartej w artykule o web scrappingu.
Plan działania
Zanim zaczniemy, ustalmy szczegółowo co chcemy zrobić:
- Pobrać zawartość wskazanej strony początkowej. Adres tej strony będzie również wzorcowym adresem, którym będziemy się sugerować, aby nie wyjść poza domenę.
- Sprawdzić linki występujące na stronie i dodać je do listy (o ile wcześniej nie wystąpiły) i o ile nie wskazują na zewnątrz domeny
- Pobrać kolejne strony z uwzględnianiem listy.
W ten sposób powinniśmy pobrać całą zawartość strony dostępną z poziomu użytkownika.
Pobieranie zawartości strony
Aby pobrać stronę skorzystamy z algorytmu napisanego wcześniej do wspomnianego artykułu. Zmodyfikujemy jedynie element związany z kompresją – dodamy sprawdzanie czy strona jest skompresowana.
function getWebsite ($url) {
$opts = [
'http'=> [
'method'=>"GET",
'header'=>"Accept-Encoding: gzip, deflate, br\r\n" .
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\n" .
"Referer: https://eskim.pl\r\n" .
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.50\r\n"
]
];
$context = stream_context_create ($opts);
$compressed = file_get_contents ($url, false, $context);
$webpage = gzdecode ($compressed);
if ($webpage === false) return $compressed;
return $webpage;
}
Funkcja gzdecode
zwróci false
, jeżeli dekompresja się nie uda. Wtedy zwracamy po prostu stronę.
Sprawdzanie domeny
Pierwszy pobierany adres będzie wspomnianym adresem wzorcowym. Napiszmy algorytm, który to zweryfikuje:
function isInternalURL ($url, $current_url) {
if ( stripos ($current_url, $url) === 0 && stripos ($current_url, $url.'.') === false ) {
return true;
}
return false;
}
Powyższa funkcja sprawdza czy przekazany w pierwszym parametrze link znajduje się na początku drugiego ciągu. Dodatkowo sprawdzamy czy nie kończy się kropką na wypadek takiej konstrukcji:
eskim.pl.example.com
Parsowanie strony
Pobieramy stronę i parsujemy zgodnie z powyższym artykułem o web scrappingu. Tym razem jednak interesują nas linki, czyli tagi a
i atrybut href
.
function getLinks ($page) {
$links = [];
$website = new DOMDocument();
$website->loadHTML ($page); // zamień stronę na obiekt DOM
$as = $website->getElementsByTagName('a'); // pobierz wszystkie tagi a
foreach ($as as $a) {
if ($a->hasAttribute('href')) {
$links[] = trim ($a->getAttribute('href') );
}
}
return array_unique ($links);
}
Następnie wchodzimy w pierwszą stronę z linka i robimy dokładnie to samo – pobieramy i zapisujemy linki (dbamy dodatkowo, aby się nie powtarzały)
Przekierowania
Czasami pod linkiem znajduje się przekierowanie na inną stronę. file_get_contents
automatycznie obsługuje przekierowania. W naszym konkretnym przypadku nie do końca jest to wskazane (przekierowanie może być na zewnętrzną stronę). Aby wyłączyć tą funkcjonalność musimy zmodyfikować ustawienia pobierania. Dodajmy do http
, follow_location
i ustawmy je na false
'http' => [
'follow_location' => false
]
Możemy również pobierać tylko strony lokalne używając przekierowań. Kod zapożyczony ze stack overflow:
function get_url_contents_and_final_url(&$url)
{
do
{
$context = stream_context_create(
array(
"http" => array(
"follow_location" => false,
),
)
);
$result = file_get_contents($url, false, $context);
$pattern = "/^Location:\s*(.*)$/i";
$location_headers = preg_grep($pattern, $http_response_header);
if (!empty($location_headers) &&
preg_match($pattern, array_values($location_headers)[0], $matches))
{
$url = $matches[1];
$repeat = true;
}
else
{
$repeat = false;
}
}
while ($repeat);
return $result;
}
Powyższy kod sprawdza czy wystąpiło przekierowanie i jeżeli tak, pobiera stronę z przekierowania.. aż do skutku. Można tutaj dodać ograniczenie (np. maksymalnie 10 stron) oraz weryfikować przed pobraniem czy strona znajduje się w tej samej domenie.
Kod na GitHub – very-simple-spider udostępniony na licencji GPL 3.
Podsumowanie
- Pobieranie zawartości zostało opisane w artykule o web scrappingu.
- Sprawdzamy czy przeszukiwany link znajduje się w domenie
- Wyciągamy atrybuty
href
z tagówa
za pomocąDOMDocument
. - Wyłączamy obsługę przekierować podczas pobierania danych przy użyciu
file_get_contents
, ustawiając opcjęfollow_location
nafalse
.