Sprawdzanie odpowiedzi serwerów HTTP w Pythonie.

Oto prosta klasa napisana w Pythonie do sprawdzania odpowiedzi serwerów HTTP:

#-*- coding: utf-8

__author__ = 'Łukasz Fidosz'
__copyright__ = 'Copyright (c) 2008 Łukasz Fidosz'
__date__ = '20.09.2008'
__license__ = 'GPL'
__version__ = '0.1.1'

from httplib import HTTPConnection
import socket
from subprocess import Popen, PIPE, STDOUT

class HTTPChecker:

        def __init__(self, urllist):
                self.hosts=urllist
        
        def ping(self, host):
                rstatus='   '
                adress=host.split(':')[0]
                p=Popen('ping -q -c3 '+adress, shell=True, stderr=STDOUT, stdout=PIPE, close_fds=True)
                output=p.stdout.read()
                if output.find('unknown host') != -1:
                        return rstatus,'Unknown host'
                else:
                        lost_packets=int(output.split(' ')[-9][:-1])
                        if lost_packets > 35:
                                return rstatus,'Connection problems'
                        else:
                                return rstatus,'No HTTP server'

        def check_host(self, host, method='HEAD'):
                header={'User-agent' : 'Mozilla/5.0'}   
                connection=HTTPConnection(host)
                try:
                        connection.request(method, '/', {}, header)
                except socket.error:
                        return self.ping(host)
                else:
                        response=connection.getresponse()
                        if response.status==501:
                                self=check_host(host, 'GET')
                        return response.status,response.reason

        def run(self):
                for host in self.hosts:
                        result=self.check_host(host)
                        message=host.ljust(30, '.')+': '+str(result[0])
                        if result[0]==200:
                                message+=' \033[1;32m'
                        else:
                                message+=' \033[1;31m'
                        message+=result[1]+'\033[0;m'
                        print message

Postaram się omówić w miarę dokładnie cały kod. Zacznijmy od metody check_host, gdyż to ona wykonuje całe zadanie, czyli pobiera od podanego w argumencie host serwera HTTP nagłówki HTTP i zwraca je.

  • header={'User-agent' : 'Mozilla/5.0'} ustala nagłówki jakie będą wysyłane przy połączeniu, w naszym przypadku jest to nazwa przeglądarki jaką udawał będzie nasz skrypt, ponieważ niektóre serwisy wysyłają nagłówek 403 Forbidden gdy przy połączeniu nie otrzymają nazwy przeglądarki(robi tak na przykład serwis Distrowatch)
  • connection=HTTPConnection(host) nawiązuje połączenie z danym serwerem, i przypisuje uchwyt do niego do zmiennej connection
  • connection.request('HEAD', '/', {}, header) próbuje wysłać do serwera żądanie nagłówka HTTP, jak widzimy przekazujemy przy tym żądaniu wcześniej spreparowany nagłówek, jeśli operacja ta się nie powiedzie wywołujemy metodę ping, która sprawdza czy w ogóle taki host istnieje oraz czy jesteśmy połączeni z internetem i zwracamy wynik jej działania. Metodę ping omówimy za chwile.
  • response=connection.getresponse() przypisuje zmiennej response obiekt zawierający odpowiedź serwera
  • return response.status,response.reason zwraca jako wynik działania metody krotkę zawierającą kod odpowiedzi serwera oraz jego opis

Metoda run wywołuje funkcję check_host dla każdej nazwy hosta lub adresu IP podanych do klasy w metodzie __init__, po czym drukuje odpowiednio sformatowany wynik. Omówmy jej kod:

  • result=self.check_host(host) przypisuje to zmiennej result krotkę zwróconą przez metodę check_host
  • message=host.ljust(30, '.')+': '+str(result[0]) przypisuje do zmiennej message wartość zmiennej host dopełnioną, tak dla czytelności z prawej strony kropkami do 30 znaków, po czym dodaje do tak przygotowanego stringa kod odpowiedzi HTTP
  • Następnie sprawdzamy czy opis odpowiedzi HTTP to OK, jeśli tak jest zmieniamy jego kolor na zielony instrukcją message+=' \033[1;32m', jeśli odpowiedź jest inna dajemy jej kolor czerwony poprzez dopisanie do zmiennej message wartości ' \033[1;31m'
  • message+=result[1]+'\033[0;m' przypisuje do zmiennej message wspomniany już opis odpowiedzi HTTP, po czym przywraca domyślny kolor napisów

Została nam jeszcze metoda ping, jej zadanie zostało już w skrócie opisane, przejdźmy więc do jej omawiania.

  • rstatus=' ' ustala co będzie zwracane przez metodę ping jako kod odpowiedzi, jako że nie mamy tu do czynienia z kodami HTTP a chodzi nam jedynie o zwrócenie opisu, dajemy tutaj ze względów estetycznych 3 spacje(bo tyle cyfr mają kody odpowiedzi HTTP)
  • p=Popen('ping -q -c3 '+host, shell=True, stderr=STDOUT, stdout=PIPE, close_fds=True) wykonuje polecenie systemowe ping po czym przypisuje uchwyt do niego do zmiennej p. Popen z takimi parametrami jest metodą zastępującą nam metodę popem4 z modułu os, której używanie jest już niezalecane, funkcjonalność metody popem4 uzyskujemy sprawiając ze wyjście błędów jest przekazywane na standardowe wyjście wywoływanego polecenia(stderr=STDOUT)
  • output=p.stdout.read() przypisujemy tutaj do zmiennej output wszystko co zwróciło polecenie systemowe ping, robimy to wywołując metodę read() na p.stdout, który jest obiektem pliko podobnym
  • if output.find('unknown host') != -1: sprawdza czy ping nie zwrócił przypadkiem wyrażenia unknown host, jeśli tak kończymy wywołanie funkcji zwracając jako opis błędu string Unknown host. W przeciwnym przypadku za pomocą instrukcji lost_packets=int(output.split(' ')[-9][:-1]) dzielimy nasz string w miejscu spacji zawierający wyjście na oddzielne elementy, po czym z tak otrzymanej listy wypieramy dziewiąty od końca element, on zawiera informacje o procencie utraconych pakietów, wyrażenie [:-1] usuwa znak %, który nie jest nam potrzebny. Sprawdzamy teraz czy ilość utraconych pakietów nie jest przypadkiem większa niż 35%(wysłaliśmy 3 pakiety i pozwalamy się ewentualnie jednemu zagubić) zwracamy jako opis błędu komunikat Connection problems, jeśli jednak host daje się fingować oznacza to ze serwer HTTP nie jest na nim uruchomiony, o czym również informujemy.

Źródła skryptu

Przykład użycia

from httpchecker import *
check=HTTPChecker(('www.google.pl', 'www.archlinux.org', 'distrowatch.com'))
check.run()

Co daje nam na przykład taki wynik:

www.google.pl.................: 200 OK
www.archlinux.org.............: 200 OK
distrowatch.com...............: 200 OK

Warto przeczytać:

MegiTeam - mówimy Twoim językiem

Komentarze do notki Sprawdzanie odpowiedzi serwerów HTTP w Pythonie.

  1. Taeril powiedział(a):

    Jako host można podać host:port jak np '127.0.0.1:8080' i ładnie pokazało mi OK :)
    Jednak jak wyłączyłem serwer to dostałem komunikat "Unknown host", bo nie wyodrębniło IP.

    Przykładowo mój router ma bardzo prosty serwer HTTP, który rozumie tylko GET i POST a na HEAD odpowiada kodem 501 czyli Not Implemented. Można więc dla takich przypadków przy kodzie 501 powtórzyć zapytanie ale z GET zamiast HEAD.

    Dalej mój router zwrócił kod 200 (jak mu wysłałem GET) ale komunikat był 'Ok' a nie 'OK' co chyba jest w porządku z rfc. To te numerki są dla nieludzi więc porównywanie do 'OK' należałoby zastąpić sprawdzaniem kodu.

    Skrypt brzydko zakłada, że wyjście to terminal, który obsłuży \033[1;32m itp - wie ktoś jak powinno się obchodzić z kolorami jak najbardziej uniwersalnie?


    btw. w czasie zabawy skryptem serwer www.archlinux.org zwrócił mi 500 ale to chyba nie prze mnie ;)

  2. joru powiedział(a):

    \033[1m to nie jest przywracanie poprzedniego koloru, to ustawianie rozjasnienia... \033[0m to reset koloru... \033[1;32m analogicznie to *jasno*zielony... a, i to dziala tylko na terminalach zgodnych ansi, czyli np. XPkowy cmd.exe tego nie obsluzy...

  3. virhilo powiedział(a):

    @Taeril-dzięki za dokładniejsze przetestowanie skryptu i uwagi:) Wszystko już poprawiłem, co do nazwy chosta poprostu zawsze odrzucamy w metodzie ping to co jest po dwukropku bez względu na to czy cos tam jest czy nie, a jeśli chodzi o 501 to dodałem argument jakim jest typ żądania, domyślnie 'HEAD', a gdy trafimy na 501 metoda wykonuje jeszcze raz samą siebie z typem żądania ustawionym na 'GET'. Co do kolorow, poszukam-może znajde jakieś ciekawe rozwiązanie:)

  4. virhilo powiedział(a):

    @joru-dzieki:) pomyliło mi się;) już poprawione:)

  5. joru powiedział(a):

    message+=' \033[1;30m'
    message+=result[1]+'\033[1;m'
    poprawione, ale zle :p
    \033[1;30m to jasnoczarny, a powinno byc \033[1;31m - jasnoczerwony...
    a nizej \033[0m do resetu...

  6. virhilo powiedział(a):

    Hehe-ale mi sie kreca te kolorki;) Teraz ok?:)

  7. virhilo powiedział(a):

    znanazlem cos ciekawego o kolorowaniu:
    http://code.activestate.com/recipes/475116/ :)

W górę » Strona główna

Dodaj komentarz: