W tym artykule przygotujemy skrypt do regularnego pobierania obrazków z serwisu tumblr. Tumblr to serwis w którym użytkownicy mogą zamieszczać stworzone przez siebie zdjęcia i rysunki, a cechą charakterystyczną wrzuconych materiałów jest schematyczny adres URL, który możemy wykorzystać do cyklicznego ich pobierania. Artykuł ma na celu pokazanie od strony praktycznej zastosowania skryptów powłoki, których użycie może ułatwić nasze codzienne czynności.

Na potrzeby artykułu wybrałem blog: pointlessxnostalgic.tumblr.com.

Przede wszystkim oglądamy jak strona główna wygląda w przeglądarce internetowej. Strona jest prosta – są na niej miniatury prowadzące do strony z pełnym obrazkiem. Wniosek nasuwa się prosty – powinniśmy ściągnąć stronę główną, następnie podstronę z obrazkiem, a później sam obrazek. Ale może jest prostszy sposób. W drugiej zakładce otwieramy pierwszy rysunek, klikamy na niego drugim przyciskiem i kopiujemy adres obrazka. W pierwszej zakładce klikamy drugim przyciskiem na miniaturce i również kopiujemy adres obrazka. Wklejamy oba adresy jeden pod drugim otrzymując:

http://25.media.tumblr.com/tumblr_m9iw61Ad3L1rohj25o1_1280.jpg
http://24.media.tumblr.com/tumblr_m9iw61Ad3L1rohj25o1_500.jpg

Patrzymy czym różnią się adresy. Widzimy, że różnią się w dwóch miejscach – na początku adresu (24 lub 25) oraz pod koniec nazwy pliku (_500 lub 1280). Sprawdzamy następny obrazek:

http://25.media.tumblr.com/tumblr_m8cumsZYxg1rohj25o1_1280.jpg
http://25.media.tumblr.com/tumblr_m8cumsZYxg1rohj25o1_500.jpg

Tutaj obrazki różnią się tylko na końcu. Sprawdźmy zatem, czy zadziała adres

http://24.media.tumblr.com/tumblr_m9iw61Ad3L1rohj25o1_1280.jpg

Zadziałał, zatem początek nie ma dla nas znaczenia. Być może są to serwery cachujące, które trzymają kopię plików. Chociaż nie zawsze mogą ją posiadać. Różnica jest tylko na końcu nazwy pliku.

Wiemy już jak strona działa i wiemy również, że nie jest aż tak trudna do rozłożenia. Z tej analizy wychodzi, że są 3 etapy działania programu

  • pobranie strony głównej z linkami do najnowszych zdjęć,
  • wyciągnięcie linków do zdjęć z pobranej strony,
  • zapisanie obrazków w folderze ze zdjęciami.

Etap pierwszy – pobranie strony głównej

Ważną rzeczą jest dopilnować, aby pobrany plik zawsze nazywał się tak samo. Wykorzystamy do tego znane nam już polecenie wget:

wget http://pointlessxnostalgic.tumblr.com/ -O strona.html

Etap drugi – wyciągnięcie linków do zdjęć

Zaczynamy od obejrzenia kodu strony (możemy to zrobić dowolnym edytorem tekstu). Wyszukujemy frazę “_500.jpg”. Znajdujemy linię:

<a href="http://pointlessxnostalgic.tumblr.com/image/30456035787"><img src="http://24.media.tumblr.com/tumblr_m9iw61Ad3L1rohj25o1_500.jpg" alt="letting go and holding on" /></a>

Czyli w zasadzie, jeśli wykonalibyśmy polecenie:

cat strona.html | grep _500.jpg

to w wyniku otrzymamy tekst:

<a href="http://pointlessxnostalgic.tumblr.com/image/30456035787"><img src="http://24.media.tumblr.com/tumblr_m9iw61Ad3L1rohj25o1_500.jpg" alt="letting go and holding on" /></a>
<a href="http://pointlessxnostalgic.tumblr.com/image/30133228668"><img src="http://24.media.tumblr.com/tumblr_m9aa6aMckr1rohj25o1_500.jpg" alt="a note, discovered in a piano bench" /></a>
<a href="http://pointlessxnostalgic.tumblr.com/image/30108217080"><img src="http://25.media.tumblr.com/tumblr_m99q1nKPJz1rohj25o1_500.jpg" alt="j.m. barrie" /></a>
<a href="http://pointlessxnostalgic.tumblr.com/image/29626521703"><img src="http://25.media.tumblr.com/tumblr_m8wqpzJUM81rohj25o1_500.jpg" alt="thirstthank you to braver-than-you-believe for this quote!&nbsp;" /></a>
<a href="http://pointlessxnostalgic.tumblr.com/image/29563426006"><img src="http://25.media.tumblr.com/tumblr_m8v29cDFoX1rohj25o1_500.jpg" alt="today and today and todaythank you to sorry-about-the-mad-person for sharing this lovely quote!" /></a>
<a href="http://pointlessxnostalgic.tumblr.com/image/29494222929"><img src="http://24.media.tumblr.com/tumblr_m8t8ifQwXb1rohj25o1_500.jpg" alt="eternity" /></a>
<a href="http://pointlessxnostalgic.tumblr.com/image/29202711916"><img src="http://25.media.tumblr.com/tumblr_m8lp2qwt5G1rohj25o1_500.jpg" alt="alone" /></a>
<a href="http://pointlessxnostalgic.tumblr.com/image/29010846306"><img src="http://25.media.tumblr.com/tumblr_m8glflj9v21rohj25o1_500.jpg" alt="TRUST LIFE" /></a>
<a href="http://pointlessxnostalgic.tumblr.com/image/28863823900"><img src="http://25.media.tumblr.com/tumblr_m8cumsZYxg1rohj25o1_500.jpg" alt="I hope." /></a>

Widzimy, że nasz plik z obrazkiem jest umieszczony w kodzie między cudzysłowami. Należałoby go stamtąd wyciągnąć. Możemy to zrobić za pomocą prostego użycia polecenia trs:

cat strona.html | grep _500.jpg | trs -e '\" \n' | grep _500.jpg

Mamy zatem bezpośrednie linki do miniatur, teraz wypadałoby jeszcze zamienić tekst _500 na _1280:

cat strona.html | grep _500.jpg | trs -e '\" \n' | grep _500.jpg | trs -e '_500 _1280'

W wyniku otrzymujemy taką treść:

http://24.media.tumblr.com/tumblr_m9iw61Ad3L1rohj25o1_1280.jpg
http://24.media.tumblr.com/tumblr_m9aa6aMckr1rohj25o1_1280.jpg
http://25.media.tumblr.com/tumblr_m99q1nKPJz1rohj25o1_1280.jpg
http://25.media.tumblr.com/tumblr_m8wqpzJUM81rohj25o1_1280.jpg
http://25.media.tumblr.com/tumblr_m8v29cDFoX1rohj25o1_1280.jpg  
http://24.media.tumblr.com/tumblr_m8t8ifQwXb1rohj25o1_1280.jpg
http://25.media.tumblr.com/tumblr_m8lp2qwt5G1rohj25o1_1280.jpg
http://25.media.tumblr.com/tumblr_m8glflj9v21rohj25o1_1280.jpg
http://25.media.tumblr.com/tumblr_m8cumsZYxg1rohj25o1_1280.jpg

W ten sposób otrzymujemy listę plików do ściągnięcia. Etap drugi zakończony, ale widzimy, że możemy to polecenie nieco skrócić. Po pierwsze – możemy wyrzucić pierwsze odsiewanie grepem:

cat strona.html | trs -e '\" \n' | grep _500.jpg | trs -e '_500 _1280'

Po drugie – możemy lekko modyfikując frazę wyszukiwaną grepem przenieść wszystkie warunki dla trs w jedno wywołanie:

cat strona.html | trs -e '\" \n _500 _1280' | grep _1280.jpg

W ten sposób otrzymaliśmy listę, którą możemy zapisać w pliku lista.txt

cat strona.html | trs -e '\" \n _500 _1280' | grep _1280.jpg > lista.txt

Ok, mamy zatem to co chcieliśmy osiągnąć w etapie drugim.

Etap trzeci – ściągnięcie plików z listy

Standardowo, posługujemy się w tym miejscu wgetem z przełącznikiem -i (od input-file)

wget -c -i lista.txt

Dobra, podsumujmy to co zrobiliśmy:
Zapisujemy nasz plik z całym kodem pod nazwą skr_tumblr, powinien on wyglądać w ten sposób:

#etap pierwszy:
wget http://pointlessxnostalgic.tumblr.com/ -O strona.html
#etap drugi:
cat strona.html | trs -e '\" \n _500 _1280' | grep _1280.jpg > lista.txt
#etap trzeci:
wget -c -i lista.txt

