\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: