Page 119 of 173

Sprawdzanie poprawności URLi z wykorzystaniem Pythona i urllib2

Dzisiaj dla odmiany od Rubiego, krótki poradnik dot. sprawdzania dostępności strony WWW z wykorzystaniem Pythona i biblioteki urllib2.

Wstęp

Zdarza się, że potrzebujemy wiedzieć czy url jaki wprowadza użytkownik jest poprawny i czy taka strona w ogóle istnieje. Może się to przydać chociażby do walidacji pola url w modelu usera. Dzięki temu użytkownik nie będzie mógł wprowadzić przez pomyłkę adresu który nie istnieje.

Metoda

Metoda jest całkiem prosta - wystarczy odpytać żądaniem HEAD nasz url, sprawdzić co zostanie zwrócone i ewentualnie przechwycić wyjątki. Warto zauważyć, że w poniższym kodzie dokonałem pewnego uproszczenia. Mianowicie, przechwytuję część błędów nadając im ręcznie wartość 404. Tak naprawdę te błędy należałoby obsłużyć w inny sposób, ponieważ niekoniecznie oznaczają to, że podana strona nie istnieje. Poniżej opis tego co który wyjątek oznacza:

  • urllib2.HTTPError - błędy z serwera (404, 500, 403, itd)
  • urllib2.URLError - nieprawidłowy URL (nie istnieje taki adres lub błąd w składni)
  • Exception - całe inne zło tego świata :)

Najpierw zaimportujmy sobie co nam potrzeba i ustalmy jaki URL sprawdzamy:

import urllib2
from urllib2 import URLError

uri = "http://onet.pl"

No i sprawdzamy:

def uri_status( uri):
  if len(uri) == 0: return '404'
  try:
      request = urllib2.Request(uri,  None, {'User-agent': 'Mozilla/5.0'})
      request.get_method = lambda : 'HEAD'
      response = urllib2.urlopen(request)
      return '200'
  except urllib2.HTTPError, e:
      return e.code
  except urllib2.URLError, e:
      return '404'
  except Exception, e:
      return '404'

print uri_status(uri)

Kod metody i jej wywołanie:

def uri_status( uri):
  if len(uri) == 0: return False
  try:
      request = urllib2.Request(uri,  None, {'User-agent': 'Mozilla/5.0'})
      request.get_method = lambda : 'HEAD'
      response = urllib2.urlopen(request)
      return '200'
  except urllib2.HTTPError, e:
      return e.code
  except urllib2.URLError, e:
      return '404'
  except Exception, e:
      return '404'

print uri_status(uri)

Podajemy user_agenta (przeglądarkę), ponieważ niektóre serwisy nie chcą (lub robią to błędnie) odpowiadają na żądania.

UPDATE

Warto ustawić także timeout dla połączeń - ponieważ połączenia bez ustalonego timeoutu mogą trwać w nieskończoność. Nie można tego jednak zrobić z poziomu urllib2 a jedynie dla socketów:

import socket
import urllib2

# timeout w sekundach
timeout = 10
socket.setdefaulttimeout(timeout)

Ruby, DRMAA, DRMAA4Ruby, Sun Grid + Monte Carlo integration

Wstęp

Przyjęło się, że wykonywanie obliczeń z pomocą gridów jest domeną języków C, C++ i Javy. To samo tyczy się zarządzania gridem. Okazuje się jednak, że z powodzenie można do tego wykorzystać także Rubiego. Jako przykład wykorzystamy całkowanie metodą Monte Carlo, zaś zarządzać gridem będziemy za pośrednictwem DRMAA'y oraz biblioteki DRMAA4Ruby.

DRMAA4Ruby

DRMAA4Ruby jest to interfejs do języka Ruby - który jest bindingiem do DRMAA'y dla C. Dzięki niemu możemy w bardzo prosty sposób rozdzielać zadania na mniejsze fragmenty i kolejkować je na gridzie.

DRMAA4Ruby ma jednak także swoje wady - nowa wersja tej biblioteki nie jest kompatybilna z wciąż popularną wersją Rubiego - 1.8.7. Występują także problemy z odnalezieniem i użyciem modułu libdrmaa.so. Na szczęście - oba te problemy nie są trudne do rozwiązania.

DRMAA4Ruby znajdziemy tutaj. W katalogu lib/ znajdują się pliki:

  • drmaa.rb
  • original-drmaa.rb

Jeśli korzystasz z Rubiego w wersji 1.8.7 (lub starszej), zamiast ładować drmaa.rb musisz ładować original-drmaa.rb.

Drugim z problemów jest błąd podczas ładowania pliku libdrmaa.so. Ruby nie może go namierzyć. Aby to zmienić, wystarczy podać dokładną ścieżkę do tegoż pliku (można to zrobić używając polecenia find w powłoce). Podmiany ścieżki na bezwzględną dokonujemy w linijce 191 (dla original-drmaa.rb):


@libdrmaa = DL.dlopen('/sciezka/bezwzgledna/libdrmaa.so')

Po tym zabiegu biblioteka powinna ładować się (i działać bez problemu).

Monte Carlo

Nie będę tutaj opisywał jak dokładnie działa całkowanie metodą Monte Carlo - zainteresowanych odsyłam do wikipedii. Poniżej przedstawię jednak kod realizujący taką metodę całkowania (oczywiście w Rubym):


xp = ARGV[0].to_f
xk = ARGV[1].to_f
parts = ARGV[2].to_i
part = ENV['SGE_TASK_ID'].to_i
STEPS=10_000

step = (xk-xp)/parts

p = xp+step*(part-1)
k = p+step

xp = p; xk = k

f = lambda { |x| x*x+3 }

dx=(xk-xp).to_f
s = 0

STEPS.times { s += f.call(xp+rand*dx) }
s = dx*s/STEPS

print s

Kod nie jest skomplikowany, a dla nas ważne jest to jak go odpalamy. Przyjmuje on 3 parametry jawne:

  • początek przedziału całkowania
  • koniec przedziału całkowania
  • ilość części na jakie będziemy dzielić nasze całe całkowanie

Co znaczy "ilość części"? Musimy pamiętać, że obliczenia wykonywane będą na gridzie, z tego względu musimy nasze zadanie rozbić na subzadania. Każde subzadanie będzie liczyć "kawałek" całki, po czym całość zsumujemy. Nasz kod ma jeszcze jeden parametr - brany ze zmiennej środowiskowej SGE_TASK_ID. Tym parametrem jest informacja który kawałek ma liczyć dana instancja programu. Po ustawieniu zmiennych nie pozostaje nam nic innego jak policzyć i wyświetlić wynik. To by było na tyle jeśli chodzi o samo zadanie wywoływane na gridzie. Nie ma tu wielkiej filozofii - wystarczy pamiętać o tym, że duże zadanie rozbijamy na mniejsze subtaski które są względem siebie niezależne (mogą funkcjonować autonomicznie).

DRMAA + Ruby + JobTemplate

Pora stworzyć kod który doda do grida nasze zadanie, a następnie zbierze wyniki i wyświetli. Na początek wymagane biblioteki i dwie stałe:


$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
$LOAD_PATH.unshift(File.dirname(__FILE__))

require 'original-drmaa.rb'

STD_PATH = "/home/path/"
SOURCE_PATH = "/home/path/"

  • STD_PATH - gdzie ma domyślnie zapisywać wyniki obliczeń z poszczególnych instancji zadania
  • SOURCE_PATH - gdzie znajduje się nasze zadanie które chcemy uruchomić

Dalej zdefiniujemy sobie JobTemplate - który odwzorowuje nasze zadanie w gridzie:

class Carlos < DRMAA::JobTemplate
  def initialize
	  super
	  self.command = "/usr/bin/ruby"
	  self.arg = ["#{SOURCE_PATH}/monte_carlo.rb", 0, 100, 10]
	  self.stdout = ":#{STD_PATH}/"
	  self.join = true
	  end
end

Warto zauważyć, że jako polecenie nie podajemy pliku z kodem ale ścieżkę do Rubiego. Plik z kodem stanowi pierwszy parametr (da się to obejść całkiem prosto - jak? Pozostawiam to wam).
Następnie podajemy 3 parametry:

  1. o - początek przedziału całki
  2. 100 - koniec przedziału
  3. 10 - ilość subzadań na gridzie

Uruchamiamy nasze zadanie (opis metod znajduje się na stronie DRMAA'y - można skorzystać z opisów do Javy, ponieważ interfejs jest praktycznie ten sam):

session = DRMAA::Session.new

jobs = []
session.run_bulk(Carlos.new, 1, 10).each { |job| jobs << job }

Zanim przejdziemy do zbierania i sumowania wyników, dla przypomnienia wspomnę, że wyniki z subzadań zapisywane są przez DRMAA'ę - tak więc nie musimy się martwić o ich zapis, tak długo, jak długo drukujemy coś na ekranie. DRMAA numeruje pliki w mniej więcej taki sposób:

  1. Nazwa polecenia (u nas ruby)
  2. .o (tak - kropka i literka "o")
  3. ID zadania
  4. . (kropka)
  5. task ID

Np: ruby.o15.61

Wiedząc o tym i majac w tablicy jobs informacje o kolejnych taskach - możemy poiterować pliki i odczytać z nich wyniki. Następnie wystarczy suma i zwrócenie wyniku całościowego:

result = 0
session.wait_each{ |info|
	if ! info.wifexited?
		puts "failed: " + info.job
	else
		file_name = "#{STD_PATH}/ruby.o#{info.job}"
		begin
			File.open(file_name) do |inl|
				while (line = inl.gets)
					result += line.to_f
				end
			end
			File.delete(file_name)
		rescue
		end
	end
}
print result

I to by było na tyle - z pomocą tego kodu możemy policzyć na gridzie całkę metodą Monte Carlo. Całość kodu managera:

$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
$LOAD_PATH.unshift(File.dirname(__FILE__))

require 'original-drmaa.rb'

STD_PATH = "/home/path"
SOURCE_PATH = "/home/path"

class Carlos < DRMAA::JobTemplate
  def initialize
	  super
	  self.command = "/usr/bin/ruby"
	  self.arg = ["#{SOURCE_PATH}/monte_carlo.rb", 0, 100, 10]
	  self.stdout = ":#{STD_PATH}/"
	  self.join = true
	  end
end


session = DRMAA::Session.new

jobs = []
session.run_bulk(Carlos.new, 1, 10).each { |job| jobs << job }

result = 0
session.wait_each{ |info|
	if ! info.wifexited?
		puts "failed: " + info.job
	else
		file_name = "#{STD_PATH}/ruby.o#{info.job}"
		begin
			File.open(file_name) do |inl|
				while (line = inl.gets)
					result += line.to_f
				end
			end
			File.delete(file_name)
		rescue
		end
	end
}
print result

Copyright © 2025 Closer to Code

Theme by Anders NorenUp ↑