Proste statystyki w Drupalu

Tags:

W Drupalu dostępny jest domyślnie prosty moduł statystyk - "Statistics". Często potrzebujemy tylko podstawowych funkcji statystyk na naszej stronie, wtedy instalowanie rozbudowanych modułów niepotrzebnie spowalnia działanie witryny. Niestety, moduł "Statistics" ma kilka wad, najpoważniejszą z nich jest nierozróżnianie botów wyszukiwarek, przez co licznik czytań poszczególnych segmentów staje się praktycznie bezużyteczny.

Konfigurując serwis IT Lublin, wprowadziłem kilka modyfikacji w tym module.
Wersja Drupala: 5.3

Jak rozpoznać boty wyszukiwarek

Najważniejsza zmiana dotyczy rozpoznawania przez skrypt wejść botów wyszukiwarek (ang. crawler). Oparłem się tutaj na 2 bardzo prostych mechanizmach. Po pierwsze, każda szanująca się wyszukiwarka się "przedstawia", tzn. przy wejściu takiego bota na naszą witrynę, w kluczu $_SERVER['HTTP_USER_AGENT'] znajdziemy nazwę tej wyszukiwarki lub użytego bota. Klucz ten zazwyczaj zawiera także kilka innych informacji, np. w przypadku google wygląda mniej więcej tak:

Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)

Rozpoznawanie po HTTP_USER_AGENT oparłem na najprostszym możliwym mechanizmie - jeżeli w kluczu $_SERVER['HTTP_USER_AGENT'] znajduje się w dowolnym miejscu ciąg identyfikujący wyszukiwarkę (np. "Googlebot", "OnetSzukaj"), wejście nie jest zliczane. Ma to taką przewagę nad wyrażeniami regularnymi, że nieco mniej obciąża serwer w czasie wykonywania skryptu. Jeżeli chcemy by nasz moduł rozpoznawał kilkanaście różnych botów (w tej liczbie zmieści sie prawdopodobnie 99% wejść wyszukiwarek), musimy uwzględnić fakt, że przy każdym wejściu na dowolny segment (artykuł, news...), jest wykonywanych kilkanaście sprawdzeń. Przy dużym ruchu na naszej witrynie, efektywność funkcji porównującej ma już znaczenie.

Drugim sposobem jest rozpoznawanie po domenie, np. wejścia z domen *.googlebot.com i livebot*.search.live.com są to boty wyszukiwarek.

Zmiany jakie wprowadziłem w module Statistics, nie wymagały odinstalowania go czy nawet wyłączania. Jednak gdy modyfikacje dotyczą bazy danych, może to być konieczne.

Jak działa moduł Statistics?

Zanim przystąpimy do wprowadzania zmian, przyjrzymy się jak działa moduł Statistics. Interesuje nas, w którym miejscu i w jaki sposób następuje zliczanie liczby wejść.

W katalogu modules/statistics znajdują się 3 pliki:

  • statistics.info - zawierający ogólne informacje o module
  • statistics.install - funkcje potrzebne przy instalowaniu i odinstalowywaniu modułu
  • statistics.module - w tym pliku jest właściwy kod modułu

