Dane z tabel WWW - regular expression

Dane z tabel WWW

Jeśli ktoś publikuje jakieś dane w internecie, to robi to zazwyczaj w taki sposób, by można się było z tymi danymi łatwo zapoznać. Jeśli danych jest dużo, to dobrym zwyczajem jest, poza umieszczeniem ich w tabeli html, udostępnienie pliku xls, csv, czy choćby txt. Dzięki temu użytkownik będzie miał możliwość łatwego pobrania danych i ich dalszej analizy. Gdy jednak gotowy plik z danymi nie istnieje, nie wszystko stracone. Są sposoby, by dość łatwo skopiować dane ze strony www bezpośrednio w środowisku MATLAB. Pomagają w tym wyrażenie regularne.

W czym problem

Do umieszczenia tego wpisu zainspirowała mnie strona www.air24.pl na której można znaleźć informacje o stopniu zanieczyszczenia powietrza pyłami w mojej okolicy. Dane są wyświetlane w tabeli na stronie ale żadne pliki do pobrania nie zostały umieszczone. Nie ma też żadnych informacji kontaktowych do administratora strony. Wygląda na to, że jedyny sposób na pozyskanie danych i stworzenie historycznej bazy danych polega na comiesięcznym kopiowaniu tabeli bezpośrednio ze strony www. Można taką operację wykonać "ręcznie", tj. można zaznaczyć dane w tabeli i skopiować je do arkusza excel, jednak istnieje lepsze rozwiązanie. Stronę można załadować do MATLABa i korzystając z wyrażeń regularnych wyłuskać interesujące nas dane.

Gotowiec

Na początku dobra wiadomość dla tych, którzy w temat wyrażeń regularnych nie chcą się wgryzać i potrzebują po prostu narzędzia do kopiowania danych ze stron www. Narzędzie takie istnieje, można je pobrać z MATLAB Central i przy sprzyjających okolicznościach pomoże w większości przypadków. Ja również w pierwszej kolejności chciałem po powyższe narzędzie sięgnąć, okazało się jednak, że w przypadku strony air24.pl gotowiec nie zadziała. Na stronie widać pojedynczą tabele ale po zerknięciu w kod html okazuje się, że każdy wiersz to osobna tabela. W takich przypadkach pozostaje albo przerabianie gotowca albo stworzenie własnej procedury, co paradoksalnie, często okazuje się szybszym rozwiązaniem 🙂

Czytanie stron w MATLABie

Funkcja umożliwiająca skopiowanie całego tekstu html do zmiennej tekstowej to webread.

adres = 'http://83.12.141.66/env_stalowa_wola/pm2.php'; 
strona = webread(adres);

Zmienna strona ma ok. 300000 znaków... Poza samymi danymi znajduje się tam mnóstwo bardziej i mniej (częściej mniej) potrzebnych znaczników html. W zasadzie głównym problemem jest rozszyfrowanie struktury dokumentu. Przyglądając się źródłu strony widać, że dane umieszczone są zawsze pomiędzy znacznikami  <td>.



<table width="500" border="1" cellspacing="0" cellpadding="0">

<tr bgcolor="#FFFFFF">

<td div align="center" width="200" > 2017-08-01
10:00 </td>


		                                      

<td width="100" bgcolor="#FFFFFF" ><center> 4</td>


											  

<td width="100" style=" color: #FFFFFF; background-color:#57b108;" > <center>4</td>


											  

<td width="100" style=" color: #FFFFFF; background-color:#57b108;" ><center>8</td>

</tr>

</table>


<table width="500" border="1" cellspacing="0" cellpadding="0">

<tr bgcolor="#FFFFFF">

<td div align="center" width="200" > 2017-08-01
09:00 </td>


		                                      

<td width="100" bgcolor="#FFFFFF" ><center> 4</td>


											  

<td width="100" style=" color: #FFFFFF; background-color:#57b108;" > <center>5</td>


											  

<td width="100" style=" color: #FFFFFF; background-color:#57b108;" ><center>10</td>

</tr>

</table>



Wyrażenia regularne

Gotowego wyrażenia regularnego można się w pierwszej chwili przestraszyć. MATLABowy kod wyszukujący wszystkie liczby całkowite znajdujące się pomiędzy znacznikami td wygląda tak:

dane = regexp(strona, '(?<=>\s?)\d+(?=\s?</td>)', 'match');

Wyrażenie można jednak łatwo rozszyfrować, chodzi tu po prostu o to, by znaną regułę przedstawić MATLABowi. Szukamy wyłącznie liczb całkowitych, a te składają się z jednej lub więcej cyfr. W wyrażeniach regularnych pojedyncze cyfry wskazywane są za pośrednictwem wskaźnika \d Jeśli ma być ich jedna lub więcej, na końcu dopisać należy plus. Stąd \d+.
Nie interesują nas wszystkie liczby, jakie się pojawiają w kodzie html, a jedynie takie, które znajdują się tuż przed zamykającym znacznikiem td. To prowadzi do wyrażenia:

\d+(?=</td>)

Zdarza się jednak, że w niektórych kolumnach przed znacznikiem td pojawia się jeszcze spacja. W związku z tym potrzebna jest drobna modyfikacja wyrażenia.

\d+(?=\s?</td>)

Po lewej stronie liczby zawsze znajduje się znak zamknięcia znacznika

(?<=>)\d+(?=\s?</td>)

I tutaj również może się zdarzyć, że pojawi się spacja.

expr_dane = '(?<=>\s?)\d+(?=\s?</td>)'

Mamy w końcu gotowe wyrażanie. Funkcja regexpr uruchomiona z parametrem match sprawia, że wszystkie elementy ciągu znaków znajdujące się w zmiennej strona, które spełniają regułę expr_dane, trafią do wynikowej macierzy typu cell. Pozostaje już tylko konwersja do typu numerycznego i odpowiednie ukształtowanie macierzy.

dane = regexp(strona, expr_dane, 'match');
dane = reshape(str2double(dane),3,[]);
dane = dane';

I gotowe! Prawie. Z tabeli można też wyciągnąć informacje o czasie pomiaru. Można to zrobić przygotowując kolejne wyrażenie regularne.

expr_daty = '(?<=>\s?)\d+-\d+-\d+
\d+:\d+(?=\s?</td>)';
dataPomiaru = regexp(strona, expr_daty, 'match')';
dataPomiaru = datetime(strrep(dataPomiaru, '
', ' ')); % należy jeszcze usunąć znacznik br który zaplątał się między datą i godziną

Na tym etapie dysponujemy macierzą danych i wektorem z informacją o czasie rejestracji poszczególnych wyników. Dobrym pomysłem będzie połączenie tych danych w ramach jednej tabeli.

tabela = [table(dataPomiaru), array2table(dane, 'VariableNames', {'PM_1','PM_2_5','PM_10'})];

Teraz już naprawdę prawie gotowe! Wystarczy tylko uruchomiać systematycznie skrypt przez kilkanaście następnych miesięcy i będą jakieś dane do analizy 😉

2 myśli nt. „Dane z tabel WWW - regular expression”

    1. To nawet dobry pomysł, pewnie w przyszłym miesiącu się coś pojawi na ten temat.

Dodaj komentarz

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