Uruchamiamy wpisując bash skr_tumblr. Zadziałał? Gratulacje!

No ale zawsze można pomarudzić – dziś ściągnęliśmy kilka plików, ale za miesiąc może być tych plików tysiące. Nasz skrypt się w tym po prostu zgubi. Zatem trzeba w wgecie wpisać folder zapisu obrazków (musi wcześniej istnieć):

#etap pierwszy:
wget http://pointlessxnostalgic.tumblr.com/ -O strona.html
#etap drugi:
cat strona.html |trs -e '\" \n _500 _1280' | grep _1280.jpg > lista.txt
#etap trzeci:
wget -c -i lista.txt -P ./obrazki

No dobra, ale jak znów odpalę skrypt, to znów się ściągają i to trwa i trwa i trwa. No cóż, trzeba zatem wymyślić jakiś mechanizm, który zablokuje ściąganie już pobranych plików. Pomyślmy – fajnie by było, jakby ściągnięte pliki zapisywały się na jakiejś liście i ta lista byłaby wykluczona z pobierania a do tego jakby sama się aktualizowała.

Zatem po kolei:

  • wyciągamy listę obrazków do pobrania z pliku strona.html
  • porównujemy ją z istniejącą listą usuwając powtarzające się wpisy
  • ściągamy tylko pliki z nowej listy

Zajmujemy się zatem tym etapem:

cat strona.html | trs -e '\" \n _500 _1280' | grep _1280.jpg > lista.txt

Chcielibyśmy, aby lista pobranych plików znajdowała się w pliku lista_pobrane.txt a lista plików do ściągnięcia w pliku lista_sciagane.txt. Najprostszy sposób – linię modyfikujemy dodając polecenie grep, które z pliku lista.txt odsieje to, co znajduje się w pliku lista_pobrane.txt.

cat lista.txt | grep -v -f lista_pobrane.txt > lista_sciagane.txt

Po tej operacji chcielibyśmy zaktualizować listę z pobranymi obrazkami:

cat lista.txt lista_pobrane.txt | sort | uniq > lista_pobrane1.txt
mv lista_pobrane1.txt lista_pobrane.txt

Niestety, podanie tego w jednej linii z pominięciem pliku lista_pobrane1.txt spowodowałoby, że okazyjnie plik lista_pobrane.txt stawałby się pusty. Czyli nasz etap drugi zamykałby się w kodzie:

cat strona.html |trs -e '\" \n _500 _1280'|grep _1280.jpg > lista.txt
cat lista.txt | grep -v -f lista_pobrane.txt > lista_sciagane.txt
cat lista.txt lista_pobrane.txt | sort | uniq > lista_pobrane1.txt
mv lista_pobrane1.txt lista_pobrane.txt

Należy pamiętać, aby przed pierwszym uruchomieniem stworzyć plik lista_pobrane.txt. Po jakimś miesiącu zauważymy, że skrypt wywołuje się coraz wolniej i po analizie okazuje się, że spowalnia go grep. W zastosowaniach, w których grep ma odsiać bardzo długą listę, program ten nie do końca się sprawdza. W takich przypadkach możemy zastąpić go poleceniem sort połączonym z uniq z opcją -u:

cat strona.html |trs -e '\" \n _500 _1280'|grep _1280.jpg > lista.txt
cat lista.txt lista_pobrane.txt | sort | uniq -u > lista_sciagane.txt
cat lista.txt lista_pobrane.txt | sort | uniq > lista_pobrane1.txt
mv lista_pobrane1.txt lista_pobrane.txt

Pamiętamy również, że w etapie trzecim powinniśmy używać pliku lista_sciagane.txt.

Etap czwarty – efekt końcowy

Reasumując, nasz skrypt po tych kilku zmianach wygląda w ten sposób:

#etap pierwszy:
wget http://pointlessxnostalgic.tumblr.com/ -O strona.html
#etap drugi:
cat strona.html |trs -e '\" \n _500 _1280'|grep _1280.jpg > lista.txt
cat lista.txt lista_pobrane.txt | sort | uniq -u > lista_sciagane.txt
cat lista_sciagane.txt lista_pobrane.txt | sort |uniq > lista_pobrane1.txt
mv lista_pobrane1.txt lista_pobrane.txt
#etap trzeci:
wget -c -i lista_sciagane.txt -P ./obrazki