Zliczanie czytań segmentu jest zaimplementowane w funkcji statistics_exit(), kończącej pracę modułu. Poniżej początek fragmentu kodu odpowiedzialnego za zliczanie czytań:

  1. // We are counting content views.
  2.     if ((arg(0) == 'node') && is_numeric(arg(1)) && arg(2) == '') {

Powyższy warunek określa kiedy i dla jakich stron zliczać wejścia - w tym przypadku dla segmentów typu node. W tym miejscu można dodać sprawdzenie czy mamy do czynienia z botem wyszukiwarki. W mojej modyfikacji stworzyłem funkcję statistics_check_if_toCount(), zwracającą false jeżeli rozpozna wyszukiwarkę. Wstawiamy to do powyższego fragmentu kodu:
  1. // We are counting content views.
  2.     if ((arg(0) == 'node') && is_numeric(arg(1)) && arg(2) == '' && statistics_check_if_toCount()) {

Funkcja sprawdzająca

Przed statistics_exit() deklarujemy naszą funkcję:

  1. function statistics_check_if_toCount ()
  2.         {

W funkcji tej użyjemy 2 zmiennych:

  • $userAgentExclude - tablica z ciągami znaków identyfikującymi wyszukiwarki
  • $doCountEntry - wartość zwracana, domyślnie true, jeżeli zostanie znalezione dopasowanie, to zmieniamy wartość na false

Nasza funkcja w najprostszej postaci będzie więc wyglądać tak:

  1. function statistics_check_if_toCount ()
  2.   {
  3.   $userAgentExclude = array ();
  4.   $doCountEntry = true;
  5.   foreach ($userAgentExclude as $search)
  6.     {
  7.     if ($search != '' && stripos($_SERVER['HTTP_USER_AGENT'],$search) !== false)
  8.       $doCountEntry = false;
  9.     }
  10.   return $doCountEntry;
  11.   }

Sprawdzanie jest tutaj bez rozróżniania wielkości liter. Jeśli chcesz by wielkość liter była rozróżniana, użyj strpos zamiast stripos.

Panel administracyjny

Aby nie edytować pliku modułu za każdym razem, gdy chcemy dodać czy zmienić listę rozpoznawanych botów, dodamy możliwość edycji tej listy w panelu administracyjnym modułu. Jest to bardzo proste - Drupal udostępnia specjalną przestrzeń zmiennych do tego celu. Nazwiemy naszą nową zmienną statistics_useragents_excluded.

W funkcji statistics_access_logging_settings() określającej parametry konfigurowalne w panelu administracyjnym, odnajdujemy wpisy zaczynające się od

  1. // count content views settings
  2.   $form['content']

Są to opcje dostępne dla licznika czytań. Na końcu dodajemy jeszcze jedną opcję:
  1. $form['content']['statistics_useragents_excluded'] = array(
  2.     '#type' => 'textarea',
  3.     '#title' => t('Which useragents do not count'),
  4.     '#default_value' => variable_get('statistics_useragents_excluded', ''),
  5.     '#rows' => 15,
  6.     '#description' => t('<p>Input here in separate lines useragents which shouldn\'t be counted. If the text is found in any place of \'USER_AGENT\', then it will not be counted. This is case-insensitive.</p><p>You can use this blacklist for not counting crawlers.</p>')
  7.     );

Pierwszy indeks zmiennej $form określa w jakim panelu znajdzie się dana opcje (content - ogólna konfiguracja modułu, access - uprawnienia dostępu), drugi to nazwa naszej zmiennej. Poszczególne elementy oznaczają:

  • #type - typ danych; textarea to wielowierszowe pole tekstowe
  • #title - nagłówek wyświetlany nad tą opcją (dla tekstu zawartego w funkcji t() można dodać tłumaczenie w panelu administracyjnym modułu "locale")
  • #default_value - wartość domyślna; funkcją variable_get() wczytujemy dotychczasową wartość
  • #rows - rozmiar pola tekstowego
  • #description - opis przy tej opcji

Jak wejdziemy teraz w Ustawienia dziennika odwiedzin, pojawi nam się nowe pole tekstowe, w które w osobnych liniach możemy wpisać ciągi znaków, po których będą rozpoznawane wyszukiwarki.

Jak korzystać ze zmiennej statistics_useragents_excluded?

W naszej funkcji sprawdzającej trzeba wykonać kilka kroków by wykorzystać dane zapisane w zmiennej:

  • pobranie jej wartości za pomocą variable_get()
  • rozbicie na tablicę, według nowych linii

Przykładowo wygląda to tak:

  1. function statistics_check_if_toCount ()
  2.    {
  3.    $userAgentExclude = explode ("\n",
  4.       str_replace(array("\r\n","\r"),
  5.          array("\n","\n"),
  6.          variable_get('statistics_useragents_excluded',''))
  7.       );

Jak widać, zastosowałem tablicę zamian różnych zakończeń linii na "\n", by zawsze prawidłowo zadziałało rozbijanie na tablicę. Nie jest to najlepsze miejsce na takie sprawdzanie poprawności, powinno ono być wykonywane jednorazowo po zapisaniu zmian w panelu administracyjnym - jednak na razie zadowolę się takim tymczasowym rozwiązaniem. Tą oraz kilka innych optymalizacji opiszę w jednym z kolejnych artykułów.

Podsumowanie

Nasza funkcja wygląda tak:

  1. function statistics_check_if_toCount ()
  2.    {
  3.    $userAgentExclude = explode ("\n",
  4.       str_replace(array("\r","\r\n","\n\r"),
  5.          array("\n","\n","\n"),
  6.          variable_get('statistics_useragents_excluded',''))
  7.       );
  8.    $doCountEntry = true;
  9.    foreach ($userAgentExclude as $search)
  10.       {
  11.       if ($search != '' && stripos($_SERVER['HTTP_USER_AGENT'],$search) !== false)
  12.          $doCountEntry = false;
  13.       }
  14.    return $doCountEntry;
  15.    }

zaś w funkcji statistics_access_logging_settings() dodaliśmy taki wpis na końcu (ale przed return oczywiście):
  1. $form['content']['statistics_useragents_excluded'] = array(
  2.     '#type' => 'textarea',
  3.     '#title' => t('Which useragents do not count'),
  4.     '#default_value' => variable_get('statistics_useragents_excluded', ''),
  5.     '#rows' => 15,
  6.     '#description' => t('<p>Input here in separate lines useragents which shouldn\'t be counted. If the text is found in any place of \'USER_AGENT\', then it will not be counted. This is case-insensitive.</p><p>You can use this blacklist for not counting crawlers.</p>')
  7.     );

Przykładowa lista ciągów identyfikujących boty wyszukiwarek:
  1. googlebot
  2. slurp
  3. OnetSzukaj
  4. msnbot
  5. crawler
  6. Homenet-Search
  7. WinkySpider

Możesz jeszcze przejść do konfiguracji witryny -> języki, i przetłumaczyć opis i tytuł dodanego pola.

  • Which useragents do not count -> Nie zliczaj następujących przeglądarek
  • <p>Input here in separate lines useragents... -> <p>W oddzielnych liniach wpisz przeglądarki, które mają być niezliczane. Gdy tekst znajduje się w dowolnym miejscu 'USER_AGENT', wtedy wejście nie będzie zliczane. Wielkość liter nie jest rozróżniana.</p><p>Umożliwia to nie zliczanie wejść botów wyszukiwarek</p>

Co dalej?

Pokazałem w tym artykule tylko proste rozpoznawanie botów wyszukiwarek za pomocą HTTP_USER_AGENT. Można w tym module wykonać jeszcze kilka ciekawych modyfikacji:

  • rozpoznawanie botów na podstawie domeny (tutaj trzeba użyć wyrażeń regularnych) oraz włączanie/wyłączanie obu mechanizmów rozpoznawania
  • optymalizacja - np. jednorazowe wykonywanie zamiany końca linii, czy wykonywanie porównań tylko do momentu napotkania pierwszego dopasowania (w tej chwili zawsze są wykonywane wszystkie)

Nie są to skomplikowane zmiany, jednak nie mam w tej chwili już czasu by je opisać. Postaram się to zrobić w kolejnym artykule - możesz też spróbować wykonać je samodzielnie.

Kopiowanie niniejszego artykułu jest dozwolone w celach komercyjnych i niekomercyjnych, pod warunkiem że zamieścisz informację iż pochodzi on ze strony www.itlublin.pl

Odpowiedzi

Cóż, hackowanie

avatar użytkownika Tomasz Dąbski

Cóż, hackowanie istniejącego modułu to trochę toporna metoda, dlatego zamiast opisywać dalsze modyfikacje, postaram się napisać nowy moduł, który będzie odpowiadał za sam licznik czytań.

--
Tomasz Dąbski