magisterka/rozdzial_3.tex

349 lines
16 KiB
TeX
Raw Normal View History

\chapter{Ekstrakcja godzin rozpoczęcia mszy świętych}
\section{Ogólny zarys systemu}
System zaczyna działanie od zebrania jak największej ilości danych (nazwa parafii, adres, diecezja
itd.) o polskich parafiach ze strony deon.pl. Następnie odpytuje api Google'a w
celu znalezienia adresów internetowych parafii.
Dla każdej parafii dla której udało się znaleźć adres url pobierane są wszystkie
podstrony w odległości (sieć to graf) conajwyżej 3 od strony startowej.
Z dużej liczby stron parafialnych, za pomocą prostych reguł wyodrębnione zostają
te na których z dużym prawdopodbieństwem znajdują się godziny mszy świętych.
Każda godzina wraz z kontekstem w jakim się znajduje trafia do \textit{systemu
crowdsourcing'owego}, gdzie jest annotowana jako poprawna lub niepoprawna godzina mszy świętej.
Do zannotowanych danych zostają dołączone poprawne godziny mszy świętych
znalezione przez
regułowy ekstraktor mszy świętych o bardzo wysokiej precyzji. Dodatkowo w celu wyrównania
klas z nieodfiltrowanego zbioru stron parafialnych wylosowane zostają niepoprawne godziny mszy świętych.
Zebrane dane zostają użyte do wytrenowania klasyfikatora godzin opartego na
płytkich sieciach neuronowych.
Finalny ekstraktor godzin mszy świętych utworzony zostaje z połączenia
ekstraktora regułowego z ekstraktorem opartym na uczeniu maszynowym.
% \bigskip
% \newpage
\begin{figure}[tbh!]
\center
\includegraphics[width=1\hsize]{struktura_wyszukiwarki.png}
\caption{Struktura ekstraktora godzin mszy świętych.}
\label{struktura_pic}
\end{figure}
\newpage
\section{Zbieranie informacji o parafiach}
\begin{figure}[tbh!]
\center
\includegraphics[width=0.7\hsize]{crawler_adresow_trans.png}
\label{crawler_adresow_pic}
\end{figure}
Dane zostały zebrane z serwisu internetowego deon.pl, który zawiera 10130 parafii.
Warto zauważyć, że deon.pl posiada większość polskich parafii, ponieważ według
danych statystycznych GUS z 2016 roku w Polsce było
10255 parafii.
Dla każdej parafii zebrano:
\begin{itemize}
\item nazwę parafii,
\item miejscowość w której się znajduje,
\item województwo w którym się znajduje,
\item dokładny adres,
\item nazwę dekanatu do którego należy,
\item nazwę diecezji do której przynależy.
\end{itemize}
Do wydobycia danych został użyty skrypt w pythonie, który korzystał z parsera
html z biblioteki BeautifulSoup. Przy wysyłaniu zapytań do serwisu deon.pl zastosowano
algorym \textit{Expotential Backoff} REF, który prezentuje się następująco:
\begin{enumerate}
\item Wyślij zapytanie do serwisu.
\item Jeśli zapytanie się powiodło wróć do punktu nr 1, jeśli nie poczekaj 1.5s i wyślij kolejne zapytanie.
\item Jeśli zapytanie znów się nie powiodło odczekaj 2.25s i wyślij kolejne
zapytanie
\item W ogólności czekaj $1.5^t$ sekund zanim wyślesz kolejne zapytanie, gdzie
$t$ to liczba następujących po sobie nieudanych zapytań.
\end{enumerate}
Powyższy algorytm uodparnia skrypt na przejściowe problemy z połączeniem i
zapobiega zbyt częstemu odpytywaniu serwisu kiedy ten nie daje sobie rady ze
zbyt dużą liczbą zapytań.
\begin{table}
\centering
\def\arraystretch{1.1}
\begin{tabular}{ l l l l l l }
\textbf{Parafia} & \textbf{Miejscowość} & \textbf{Adres} & \textbf{Diecezja} & \textbf{Dekanat} & \textbf{Województwo} \\
\hline \\ [-2ex]
Bożego Ciała & Hel & ul. Gdań... & gdańska & Morski & pomorskie \\
Ducha Św. & Śrem & ul. Prym... & poznańska & Śrem & wielkopolskie\\
Św. Trójcy & Paszowice & Paszowic... & legnicka & Jawor & dolnośląskie\\
\\ [-1.5ex]
\end{tabular}
\caption{Fragment zebranych danych.}
\end{table}
\section{Wyszukiwanie stron internetowych parafii}
\begin{figure}[tbh!]
\center
\includegraphics[width=0.7\hsize]{crawler_url_trans.png}
\label{crawler_url_pic}
\end{figure}
\subsubsection{Pierwsze próby}
Do wyszukiwania adresów url parafii próbowano wykorzystać wyszukiwarki takie jak
Google i DuckDuckGo. Automatycznie wysyłano zapytanie złożone z konkatenacji
nazwy parafii, jej miejscowości i ulicy na której się znajduje. Wyszukiwarka Google dawała
zadowalające wyniki, jednak po kilkunastu zapytaniach blokowała adres ip. W
dodatku w warunkach użytkowania serwisu i w robots.txt Google zabrania
korzystania z pająków na ich wyszukiwarce.
DuckDuckGo nie blokowało adresu ip, ale zabraniało \textit{crawlowania} w robots.txt i słabo radziło sobie z polskimi
zapytaniami. W obu przypadkach powyższa metoda stwarzała kolejny problem do
rozwiązania - z wielu wyników wyszukiwania trzeba było wybrać ten który zawierał
adres url parafii.
\subsubsection{Rozwiązanie}
Po wieleokrotnych próbach poszukiwań znaleziono klucz do rozwiązania problemu
wyszukiwania adresów url jakim jest
\textit{Google Places Api} REF. Serwis \textit{Text Search} REF pozwala na wyszukanie miejsca
danego obiektu na
podstawie jego nazwy. Ponadto mając już wyszukany dany obiekt i jego
identyfikator można odpytać serwis \textit{Place Detail} REF, aby wyciągnąć więcej
szczegółów o danym miejscu. Między innymi można otrzymać adres url danego obiektu.
Jedynym minusem jest ograniczenie liczby zapytań do 1000 na 24 godziny. W
dodatku każde zapytanie do serwisu \textit{Text Search} traktowane jest jak 10
zapytań. Podając swoją kartę płatniczą można zwiększyć limit
zapytań do 150 000 na 24 godziny. Karta płatnicza jest potrzebna Google'owi do
identyfikacji osoby. Żadna opłata nie jest pobierana za korzystanie z api.
Dla każdej parafii wykonywane jest zapytanie do serwisu \textit{Text Search}
składające się z konkatenacji nazwy, parafii, jej miejscowości i ulicy na której
się znajduje. Jeśli nie zostanie znaleziony żaden obiekt wysyłane jest powtórne
zapytanie, lecz tym razem składające się tylko z nazwy parafii i jej
miejscowości. Zdarza się, że \textit{Text Search} zwraca kilka obiektów. W takim
przypadku brany jest adres url pierwszego obiektu z listy wyników.
Najczęściej jednak oba obiekty należą do tej samej parafii, więc mają taki sam
adres internetowy.
Powyższą metodą udało się zebrać adresy url dla ok. 5600 parafii.
\begin{figure}[tbh]
\center
\includegraphics[width=1\hsize]{amb_text_search.png}
\caption{Przykład dwóch obiektów zwróconych przez \textit{Text Search}, które
mają ten sam adres internetowy.}
\label{text_search_pic}
\end{figure}
\section{\textit{Crawlowanie} stron parafialnych}
\begin{figure}[tbh]
\center
\includegraphics[width=0.7\hsize]{crawler_parafii_general.png}
\label{crawler_parafii_general_pic}
\end{figure}
Crawler został napisany przy użyciu biblioteki Scrapy.
Punktem startowym jest pojedynczy adres url parafii podawany na wejście
programu. Z początkowego adresu url wydobywana jest domena w obrębie której
porusza się pająk. Oznacza to, że jedna instancja pająka zajmuje się ściąganiem
tylko jedenej parafii. W ramach jednej parafii pająk jest w stanie
asynchronicznie wysłać wiele zapytań do serwera i odbierać wiele odpowiedzi od serwera.
\subsection{Komponenty pająka}
\begin{description}
\item [Silnik] - odpowiada za kontrolę przepływu danych i komunikację między komponentami.
\item [Dyspozytor] - otrzymuje żądania od silnika, kolejkuje je i na
prośbę silnika odsyła z powrotem.
\item [Downloader] - jego zadniem jest ściąganie stron parafialnych i
przekazywanie ich silnikowi.
\item [Przetwarzacz danych] - zajmuje się końcową obróbką i zapisem danych.
\item [Spider]\footnote{Użyto angielskiej nazwy, aby rozróżnić
\textit{spider'a} (komponent pająka), od pająka (cały program
odpowiedzialny za crawlowanie stron parafialnych).}
- definuje sposób w jaki ściągać dane, między innymi jak parsować stronę i za jakimi
linkami podążać.
\item [Spider middleware] - programy pośredniczące między silnkiem, a
spider'em. Odpowiedzialne są za dodatkowe przetwarzanie danych wyjściowych
(dane parafialne i żądania) i wejściowych (odpowiedzi) spider'a.
\item [Downloader middleware] - programy pośredniczące między silnikiem, a
downloader'em. Zajmują się dodatkowym przetwarzaniem danych wejściowych
(żądań) i wyjściowych (odpowiedzi) downloader'a.
\end{description}
\newpage
\subsection{Przepływ danych}
Przepływ danych kontrolowany jest przez
silnik i prezentuje się następująco:
\begin{figure}[tbh]
\center
\includegraphics[width=0.85\hsize]{scrapy_data_flow.png}
% \caption{100 crawlerów pracujących jednocześnie}
\label{scrapy_data_flow_pic}
\end{figure}
\begin{enumerate}
\item Silnik otrzymuje od spider'a żądanie pobrania początkowej strony danej
parafii (najczęściej jest to strona główna parafii).
\item Silnik oddaje żądania dyspozytorowi, który kolejkuje je do dalszego
przetwarzania oraz pyta dyspozytora o żądania gotowe do przekazania downloader'owi.
\item Dyspozytor zwraca silnikowi następne żądania.
\item Silnik wysyła żądania do downloader'a. Zanim żądania dotrą do
downloader'a przetwarzane są przez downloader middleware.
\item Downloader ściąga stronę parafialną i umieszcza ją w odpowiedzi, którą
przesyła silnikowi. Zanim odpowiedź dotrze do silnka przetwarzana jest przez
downloader middleware.
\item Silnik otrzymuje odpowiedź od downloader'a i przekazuje ją spider'owi do
dalszego przetwarzania. Zanim odpowiedź trafi do spider'a przetwarzana jest
przez spider middleware.
\item Spider przerabia odpowiedź i zwraca dane strony parafialnej silnikowi. Zanim dane
trafią do silnika przechodzą przez spider middleware. Dodatkowo spider
wysła żądania z nowymi stronami parafialnymi do pobrania.
\item Silnik wysyła zebrane dane do przetwarzacza danych, który zapisuje je w
pliku jsonline REF. Następnie przekazuje nowe żądania do zakolejkowania
dyspozytorowi.
\end{enumerate}
% \vspace*{-20mm}
Cały proces trwa dopóty, dopóki są nowe żądania do przetworzenia.
\subsection{Sprawdzanie typu odpowiedzi}
Podczas crawlowania ważne jest rozpoznawanie typu ściąganych danych. W przypadku
crawlowania stron parafialnych interesują nas wyłącznie dane tekstowe. Należy
zatem zadbać o to aby nie pobierać danych binarnych takich jak np. video, audio
i obrazy.
Scrapy obsługuje rozpoznawanie typu zawartości odpowiedzi bazując na
następujących kryteriach:
\begin{itemize}
\item wartościach \mintinline{bash}{Content-type}, \mintinline{bash}{Content-Encoding} i \mintinline{bash}{Content-Disposition} w nagłówku odpowiedzi,
\item nazwie pliku lub adresie url (jeśli nie udało się rozpoznać po nagłówku),
\item analizując pierwsze 5000 bajtów zawartości odpowiedzi w poszukiwaniu
znaków znajdującyh się wyłącznie w plikach binarnych (jeśli nie udało się
rozpoznać po nazwie pliku lub adresie url).
\end{itemize}
Powyższy schemat postępowania jest skuteczny jeśli serwisy internetowe zwracają
poprawne odpowiedzi. Niestety niektóre strony parafialne potrafią zwracać
odpowiedzi, które są niezgodne z
https://tools.ietf.org/html/rfc7231 section-3.1.1.5.
UZU Dla przykładu potrafi zwórcić \mintinline{bash}{Content-Type: text/html}, a w \mintinline{bash}{body} binarną
zawartość np. film.
Ze względu na tego rodzaju anomalie zastosowano następujący algorytm REF.
\enlargethispage{4\baselineskip}
\begin{algorithm}
\setstretch{1.2}
\SetAlgorithmName{Algorytm}{algorithm}{List of Algorithms}
\caption{Rozpoznawanie plików binarnych}
\SetAlgoLined
\SetKwData{File}{bytes}
\SetKwData{True}{True}
\SetKwData{False}{False}
\SetKwInput{kwInput}{Wejście}
\SetKwInput{kwOutput}{Wyjście}
\kwInput{\File $\leftarrow$ 1024 pierwsze bajty pliku}
\kwOutput{\True jeśli plik binarny, \False jeśli plik tekstowy}
\SetKwData{ControlCount}{control\_char\_count}
\SetKwData{ControlRatio}{control\_char\_ratio}
\SetKwData{AsciiCount}{extended\_ascii\_count}
\SetKwData{AsciiRatio}{extended\_ascii\_ratio}
\If{\File puste}{
\Return{\False}\;
}
\If{\File dekodowalne jako unikod}{
\Return{\False}\;
}
\ControlCount $\leftarrow$ liczba znaków kontrolnych w \File\;
\AsciiCount $\leftarrow$ liczba znaków ascii o kodach od 128 do 255 w \File\;
\ControlRatio $\leftarrow \frac{\ControlCount}{1024}$\;
\AsciiRatio $\leftarrow \frac{\AsciiCount}{1024}$\;
\SetKwIF{IfM}{ElseIfM}{ElseM}{if~(\endgraf}{\endgraf)~then}{else if}{else}{end if}%
\IfM{\begin{tabular}{@{\hspace*{1.5em}}l@{}}
$($\ControlRatio $> 0.3$ {\bf and} \AsciiRatio $< 0.05)$ {\bf or}\\
$($\ControlRatio $> 0.8$ {\bf and} \AsciiRatio $> 0.80)$
\end{tabular}}{
\Return{\True}\;
}
\SetKwData{Null}{null}
\If{znak \Null w \File}{
\Return{\True}\;
}
\Return{\False}\;
\end{algorithm}
\subsection{Automatyczna regulacja częstości zapytań}
Biblioteka scrapy zawiera przydatne rozszerzenie, które potrafi automatycznie
regulować częstość zapytań w zależności od obciążenia crawler'a i strony internetowej.
\noindent Algorytym regulacji częstości zapytań prezentuje się następująco:
\begin{enumerate}
\item Przyjmijmy, że:
\subitem {\makebox[4.55cm][l]{\mintinline{python}{download_delay}\footnote{Czasy oczekiwania i
opóźnienia liczone są w sekudnach.}}} to opóźnienie wysłania
zapytania.
\subitem \mintinline{python}{target_download_delay} to docelowe
opóźnienie zapytania.
\subitem {\makebox[4.55cm][l]{\mintinline{python}{init_download_delay}}} to początkowe opóźnienie wysłania
zapytania.
\subitem {\makebox[4.55cm][l]{\mintinline{python}{min_download_delay}}} to minimalne opóźnienie wysyłania.
\subitem {\makebox[4.55cm][l]{\mintinline{python}{max_download_delay}}} to maksymalne opóźnienie wysyłania.
\subitem {\makebox[4.55cm][l]{\mintinline{python}{latency}}} to czas od
ustanowienia połączenia \\
{\makebox[5.22cm][l]{}} do otrzymania nagłówków odpowiedzi.
\subitem {\makebox[4.55cm][l]{\mintinline{python}{target_concurrency}}} to zaplanowana liczba zapytań na sekundę.
\item Zacznij z \mintinline{python}{download_delay} równym \mintinline{python}{init_download_delay}.
\item Kiedy odpowiedź jest odebrana, \\ustaw
\mintinline{python}{target_download_delay = latency / target_concurrency}
\item Następne \mintinline{python}{download_delay} ustaw jako średnią z
aktualnego \mintinline{python}{download_delay} i \mintinline{python}{target_download_delay}.
\item \mintinline{python}{download_delay} nie może być mniejszy niż i większy niż .
\item Czasy oczekiwania na odpowiedzi z kodem http różnym od 2xx nie są brane
pod uwagę.
\end{enumerate}
\noindent W crawlerze stron parafialnych stałe ustawiono następująco:
\begin{itemize}
\item \mintinline{python}{min_download_delay = 0}
\item \mintinline{python}{init_download_delay = 5}
\item \mintinline{python}{target_concurrency = 1}
\end{itemize}
\subsection{Crawlowanie wieloprocesorowe}
Pająk został zaprojektowany w ten sposób, aby bardzo łatwo można było
urównoleglić ściąganie stron parafialnych.
Z pomocą GNU parallel crawlowane jest jednocześnie 100 parafii.
\begin{figure}[tbh]
\center
\includegraphics[width=0.7\hsize]{crawler.png}
\caption{100 crawlerów pracujących jednocześnie.}
\label{crawler_pic}
\end{figure}
\subsection{Konwersja html na tekst.}
\section{System crowdsourcingowy}
\section{Ekstraktor regułowy}
\section{Ekstraktor oparty na uczeniu maszynowym}
\chapter{Rezultaty}
% \section{Organizacja danych} % może zbyt inżynierskieby
\chapter{Perspektywy na przyszłość}
\chapter{Podsumowanie}
% \subsection{Ewaluacja wewnętrzna} %F1 score
% \subsection{Ewaluacja zewnętrzna} % w systemie webowym, użytkownicy
% \chapter{Wnioski}
%%% Local Variables:
%%% LaTeX-command: "latex -shell-escape"
%%% End: