[cURL] Jak pobrać wszystkie demotywatory z głównej?

Lut 05
2011

piratSerwis demotywatory.pl jest znany zapewne każdemu internaucie. Gromadzi on obrazki wrzucane przez użytkowników z ironicznymi podpisami. W godzinach szczytu ciężko jest jednak przeglądać stronę główną. Nic nie stoi na przeszkodzie, aby wszystkie te obrazki pobrać na nasz lokalny dysk.

Pierwszą rzeczą, którą musimy zrobić to oszacować ilość demotywatorów na głównej. Biorąc pod uwagę, że na jednej podstronie jest ich wyświetlanych 10, a podstron z nimi w dniu 5 lutego 2011 jest 1953 możemy założyć, że do pobrania mamy niecałe 20 000 obrazków.

Nie jest to spora liczba. Do osiągnięcia spokojnie w 2-3 godziny działania skryptu na średniej klasy łączu internetowym.

Jeśli chodzi o wybór języka, zdecydowałem się skorzystać z PHP+MySQL oraz biblioteki cURL.

Ok, mając przygotowane założenia można przystąpić do analizy strony. Przeciętny demot jest wyświetlany w następujący sposób (załamałem linie, aby było czytelniej):

<img src="http://statichg.demotywatory.pl/uploads/201102/1296841564_by_Kavarito_500.jpg"
class="demot"
alt="Najprostsze wynalazki - To one tak bardzo ułatwiają nam codzienne życie "
height="5620"
width="500px">

Korzystając z wyrażeń regularnych wystarczy użyć poniższego zapisu, aby wyciągnąć wszystkie 10 ścieżek do obrazków.

$wzorzec = '#img src="(.*)" class="demot"#';

Rozbijmy nasze zadanie na dwa skrypty. Jeden, który pobierze i zapisze wszystkie adresy obrazków w bazie danych i drugi, który będzie je pobierał.

Uważam, że będzie to najbardziej optymalne. Raz, że będziemy mieć adresy demotów pod ręką, a dwa, zapisanie adresów obrazków potrwa krócej, niżeli samych obrazków. Dzięki tej technice przy odrobinie szczęścia unikniemy sytuacji, kiedy to w połowie pobierania danych, admin doda nowy obrazek, w efekcie czego całość ulegnie przesunięciu, a my pominiemy jakiś plik.

Do pobrania danych musimy sprawdzić, która z podstron jest ostatnią, na chwilę obecną jest to 1953. Dzięki wszechmocnemu adminowi, strony numerowane są w zmiennej GETdemotywatory.pl/page/ID, co znacząco ułatwia nam pracę – pętla for z dekrementacją.

Kod będzie wyglądał tak jak ten poniżej, pomijając jeden szczegół 😉

<?php
//MySQL
mysql_connect("localhost", "login", "haslo") or die("Error 1");
mysql_select_db("baza") or die("Error 2");
 