No dobra, mamy skrypt, który działa, ale trochę niewygodnie jest go codziennie odpalać z palca. Dobrze by było go umieścić w CRONie z. Aby to zrobić, należy przygotować do tego nasz skrypt przez podanie mu folderów w których ma pracować. Ja to zrobię definiując zmienną $folder jako /home/arek/tumblr a następnie wpisując ją w każdym koniecznym miejscu. Dodatkowo wyjście wgeta będę kierował do pliku wget.log również w zdefiniowanym folderze. Wskazane jest również w pierwszej linii zdefiniować powłokę bash, w której ma działać nasz skrypt:

#!/bin/bash
#definicje zmiennych:
folder=$(echo -n "/home/arek/tumblr")
#etap pierwszy:
wget http://pointlessxnostalgic.tumblr.com/ -O $folder/strona.html -a $folder/wget.log
#etap drugi:
cat $folder/strona.html |trs -e '\" \n _500 _1280'|grep _1280.jpg > $folder/lista.txt
cat $folder/lista.txt $folder/lista_pobrane.txt | sort | uniq -u > $folder/lista_sciagane.txt
cat $folder/lista_sciagane.txt $folder/lista_pobrane.txt | sort | uniq > $folder/lista_pobrane1.txt
mv $folder/lista_pobrane1.txt $folder/lista_pobrane.txt
#etap trzeci:
wget -c -i $folder/lista_sciagane.txt -P $folder/obrazki -a $folder/wget.log

Teraz możemy już nadać skryptowi prawa wykonywania przez użytkownika i wstawić go w CRONie. Jeśli dany blog by dodawał więcej zdjęć, lub polecenie chcielibyśmy używać znacznie rzadziej, to zawsze możemy zmodyfikować pierwszy etap na przykład tak:

wget http://pointlessxnostalgic.tumblr.com/ http://pointlessxnostalgic.tumblr.com/page/2 http://pointlessxnostalgic.tumblr.com/page/3 -O ./strona.html -a ./wget.log

Podobne artykuły

Programowanie

przez -
14 5175
Konsola

przez -
13 846
  • WalDo

    Dla skryptu to bez znaczenia, ale dla porządku warto zauważyć, że na początku linków (http://25.media.tumblr.com/... czy http://24.media.tumblr.com/…)można spokojnie pominąć cyfry. Wystarczy http://media.tumblr.com/...

    • TomJ

      A czemu działa bez cyferki?

    • WalDo

      Czytaj tekst to będziesz wiedział bez pytania: "Być może są to serwery cachujące, które trzymają kopię plików."

    • arek

      Admin może się zdziwić jeśli na wszystkich podstronach linki do plików jpg zawierają w hoście liczby, a z jakiegoś IP ma zapytania o pliki bez liczb w hoście. To jasne wskazanie na skrypt mogący służyć do hurtowego pobierania plików. W zasadzie można taką analizę zrobić prostym skryptem i od razu nagrodzić IP za nadgorliwość.

    • WalDo

      Słusznie. O tym nie pomyślałem, jednak dość długie wywody nt. tego co ma 24 a co 25 w nazwie skłoniły mnie do wpisania tej uwagi. W sumie mógłbyś pominąć tę część w artykule, bo studium przypadku nie dotyczy adresów a sposobu wykorzystania pewnej systematyki linków opartej ostatecznie o "grep _500.jpg".
      I żeby nie było wątpliwości – nie umniejszam wartości artykułu jako całości.

  • Greg

    Super Case Study! Takiego czegoś mi trzeba było :) Szkoda, że nie każdy skrypt jest tak opisywany :(

  • o_O

    Po co to zapisywanie do plików? Nie można porządnie przechować tego w zmiennych? Wystarczy wget -O- i trochę pomyśleć.
    Jeśli już pliki, to też porządnie: tymczasowe z nazwami generowanymi przez tempfile.

    • Gerard Stańczak

      dobra porada, dzięki ;)

    • Makar

      Łatwiej analizuje się działanie jak ma się dostęp do plików tymczasowych. Prosty dostęp.

  • Makar

    Fajnie by było zrobić więcej takich case study.

  • przemek

    Witam. Skrypt oczywiscie dziala, ale nie pobiera wszystkiego. Ja na swoim blogu mam 4 tys notek. Natomiast skrypt pobiera tylko okolo 10. Co zrobic aby pobral wszystko?