2017-03-04 45 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 ...>