2017-03-04 75 views
5

使用会话时,您似乎需要每次都提供完整的URL,例如,Python请求:会话中的URL基址

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

这会变得有点乏味。有没有办法做这样的事情:

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

回答

1

我没有看到一个内置的方式做到这一点,但您可以使用包装函数添加你想要的功能:

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__')) 
) 

现在,当你建立一个新的requests.Session对象,你可以指定一个基本URL:

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') 
+0

我喜欢这个答案,但它只适用于基础url没有sublevels之类的情况,因为'urljoin'用覆盖它们的URL作为获取和发布方法的URL。我需要它在我的情况下,所以我用简单的字符串连接替换了'urljoin'调用 –

4

你可以只继承request.Session和过载的__init__request的方法是这样的:

# 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) 

然后你可以在代码中使用你的子类,而不是requests.Session

from my_requests import SessionWithUrlBase 


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

你也可以猴子补丁requests.Session避免修改现有的代码库(该实施应100%兼容),但一定要做到实际打补丁的任何代码调用之前requests.Session()

# monkey_patch.py 
import requests 


class SessionWithUrlBase(requests.Session): 
    ... 

requests.Session = SessionWithUrlBase 

然后:

# main.py 
import requests 
import monkey_patch 


session = requests.Session() 
repr(session) # <monkey_patch.SessionWithUrlBase object at ...>