//OPTIONS
if(empty($argv[1])) {
	die('[!]Usage: $ php '.$argv[0].' <ostatnia strona>
');
}
 
//START
$a = array();
$curl = curl_init();
 
for($i=$argv[1]; $i>=0; --$i) { //od podanej strony do pierwszej
curl_setopt_array(
    $curl,
    array(
        CURLOPT_URL        => 'http://www.demotywatory.pl/page/'.$i,
        CURLOPT_RETURNTRANSFER    => true,
        CURLOPT_COOKIEFILE    => '/tmp/dd.cookie',
        CURLOPT_COOKIEJAR    => '/tmp/dd.cookie',
        CURLOPT_USERAGENT    => 'Mozilla/5.0 (X11; U; Linux x86_64; pl-PL; rv:1.9.2.13) Gecko/20101206 Ubuntu/10.04 (lucid) Firefox/3.6.13'
    )
);
 
//kod HTML strony zapisujemy do zmiennej $wynik
$wynik = curl_exec($curl);
 
#echo $wynik;

//luskamy co potrzeba
$wzorzec = '#img src="(.*)" class="demot"#';
preg_match_all($wzorzec, $wynik, $a[$i]);
 
#print_r($a[$i][1]); //sciezki do obrazkow
#print_r($a); //wszystko

for($j=9; $j>=0; --$j) { //10 obrazkow na stronie
	$adres = $a[$i][1][$j];
	mysql_query("INSERT INTO demoty (adres) VALUES ('$adres');");
}
 
echo $i." - Transfer completed
";
}
 
curl_close($curl);
?>

Nasza baza danych powinna przy dobrych wiatrach urosnąć do rozmiarów rzędu 20 000 rekordów w niecałe 15 minut.

phpmyamin

Teraz tylko odczytanie wszystkich rekordów jeden po drugim i zapisywanie ścieżek do katalogu.

<?php
mysql_connect("localhost", "login", "haslo") or die("Error 1");
mysql_select_db("baza") or die("Error 2");
 
function save_image($img, $fullpath) { //ta funkcja byla znaleziona gdzies w sieci
	$ch = curl_init($img);
	curl_setopt($ch, CURLOPT_HEADER, 0);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($ch, CURLOPT_BINARYTRANSFER,1);
	$rawdata = curl_exec($ch);
	curl_close($ch);
	if(file_exists($fullpath)){
		unlink($fullpath);
	}
	$fp = fopen($fullpath,'x');
	fwrite($fp, $rawdata);
	fclose($fp);
}
 
$zapytanie = mysql_query("SELECT * FROM demoty ORDER BY id ASC");
while($zapytanie && $rekord = mysql_fetch_assoc($zapytanie)) {
	$id = $rekord['id'];
	$adres = $rekord['adres'];
	save_image($adres, "demoty/$id.jpg");
	echo $id." - Download copleted
";
}
 
?>

Ten etap powinien zająć już nieco więcej czasu, jak wspomniano na początku, 2-3 godzinki.

No i cóż, to wszystko. Warto dodać, że otwarty katalog z ~20 000 zdjęciami (~1 GB) jest w stanie skutecznie przymulić system. Niegłupim pomysłem przez to wydaje się posegregowanie obrazków po 1000 w podkatalogach 😉

5 komentarzy w “[cURL] Jak pobrać wszystkie demotywatory z głównej?”

  1. piatkosia napisał/a:

    Ostatnio się bawiłam w przymulanie systemu. Może ci to pokażę.

  2. ar napisał/a:

    Można prościej, czytelniej i nie w pehapie:

    [code]
    #!/usr/bin/env ruby
    require ‚rubygems’
    require ‚open-uri’
    require ‚nokogiri’
    require ‚fileutils’

    DIR=”/home/arachnist/demotywatory”

    (62..1988).each { |page|
    puts „Page: #{page}”
    html = Nokogiri::HTML(open(„http://demotywatory.pl/page/#{page}”))
    html.xpath(„//img[@class=’demot’]/@src”).each { |attr|
    if attr.value =~ /[a-z]$/ then
    uri = attr.value.sub(„_500”, „”)
    puts uri.sub(/.*//, „”)
    i = open(uri.gsub(” „, „%20”)).read
    o = File.new(„#{DIR}/#{uri.sub(/.*//, „”)}”, „w”)
    o.puts i
    o.close
    end
    }
    }
    [/code]

  3. Nikow napisał/a:

    Robiłem coś podobnego swego czasu, ale problem „zbyt wielu zdjęć w jednym katalogu” można rozwiązać bardzo prosto, zrób foldery o stałej szerokości nazwy, u mnie to było 8 cyfr (a wystarczą 3 cyfry) i rób katalog na podstawie int(page/10). Daje Ci to 100obrazków na jeden folder.

  4. Maciek napisał/a:

    Zawsze lepiej stosować pętlę while, niż for.
    Ze względu na wydajność

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *