Přesměrování (redirect) s modulem urllib2

Standardně je Pythonovský modul urllib2 naprogramován tak, že automaticky zajistí, aby se HTTP přesměrování provedlo, aniž by o tom uživatel (nebo spíše programátor) věděl. Někdy je ovšem dobré vědět, že bylo přesměrování provedeno a jaký je jeho typ. Například v případě, že potřebujete uložit novou URL stránky, protože ta se díky přesměrování změní. Při zjišťování přesměrování mohou ale nastat nečekané problémy.

Typy HTTP přesměrování

Nejdříve si něco řekněme o přesměrování samotném. Určitě se Vám někdy stalo, že jste zadali adresu do prohlížeče a po načtení webové stránky se ta adresa změnila. To je přesměrování. Jednoduché že?

Ne tak docela. Přestože to většinou nikoho nezajímá, existuje více typů přesměrování. Odlišeny jsou od sebe svým kódem (číslem), které Váš internetový prohlížeč dostane v HTTP hlavičce. U přesměrování se jedná o čísla 300 až 307, já se ovšem budu dále zabývat jen dvěmi z nich a to 301 - Moved Permanently (Trvale přesunuto) a 302 - Moved Temporarily (Dočasně přesunuto, někdy bývá uvedeno Found - Nalezeno). Rozdíl mezi trvalým a dočasným přesměrováním snad netřeba vysvětlovat :)

Rozlišení přesměrování v Pythonu

Teď už se snad dostanu k jádru pudla. V Pythonu při zjišťování stavového kódu přesměrování vznikne výjimka, která program shodí. Vyzkoušejte následující příkazy a uvidíte.

>>> import urllib2, httplib
>>> httplib.HTTPConnection.debuglevel = 1
>>> request = urllib2.Request(
...         'http://www.mpower-project.eu') # URL s přeměrováním
>>> opener = urllib2.build_opener()
>>> f = opener.open(request)

Zatím nic. Ovšem jen do doby, kdy si nechám vypsat URL stránky v paměti.
>>> f.url
'http://www.sintef.no/Projectweb/MPOWER/'

Je jiná než jsem zadal. Ovšem bylo přesměrovnání dočasné nebo trvalé? Nevím.
>>> f.status
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
AttributeError: addinfourl instance has no attribute 'status'

Chytré hlavy ale vědí, jak to vyřešit. Stačí se podívat, jak funguje urllib2 a napsat si svůj redirect handler, pro tyto dva stavové kódy. Následující kód se může vyťukat do terminálu nebo uložit do samostatného souboru.
class SmartRedirectHandler(urllib2.HTTPRedirectHandler):
    def http_error_301(self, req, fp, code, msg, headers):
        result = urllib2.HTTPRedirectHandler.http_error_301(
            self, req, fp, code, msg, headers)
        result.status = code
        return result
 
    def http_error_302(self, req, fp, code, msg, headers):
        result = urllib2.HTTPRedirectHandler.http_error_302(
            self, req, fp, code, msg, headers)
        result.status = code
        return result   

Nyní stačí malinko upravit původní kód a mám HTTP status z hlavičky jak na dlani, což potřebuji. Předpokládejme, že následující kód už třídu SmartRedirectHandler zná.
>>> import urllib2, httplib
>>> httplib.HTTPConnection.debuglevel = 1
>>> request = urllib2.Request(
...         'http://www.mpower-project.eu') # URL s přeměrováním
>>> opener = urllib2.build_opener(
...         SmartRedirectHandler())
>>> f = opener.open(request)
>>> f.url
'http://www.sintef.no/Projectweb/MPOWER/'
>>> f.status
301

Bingo. Teď už mé programy umí rozlišovat dočasná a trvalá přesměrování.

Ale nemyslete si, že já jsem ta chytrá hlava, která na to došla. Musel jsem trochu zagooglit, abych našel hezkou stránku, kde bylo řešení uvedeno ;) Vy tu stránku můžete najít v odkazech pod článkem.