40dd43ee2a
Nowe podrozdziały to: - Sprawdzanie typu odpowiedzi - Automatyczna regulacja częstości zapytań Napoczęto crawlowanie wieloprocesowe. Poprawiono diagramy crawler.xml i scrapy_data_flow.xml Nowe pakiety minted i algoruled.
349 lines
16 KiB
TeX
349 lines
16 KiB
TeX
\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: |