Peticiones de Python: URL base en Session

Al usar una sesión, parece que necesita proporcionar la URL completa cada vez, por ejemplo,

session = requests.Session() session.get('http://myserver/getstuff') session.get('http://myserver/getstuff2') 

Esto se pone un poco tedioso. ¿Hay una manera de hacer algo como:

 session = requests.Session(url_base='http://myserver') session.get('/getstuff') session.get('/getstuff2') 

request_toolbelt.sessions.BaseUrlSession https://github.com/requests/toolbelt/blob/f5c86c51e0a01fbc8b3b4e1c286fd5c7cb3aacfa/requests_toolbelt/sessions.py#L6

NOTA: Esto usa urljoin from standard lib. Cuidado con el comportamiento de los urljoin.

 In [14]: from urlparse import urljoin In [15]: urljoin('https://localhost/api', '/resource') Out[15]: 'https://localhost/resource' In [16]: urljoin('https://localhost/api', 'resource') Out[16]: 'https://localhost/resource' In [17]: urljoin('https://localhost/api/', '/resource') Out[17]: 'https://localhost/resource' In [18]: urljoin('https://localhost/api/', 'resource') Out[18]: 'https://localhost/api/resource' 

O

 import requests from functools import partial def PrefixUrlSession(prefix=None): if prefix is None: prefix = "" else: prefix = prefix.rstrip('/') + '/' def new_request(prefix, f, method, url, *args, **kwargs): return f(method, prefix + url, *args, **kwargs) s = requests.Session() s.request = partial(new_request, prefix, s.request) return s 

Usted podría simplemente subclasificar request.Session y sobrecargar su __init__ y request métodos como este:

 # my_requests.py import requests class SessionWithUrlBase(requests.Session): # In Python 3 you could place `url_base` after `*args`, but not in Python 2. def __init__(self, url_base=None, *args, **kwargs): super(SessionWithUrlBase, self).__init__(*args, **kwargs) self.url_base = url_base def request(self, method, url, **kwargs): # Next line of code is here for example purposes only. # You really shouldn't just use string concatenation here, # take a look at urllib.parse.urljoin instead. modified_url = self.url_base + url return super(SessionWithUrlBase, self).request(method, modified_url, **kwargs) 

Y luego podría usar su subclase en lugar de requests.Session en su código:

 from my_requests import SessionWithUrlBase session = SessionWithUrlBase(url_base='https://stackoverflow.com/') session.get('documentation') # https://stackoverflow.com/documentation 

También puede requests.Session parches. requests.Session para evitar modificar la base de código existente (esta implementación debería ser 100% compatible), pero asegúrese de hacer parches reales antes de que cualquier código llame a requests.Session() :

 # monkey_patch.py import requests class SessionWithUrlBase(requests.Session): ... requests.Session = SessionWithUrlBase 

Y entonces:

 # main.py import requests import monkey_patch session = requests.Session() repr(session) #  

Esta característica se ha solicitado en los foros varias veces 1 , 2 , 3 . El enfoque preferido como se documenta aquí , es la subclasificación, como sigue:

 from requests import Session from urlparse import urljoin class LiveServerSession(Session): def __init__(self, prefix_url=None, *args, **kwargs): super(LiveServerSession, self).__init__(*args, **kwargs) self.prefix_url = prefix_url def request(self, method, url, *args, **kwargs): url = urljoin(self.prefix_url, url) return super(LiveServerSession, self).request(method, url, *args, **kwargs) 

Usted usaría esto simplemente como sigue:

 baseUrl = 'http://api.twitter.com' with LiveServerSession(baseUrl) as s: resp = s.get('/1/statuses/home_timeline.json') 

No veo una forma integrada de hacer esto, pero puedes usar funciones de envoltura para agregar la funcionalidad que deseas:

 from functools import wraps import inspect import requests from requests.compat import urljoin def _base_url(func, base): '''Decorator for adding a base URL to func's url parameter''' @wraps(func) def wrapper(*args, **kwargs): argname = 'url' argspec = inspect.getargspec(func) if argname in kwargs: kwargs[argname] = urljoin(base, kwargs[argname]) else: # Find and replace url parameter in positional args. The argspec # includes self while args doesn't, so indexes have to be shifted # over one for i, name in enumerate(argspec[0]): if name == argname: args = list(args) args[i-1] = urljoin(base, args[i-1]) break return func(*args, **kwargs) return wrapper def inject_base_url(func): '''Decorator for adding a base URL to all methods that take a url param''' @wraps(func) def wrapper(*args, **kwargs): argname = 'base_url' if argname in kwargs: obj = args[0] # Add base_url decorator to all methods that have a url parameter for name, method in inspect.getmembers(obj, inspect.ismethod): argspec = inspect.getargspec(method.__func__) if 'url' in argspec[0]: setattr(obj, name, _base_url(method, kwargs[argname])) del kwargs[argname] return func(*args, **kwargs) return wrapper # Wrap requests.Session.__init__ so it takes a base_url parameter setattr( requests.Session, '__init__', inject_base_url(getattr(requests.Session, '__init__')) ) 

Ahora puede especificar una URL base cuando construye un nuevo pedido. Objeto de sesión:

 s = requests.Session(base_url='http://stackoverflow.com') s.get('questions') # http://stackoverflow.com/questions s.post('documentation') # http://stackoverflow.com/documentation # With no base_url, you get the default behavior s = requests.Session() s.get('http://google.com')