"The Grid. A digital frontier. I tried to picture clusters of information as they move through the computer. What did they look like? Ships? Motorcycles? Were the circuits like freeways? I kept dreaming of a world I thought I’d never see. And then, one day, I got in." — Tron: Legacy

2012-09-30

Smalltalk : Seaside z użyciem Pharo, cz.2

(c) Robert Tinney

Dzisiaj postanowiłem pokazać jak w prosty sposób odczytać komentarze z bloga na portalu dobreprogramy.pl, który nie posiada usługi odczytu wiadomości za pomocą XML/JSON przez co trzeba parsować HTML. Dla niecierpliwych na końcu tekstu jest krótki film podsumowujący temat tego wpisu.

Odczyt zawartości strony internetowej robi się dość banalnie. Wystarczy w Workspace Pharo wpisać :

'http://www.google.com' asUrl retrieveContents contents.
 
Potem umieścić kursor klawiatury za kropką (bądź zaznaczyć myszką całą linijkę kodu) i nacisnąć [Ctrl+p] ("Print it"). Powinna pojawić się zawartość z podanego adresu. Tekst, który się pojawia jest już zaznaczony, więc można nacisnąć klawisz Del by go usunąć.

Jako, że tekst strony już mamy, to potrzebny będzie parser HTML. Będzie potrzebny, bo wbudowany parser XML tonie w zupie tagów luźno zapodanego HTMLa, gdzie zdarzają się niedomknięte tagi, ew. tagi zapisane niezgodnie z dokumentacją i wtrącenia w postaci JavaScriptu czy CSS i inne śmieci.

Dodatkowe biblioteki Smalltalka są przechowywane w repozytoriach. Z takiego dziś skorzystam. Jest to repozytorium Squeaka (inna odmiana Smalltalka zgodna z Pharo).

Aby zainstalować bibliotekę trzeba wejść do przeglądarki Monticello (dostępne z menu myszki). Następnie trzeba kliknąć w "+Package" nadać dowolną nazwę, np. "Soup". Potem klik w "+Repository" i wybieram opcję HTTP. Modyfikuję adres, który się pojawi na szablonie na:

location: 'http://www.squeaksource.com/Soup'

Klikam OK, potem w "Open". Załaduje się lista wersji pakietów. Trzeba teraz wybrać ConfigurationOfSoup - najnowszą wersję , u mnie jest 37 i kliknąć Load. ConfigurationOfSoup to pakiet obiektów, które pobiorą najnowszą wersję zupki. Aby się dowiedzieć jak go uruchomić trzeba otworzyć System Browser i znaleźć ConfigurationOfSoup. Można to zrobić poprzez menu z Find, górną belkę, lub przewinąć listę na sam koniec. Najnowsze pakiety są widoczne zawsze na końcu. W drugim okienku, które jest zwane klasowym lub instancji trzeba kliknąć jeszcze raz nazwę ConfigurationOfSoup, a potem pytajnik. Pod pytajnikiem zwykle jest dokumentacja obiektu.

Widać tam dwie linijki. Jako, że kod Smalltaka można wykonać z każdego okienka, zaznaczam linijkę z wersją #stable i wykonuję "Do it". skrypt pobierze i skompiluje resztę biblioteki.

Teraz można pobrać zupę do obiektu już za pomocą samej biblioteki (oczywiście wszystko to wpisuję w Workspace):

zupa := Soup fromUrl: 'http://*dobreprog***.pl/aeroflyluby/Domowy-sposob-na-Diode,36474.html'.

Tym razem użyłem prawdziwego adresu z bloga. Mam więc zupę z tagów i w tym momencie zaczyna się zabawa. Trzeba dowiedzieć się pomiędzy jakimi tagami trzymany jest blok komentarzy. Potem znaleźć pojedyncze wpisy, a następnie rozbić na:
  •     adres logo: #img
  •     nazwę komentującego: #nick
  •     treść komentarza: #text
Zrobiłem małe rozeznanie i znalazłem id tagu, w którym te komentarze siedzą:

ctl00_phContentLeft_panUpdateComment
 
Niestety Soup nie ma możliwości wyciągania tagów po identyfikatorze. Trzeba sobie napisać swoją funkcję. Najlepiej to zrobić zrzynając już z istniejącej:

findTagByClass: aString
    ^ self findTagByClass: aString ifAbsent: [nil]


Oczywiście tworzymy ją w instancji klasy SoupTag w pakiecie Soup-Core. Widać w kodzie, że funkcja ta wywołuje jeszcze inną, z parametrem ifAbsent, która to jest główną (rozszerzoną funkcją) do wyciągania tagu. Jej nazwę też trzeba podmienić. W sumie tworzymy dwie funkcje:

findTagByID: aString
    ^ self findTagByID: aString ifAbsent: [nil]


i

findTagByID: aString ifAbsent: aBlock
    self findTag: [:aTag | (aTag attributeAt: 'id') = aString]
        ifPresent: [:aTag | ^ aTag].
    ^ aBlock value


Mając te dwie funkcje mogę już wyciągnąć tag, w którym siedzą komentarze:

root:= aSoup findTagByID: 'ctl00_phContentLeft_panUpdateComment'.

Można użyć "Print it", które wyświetli treść tagu, lub dać "Do it" i podejrzeć obiekt root przez zaznaczenie go i użycie "Inspect it".

Teraz czas wyciągnąć pojedyncze pola komentarzy. Tutaj będzie trochę trudniej, bo id komentarzy zmienia się. To jest, id komentarzy jest w postaci: komentarz_124234. Niestety, Soup nie umożliwia wyciągania tagów z użyciem wyrażeń regularnych. Trzeba będzie sobie poradzić. I znów trzeba zmodyfikować nasze nowe funkcje:


findAllTagsByIDregX: aString
    ^ Array streamContents: [ :aStream |
                self findAllTagsByIDregX: aString
                    stream: aStream ]

i

findAllTagsByIDregX: aString stream: aStream
    self childTagsDo:
        [ :aTag | ((aTag attributeAt: 'id') isNil ) ifFalse:
            [((aTag id) matchesRegex: aString) ifTrue: [aStream nextPut: aTag ]].
        aTag findAllTagsByID: aString stream: aStream ]

oraz:

findAllTagsByID: aString
   
^ Array streamContents:  [ :aStream |
               
self findAllTagsByID: aString stream: aStream ]i

findAllTagsByID: aString stream: aStream
    self childTagsDo:
        [ :
aTag | (aTag attributeAt: 'id') = aString
            ifTrue: [
aStream nextPut: aTag ].
       
aTag findAllTagsByID: aString stream: aStream ]

Mając te funkcje teraz mogę wyciągnąć komentarze do tablicy:


komentIDregX
:= 'komentarz_[0-9]+'.
komentSoups := root findAllTagsByIDregX: komentIDregX.


Super. ;) Teraz utworzę obiekty na tagi komentarzy, nicków i logo komentujących:


komentClass := 'text-h75 tresc'. 
imgClass := 'border small float-left'.
nickClass := 'text-h65 font-heading display-inl_blk nick'.
nixesTags := root findAllTagsByClass: nickClass.
imgsTags := root findAllTagsByClass: imgClass.
komentTags := root findAllTagsByClass: komentClass.

OK. Teraz wyciągamy tekst i inne ciekawe rzeczy:


nixes := OrderedCollection new.imgs := OrderedCollection new. 
koments := OrderedCollection new.nixesTags do: [:tag | nixes add: (tag text) ].
imgsTags do: [:tag | imgs add: (tag src) ].
komentTags do: [:tag | koments add: (tag text) ].


Oto rezultat:


Jak ktoś chce sprawdzić czy adresy obrazków są dobre, to może wyświetlić np. pierwszy poleceniem:

(ImageMorph fromStream: ((imgs at: 1) asUrl retrieveContents contentStream)) openInWorld.


Aby pozbyć się obrazka trzeba wcisnąć Shift i kliknąć na obrazku środkowym przyciskiem myszki.

Oczywiście poprzedni kod można zapisać krócej używając słownika i listy. Słownik (Dictionary) to odpowiednik mapy w Javie. Ostateczna postać:

zupa := Soup fromUrl: 'http://www.d***y.pl/aeroflyluby/Domowy-sposob-na-Diode,36474.html'.
root:= zupa findTagByID: 'ctl00_phContentLeft_panUpdateComment'.

komentIDregX := 'komentarz_[0-9]+'.
komentClass := 'text-h75 tresc'.
imgClass := 'border small float-left'.
nickClass := 'text-h65 font-heading display-inl_blk nick'.

komentSoups := root findAllTagsByIDregX: komentIDregX.

komenty := OrderedCollection new.
komentSoups do: [:ks |
      koment := Dictionary new.
        koment at: #nick put: (ks findTagByClass: nickClass) text.
        koment at: #img put: (ks findTagByClass: imgClass) src.
        koment at: #text put: (ks findTagByClass: komentClass) text.
        komenty add: koment.
    ].

(ImageMorph fromStream: (((komenty at: 1) at: #img) asUrl retrieveContents contentStream)) openInWorld.


Podsumowanie na video w jeszcze innym stylu:


Na filmie można zauważyć, że nie tworzę funkcji przed wykonaniem kodu. Środowisko Smalltalka informuje mnie, że takowej nie ma i pozwala utworzyć brakującą. Po utworzeniu funkcji można kliknąć "Proceed" i środowisko zachowa się jakby nic nie było. W ten sposób także bardzo łatwo i szybko pisze się testy w konwencji TDD - test first. To nie żadna nowość. Smalltalk był pierwszym szeroko dostępnym środowiskiem, które było wyposażone w bibliotekę do testów jednostkowych xUnit (jUnit - wersja na Javę). To, czym dziś ludzie się zachwycają i biorą za nowość, było używane grubo ponad 30 lat temu.

W kolejnej części o tym jak to wszystko wyświetlić na stronie WWW.

Brak komentarzy:

Prześlij komentarz