Existe uma maneira fácil de solicitar um URL em python e NÃO seguir redirecionamentos?

96

Olhando para a fonte de urllib2, parece que a maneira mais fácil de fazer isso seria subclasse HTTPRedirectHandler e, em seguida, usar build_opener para substituir o HTTPRedirectHandler padrão, mas isso parece muito (relativamente complicado) de trabalho para fazer o que parece que deveria ser bem simples.

John
fonte
2
Para googlers: usar a biblioteca de solicitações poupará muitas dores de cabeça: docs.python-requests.org e veja a resposta de Marian abaixo, é muito elegante.
Alojz Janez
Concordo que os pedidos são o caminho a percorrer nos dias de hoje. Votei positivamente neste comentário e na resposta de Marian, mas estou deixando a resposta como premiada, pois foi a melhor na época.
João
1
Os prêmios @John são bons, mas o tempo passa e este é um site editado pela comunidade. O foco está nas boas respostas e não nas pessoas. Ele manterá seus pontos positivos. Você está enganando toneladas de outros programadores em bibliotecas obsoletas.
mit
1
Ok, é justo. Aceitei a resposta dos pedidos.
João

Respostas:

180

Esta é a forma de Solicitações :

import requests
r = requests.get('http://github.com', allow_redirects=False)
print(r.status_code, r.headers['Location'])
Mariana
fonte
5
Então olhe r.headers['Location']para ver para onde isso o teria levado
patricksurry
Observe que parece que as solicitações serão normalizadas Locationpara location.
Hamish
2
@Hamish requestspermite que você acesse os cabeçalhos tanto na forma canônica quanto em minúsculas. Veja docs.python-requests.org/en/master/user/quickstart/…
Marian
1
A partir de 2019 em Python 3, isso parece não funcionar mais para mim. (Recebo um erro de ditado chave.)
Max von Hippel
35

Dive Into Python tem um bom capítulo sobre como lidar com redirecionamentos com urllib2. Outra solução é httplib .

>>> import httplib
>>> conn = httplib.HTTPConnection("www.bogosoft.com")
>>> conn.request("GET", "")
>>> r1 = conn.getresponse()
>>> print r1.status, r1.reason
301 Moved Permanently
>>> print r1.getheader('Location')
http://www.bogosoft.com/new/location
olt
fonte
7
Todo mundo que vem do google, por favor, observe que o jeito mais atualizado de ir é este: stackoverflow.com/a/14678220/362951 A biblioteca de solicitações vai poupar muitas dores de cabeça.
mit
O link para "Dive Into Python" está morto.
guettli
11

Este é um manipulador urllib2 que não seguirá os redirecionamentos:

class NoRedirectHandler(urllib2.HTTPRedirectHandler):
    def http_error_302(self, req, fp, code, msg, headers):
        infourl = urllib.addinfourl(fp, headers, req.get_full_url())
        infourl.status = code
        infourl.code = code
        return infourl
    http_error_300 = http_error_302
    http_error_301 = http_error_302
    http_error_303 = http_error_302
    http_error_307 = http_error_302

opener = urllib2.build_opener(NoRedirectHandler())
urllib2.install_opener(opener)
Carles Barrobés
fonte
Estou testando uma API de unidade e lidando com um método de login que redireciona para uma página que não interessa, mas não envia o cookie de sessão desejado com a resposta ao redirecionamento. Isso é exatamente o que eu precisava para isso.
Tim Wilder
9

A redirectionspalavra-chave no httplib2método de solicitação é uma pista falsa. Em vez de retornar a primeira solicitação, ele gerará uma RedirectLimitexceção se receber um código de status de redirecionamento. Para retornar a resposta inicial, você precisa definir follow_redirectscomo Falseno Httpobjeto:

import httplib2
h = httplib2.Http()
h.follow_redirects = False
(response, body) = h.request("http://example.com")
Ian Mackinnon
fonte
8

suponho que isso ajudaria

from httplib2 import Http
def get_html(uri,num_redirections=0): # put it as 0 for not to follow redirects
conn = Http()
return conn.request(uri,redirections=num_redirections)
Ashish
fonte
5

Segui a indicação de olt para Dive into Python . Aqui está uma implementação usando manipuladores de redirecionamento urllib2, mais trabalho do que deveria? Talvez, dê de ombros.

import sys
import urllib2

class RedirectHandler(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                                 
        raise Exception("Permanent Redirect: %s" % 301)

    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                                
        raise Exception("Temporary Redirect: %s" % 302)

def main(script_name, url):
   opener = urllib2.build_opener(RedirectHandler)
   urllib2.install_opener(opener)
   print urllib2.urlopen(url).read()

if __name__ == "__main__":
    main(*sys.argv) 
Aaron Maenpaa
fonte
3
Parece errado ... Este código realmente segue os redirecionamentos (chamando o manipulador original, emitindo assim uma solicitação HTTP) e, em seguida,
gera
5

O caminho mais curto, entretanto, é

class NoRedirect(urllib2.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, hdrs, newurl):
        pass

noredir_opener = urllib2.build_opener(NoRedirect())
Tzury Bar Yochay
fonte
1
Como esse é o caminho mais curto? Ele nem mesmo contém a importação ou a solicitação real.
Mariana
Eu já ia postar essa solução e fiquei bastante surpreso ao encontrar essa resposta no final. É muito conciso e deve ser a resposta principal na minha opinião.
usuário
Além disso, dá mais liberdade, desta forma é possível controlar quais URLs seguir .
usuário
Confirmo, esta é a forma mais fácil. Um breve comentário para quem deseja depurar. Não se esqueça de que você pode definir múltiplos manipuladores ao construir o abridor como: opener = urllib.request.build_opener(debugHandler, NoRedirect())onde debugHandler=urllib.request.HTTPHandler()e debugHandler.set_http_debuglevel (1). No final:urllib.request.install_opener(opener)
StashOfCode