W dzisiejszym wpisie nieco bardziej techniczne sprawy, ale bardzo cenne dla osób, które przy pomocy R chcą pobierać dane ze stron WWW.
Przedstawię trzy narzędzia, które w tym pomogą. Dwa pomogą na pewno, trzecie to bardziej ciekawostka do robienia screen shotów.
Jak dobrać się do tabelki na stronie?
Często jakieś dane, które nas interesują są opublikowane na stronach WWW. Można je przepisać ręcznie, można je wciągnąć do Excela (jest odpowiedni, w miarę intuicyjny importer tabelek dostępny pod guzikiem Z sieci Web w menu Dane). Ale nas interesuje oczywiście w R (i Python, ale to nie w tym odcinku).
W dzisiejszych zajęciach przydadzą nam się biblioteki:
R
1
2
3
4
5
6
7
library(tidyverse)
library(rvest)
library(glue)
library(stringr)
library(lubridate)
library(knitr)
library(kableExtra)
Za przykład niech posłuży nam strona o polskich województwach z Wikipedii. Przerwij na chwilę czytanie tego tekstu i przejdź na tę stronę (kliknij środkowym klawiszem myszki czy tam rolką w link). Na stronie mamy kilka tabelek, pobierzemy sobie jedną (pierwszą).
Teraz potrzebujemy się dobrać do tabelek. W tym miejscu przyda się wiedza na temat struktury dokumentu HTML (kilka podstawowych tagów, drzewo tagów i zagnieżdżenia jednych tagów w drugich) oraz CSS (klasy, i identyfikatory elementów, czyli class i id). Taką wiedzę można znaleźć w innych miejscach – poszukajcie.
Korzystając z pakietu rvest mamy dwa sposoby wyboru elementów. Jeden to ścieżka do elementu w postaci XPath drugi to wędrówka po drzewie HTML z podaniem odpowiednich klas.
Zacznijmy od tego pierwszego.
Tabele znajdują się na stronie w ścieżce:
div o id bodyContent
w którym jest div o id mw-content-text
wewnątrz którego mamy div o klasie mw-parser-output
i zobaczmy zawartość jej kilku kolumn (1, 2, 3 i 9):
R
1
tabelka[c(1:3,9)]
TERYT
województwo
miasto wojewódzkie
tablica rejestracyjna
2
dolnośląskie
Wrocław
D
4
kujawsko-pomorskie
Bydgoszcz ¹Toruń ²
C
6
lubelskie
Lublin
L
8
lubuskie
Gorzów Wielkopolski ¹Zielona Góra ²
F
10
łódzkie
Łódź
E
12
małopolskie
Kraków
K
14
mazowieckie
Warszawa
W
16
opolskie
Opole
O
18
podkarpackie
Rzeszów
R
20
podlaskie
Białystok
B
22
pomorskie
Gdańsk
G
24
śląskie
Katowice
S
26
świętokrzyskie
Kielce
T
28
warmińsko-mazurskie
Olsztyn
N
30
wielkopolskie
Poznań
P
32
zachodniopomorskie
Szczecin
Z
Prawda, że proste?
Strony i treść podstron
Zajmijmy się zatem czymś trudniejszym. Wyobraźmy sobie, że mamy stronę z listą linków do innych podstron. Ta lista jest stronicowana, a nas interesują informacje zawarte na tych właściwych stronach. Czyli: mamy stronicowaną listę artykułów, a interesuje nas jakaś zawartość artykułu. Zamiast artykułów może być na przykład lista ogłoszeń, a interesować mogą nas jakieś parametry ogłoszenia.
Pozostańmy przy przykładzie z artykułami, na warsztat weźmiemy te z Wiadomości portalu Gazeta.pl. Wchodzimy zatem na stronę główną Wiadomości i jedziemy na sam dół, a następnie klikamy w którąś kolejną stronę. W efekcie widzimy, że link dla strony drugiej wygląda tak: http://wiadomosci.gazeta.pl/wiadomosci/0,114871.html?str=2_19834953, a dla trzeciej http://wiadomosci.gazeta.pl/wiadomosci/0,114871.html?str=3_19834953.
Parametr str odpowiada za numer strony. Sprawdzamy jeszcze co się stanie dla str=1. Dzieje się to co powinno – mamy pierwszą stronę. Wykorzystamy to i pobierzemy linki do artykułów z pierwszych 5 stron.
# dodajemy zgromadzone linki z jednej strony do wszystkich zebranych wcześniej linków
linki<-c(linki,linki_tmp)
}
W efekcie zebraliśmy 115 linków. Ale uwaga – nie wszystkie linki prowadzą do artykułów (artykuły to strony typ numer 7, czyli urle zaczynające się od http://wiadomosci.gazeta.pl/wiadomosci/7,) Zostawmy więc tylko te urle, które prowadzą do artykułów:
Zostało 111 sztuk. Dlaczego zrobiliśmy takie przesianie? Ano dlatego, że szablon strony innej niż siódemka wymagałby innej obsługi zbierania danych ze strony z treścią. Aby nie komplikować procesu po prostu się ograniczymy.
Za chwilę zbierzemy treści artykułów, ale a co jeśli się nie uda pobrać strony? Skrypt nam się wywali. Aby do tego nie dopuścić skorzystamy ze świetnej funkcji safely() z pakietu purrr mapując funkcję read_html(). Pozwoli nam to sprawdzić czy pobranie strony się udało czy nie (bo na Czerskiej założyli nam bana, albo po prostu padła sieć ;)
R
1
safe_read_html<-safely(read_html)
Teraz dla każdego z artykułów pobierzemy autora, datę publikacji i lead. Ponieważ jest to czynność mocno powtarzalna – zrobimy do tego stosowną funkcję.
Na szczęście szablony stron Gazeta.pl są bardzo czytelnie przygotowane, każdy właściwie element ma swoją klasę i wystarczyło skorzystać z odpowiednich selektorów. Wykorzystałem XPath, ale równie dobrze działa wybieranie elementów po CSSie. Zanim napisałem powyższy kod otworzyłem stronę artykułu w Chrome, kliknąłem w “Zbadaj” i wio, element po elemencie, sprawdzałem jaką ma klasę, w jakim znaczniku HTML jest zapisany. I czy jest tekstem wewnątrz znacznika, czy też jego atrybutem. Tego inaczej nie da się zrobić – potrzebna jest praca z przeglądarką.
Mając gotowe narzędzie dla wszystkich zgromadzonych urli wywołujemy funkcję i zbijamy dane w jedną długą tabelę (co trochę trwa):
R
1
2
articles<-linki%>%
map_df(get_article_details)
Zebraliśmy 107 wierszy w tabeli. Czyli nie udało się dla wszystkich zgromadzonych z indeksu linków. Ale to nic… jak mawia moje młodsze dziecko – nie o to w tym chodzi.
Co z tymi danymi można zrobić? A na przykład sprawdzić jaki autor publikuje najczęściej:
Albo coś więcej, ale o tym już pisałem ponad pół roku temu! Tam też znajdziecie (w pierwszej części tekstu) informacje jak artykuły zostały pobrane.
Strony dynamiczne i formularze
Wszystko ładnie pięknie działa w przypadku stron statycznych, które nie doczytują sobie żadnych danych w trakcie. A co jeśli te mechanizmy nie zadziałają? Pozostaje nam udawanie klikania po stronie, tak jakbyśmy to sami robili. Z pomocą przychodzi Selenium którego warto się nauczyć. W R mamy bibliotekę
R
1
library(RSelenium)
Idea Selenium to uruchomienie rzeczywistej przeglądarki na jakiejś wirtualnej maszynie i udawanie prawdziwego zachowania użytkownika. Wirtualną maszynę zapewni nam Docker. Kiedyś może więcej o tym napiszę, na dzisiaj wystarczy instalacja (tutaj opisano jak to zrobi na Ubuntu).
Po zainstalowaniu Dockera instalujemy obraz z przeglądarką – w tym przypadku z Chrome. W konsoli wpisujemy po prostu:
R
1
docker pull selenium/standalone-chrome
Następnie uruchamiamy maszynę z przeglądarką, możemy to zrobić z poziomu R:
R
1
2
3
4
5
docker_pid<-system("docker run -d -p 4445:4444 --shm-size=2g selenium/standalone-chrome",intern=TRUE)
Zapisujemy sobie tutaj process id uruchomionej maszyny – po zakończeniu zabawy maszynę wyłączymy. Cyferki 4445 i 4444 mogą się różnic w Twojej konfiguracji, to kwestia otwartych portów na konkretnej maszynie, w konkretnej sieci.
W kolejnym kroku przygotowujemy driver do przeglądarki, czyli odpalamy maszynę Dockera:
To zaciemnienie wynika z samego Facebooka – loguję się na konto z innego niż zazwyczaj adresu, gdzieś tam w rogu na dole pokazuje się komunikat o tym informujący.
Gdzieś widziałem kod funkcji robiącej screen shota z podanego tweetu (wg jego ID) – to bardzo fajna sprawa. I na przykład jak @dziennikarz publikuje najbardziej popularne tweety dziennikarzy to warto je mieć czasem w formie obrazka… Śpieszmy się zapisywać tweety, tak szybko są czasem kasowane ;)
Pamiętaj, żeby wpaść na fanpage’a Dane i Analizy, zalajkować go i udzielać się w komentarzach ;) A jak chcesz – rzuć piątaka na serwer, korzystając z linku obok.
Jak zwykle przystępny artykuł. To kiedy ten Python?
Do „wybierania” elementów ze strony polecam dodatek do Chrome’a „SelectorGadget” — można sprawdzić klasę/id elementu i od razu przerobić dane informacje na xpatha. Z takich ciekawostek warto jeszcze się zapoznać z httrem, połączenie rvest + httr sporo czasu mi oszczędziło w pracy już na porównywaniu danych z różnych zabezpieczonych projektów i jakoś wolę to od RSelenium. Mniej setupu :)
Tak, httr też przydatny szczególnie w „rozmowach” z api.
A Python może w następnym odcinku? Bo pomysł na wpis jest dość banalny, więc można przy okazji nauczyć się Pythona (to o mnie).
Magda Mzwg
Banalne wpisy też się przydają. Jakoś próbuję się przekonać do Pandasów i Seaborna, ale w porównaniu do R jakieś to wszystkie takie toporne. Kwestia przyzwyczajenia zapewne.
Świetny artykuł. Dziękuję.
Pozwolę sobie polecać wszystkim zainteresowanym R lekturę. Bo jest to chyba jeden z wiodących blogów R „data journalism” w PL
A że robię prasówkę z konkretnego działu Rynek Zdrowia, zaraz wykorzystam. btw też marzy mi się nauka Pythona, ale najpierw chciałbym poznać dobrze R… i chyba poczekam aż będzie można używać Pythona zamiennie w R notebooks w RStudio.
Jak zwykle przystępny artykuł. To kiedy ten Python?
Do „wybierania” elementów ze strony polecam dodatek do Chrome’a „SelectorGadget” — można sprawdzić klasę/id elementu i od razu przerobić dane informacje na xpatha. Z takich ciekawostek warto jeszcze się zapoznać z httrem, połączenie rvest + httr sporo czasu mi oszczędziło w pracy już na porównywaniu danych z różnych zabezpieczonych projektów i jakoś wolę to od RSelenium. Mniej setupu :)
Tak, httr też przydatny szczególnie w „rozmowach” z api.
A Python może w następnym odcinku? Bo pomysł na wpis jest dość banalny, więc można przy okazji nauczyć się Pythona (to o mnie).
Banalne wpisy też się przydają. Jakoś próbuję się przekonać do Pandasów i Seaborna, ale w porównaniu do R jakieś to wszystkie takie toporne. Kwestia przyzwyczajenia zapewne.
Świetny artykuł. Dziękuję.
Pozwolę sobie polecać wszystkim zainteresowanym R lekturę. Bo jest to chyba jeden z wiodących blogów R „data journalism” w PL
A że robię prasówkę z konkretnego działu Rynek Zdrowia, zaraz wykorzystam. btw też marzy mi się nauka Pythona, ale najpierw chciałbym poznać dobrze R… i chyba poczekam aż będzie można używać Pythona zamiennie w R notebooks w RStudio.