Czy istnieje łatwy sposób, aby zażądać adresu URL w pythonie i NIE podążać za przekierowaniami?

101

Patrząc na źródło urllib2, wygląda na to, że najłatwiejszym sposobem byłoby utworzenie podklasy HTTPRedirectHandler, a następnie użycie build_opener do zastąpienia domyślnego HTTPRedirectHandler, ale wydaje się, że jest to dużo (stosunkowo skomplikowanej) pracy, aby wykonać to, co wygląda na to, że powinno być dość proste.

Jan
źródło
2
Dla pracowników Google: korzystanie z biblioteki żądań pozwoli Ci zaoszczędzić wiele bólu głowy: docs.python-requests.org i zobacz odpowiedź Mariana poniżej, jest bardzo elegancka.
Alojz Janez
Zgadzam się, że prośby są obecnie drogą. Głosowałem za tym komentarzem i odpowiedzią Mariana, ale pozostawiam odpowiedź jako przyznaną, ponieważ była wtedy najlepsza.
John
1
Nagrody @John są dobre, ale czas mija i jest to witryna redagowana przez społeczność. Skupiamy się na dobrych odpowiedziach, a nie na ludziach. Zachowa swoje punkty upvote. Wprowadzasz w błąd mnóstwo innych programistów do przestarzałych bibliotek.
mit
1
Ok, w porządku. Przyjąłem odpowiedzi na prośby.
John

Odpowiedzi:

187

Oto sposób Żądania :

import requests
r = requests.get('http://github.com', allow_redirects=False)
print(r.status_code, r.headers['Location'])
Marian
źródło
6
Następnie spójrz, r.headers['Location']dokąd by cię to wysłało
patricksurry
Zwróć uwagę, że wygląda na to, że Request będzie normalizować się Locationdo location.
Hamish
2
@Hamish requestsumożliwia dostęp do nagłówków zarówno w formie kanonicznej, jak i małych liter. Zobacz docs.python-requests.org/en/master/user/quickstart/…
Marian
1
Od 2019 roku w Pythonie 3 wydaje mi się, że to już nie działa. (Dostaję kluczowy błąd dyktowania.)
Max von Hippel
36

Dive Into Python ma dobry rozdział na temat obsługi przekierowań za pomocą urllib2. Innym rozwiązaniem jest 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
źródło
7
Każdy, kto przychodzi tutaj z Google, pamiętaj, że aktualna droga jest następująca: stackoverflow.com/a/14678220/362951 Biblioteka żądań pozwoli Ci zaoszczędzić wiele bólu głowy.
mit
Link do „Dive Into Python” nie działa.
guettli
11

To jest moduł obsługi urllib2, który nie będzie podążał za przekierowaniami:

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
źródło
Testuję jednostkowo interfejs API i mam do czynienia z metodą logowania, która przekierowuje na stronę, na której mi nie zależy, ale nie wysyła żądanego pliku cookie sesji z odpowiedzią na przekierowanie. Właśnie tego potrzebowałem.
Tim Wilder,
9

Słowo redirectionskluczowe w httplib2metodzie request to czerwony śledź. Zamiast zwracać pierwsze żądanie, zgłosi RedirectLimitwyjątek, jeśli otrzyma kod stanu przekierowania. Aby przywrócić inital odpowiedź trzeba ustawić follow_redirectssię Falsena Httpobiekcie:

import httplib2
h = httplib2.Http()
h.follow_redirects = False
(response, body) = h.request("http://example.com")
Ian Mackinnon
źródło
8

przypuszczam, że to by pomogło

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
źródło
5

Drugi wskaźnik olta do Dive into Python . Oto implementacja wykorzystująca programy obsługi przekierowań urllib2, więcej pracy niż powinno? Może wzruszysz ramionami.

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
źródło
3
Wygląda źle ... Ten kod faktycznie podąża za przekierowaniami (wywołując oryginalną procedurę obsługi,
wysyłając w
5

Jednak najkrótsza droga jest

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

noredir_opener = urllib2.build_opener(NoRedirect())
Tzury Bar Yochay
źródło
1
Jak to jest najkrótsza? Nie zawiera nawet importu ani rzeczywistego żądania.
Marian
Miałem już zamiar opublikować to rozwiązanie i byłem dość zaskoczony, gdy znalazłem tę odpowiedź na dole. Jest bardzo zwięzła i moim zdaniem powinna być najlepszą odpowiedzią.
użytkownik
Ponadto zapewnia większą swobodę, w ten sposób można kontrolować, które adresy URL należy śledzić .
użytkownik
Potwierdzam, to najłatwiejszy sposób. Krótka uwaga dla tych, którzy chcą debugować. Nie zapominaj, że możesz ustawić obsługę wielokrotności podczas buldowania otwieracza, takich jak: opener = urllib.request.build_opener(debugHandler, NoRedirect())gdzie debugHandler=urllib.request.HTTPHandler()i debugHandler.set_http_debuglevel (1). Na koniec:urllib.request.install_opener(opener)
StashOfCode