使用 urllib、urllib2 和 request 避免 "Pyramid of Doom" 之类的语法

Avoiding the "Pyramid of Doom" like syntax with urllib, urllib2 and request

很难为响应编写 Python2 和 Python3 以及 request 依赖代码,因为它们 urlopen() 函数和 requests.get() 函数 return 不同类型:

为了同时支持 Python2 和 Python3 以及不想安装 request 依赖项的用户,我尝试了 "Pyramid of Doom" 看起来 try-except 在导入和 get_content() 函数中:

try: # Try importing requests first.
    import requests
except ImportError: 
    try: # Try importing Python3 urllib
        import urllib.request
    except AttributeError: # Now importing Python2 urllib
        import urllib


def get_content(url):
    try:  # Using requests.
        return requests.get(url).content # Returns requests.models.Response.
    except NameError:  
        try: # Using Python3 urllib.
            with urllib.request.urlopen(index_url) as response:
                return response.read() # Returns http.client.HTTPResponse.
        except AttributeError: # Using Python3 urllib.
            return urllib.urlopen(url).read() # Returns an instance.

是否有其他方法可以达到 return读取内容并避免嵌套 try-except 的相同结果?

您的用户使用 requests 有什么好处?简单地忽略 requests 并支持标准库函数会更容易。这可以通过像这样导入对其余代码透明地完成:

try:
    from urllib.request import urlopen
except ImportError:
    from urllib2 import urlopen

从那时起,所有 GET 请求都可以用 urlopen(url) 发出。可以使用 read().

检索返回的数据

现在,如果您真的想继续 requests 支持,您可以像这样将导入代码与 get_content() 的定义一起编写:

try:
    import requests
    get_content = lambda url : requests.get(url).content
except ImportError:
    try:                   # Python 3
        from urllib.request import urlopen
    except ImportError:    # Python2
        from urllib2 import urlopen
    get_content = lambda url : urlopen(url).read()

正如每个人都提到的,如果你走的是 urllib 的路线,你不应该太在意请求。但是如果你仍然想避免过多的嵌套异常,你可以使用类似下面的东西

import sys
key = str(sys.version_info.major)

try:  # Try importing requests first.
    import requests
    key = "requests"
except ImportError:
    try:  # Try importing Python3 urllib
        import urllib.request
    except (AttributeError, ImportError):  # Now importing Python2 urllib
        import urllib

FUNC = {
    "2": {
        "urlopen": lambda url: urllib.urlopen(url),
        "read": lambda res: res.read()
    },
    "requests": {
        "urlopen": lambda url: requests.get(url),
        "read": lambda res: res.content
    },
    "3": {
        "urlopen": lambda url: urllib.request.urlopen(url),
        "read": lambda res: res.read()
    },
}

urlopen = FUNC[key]["urlopen"]
read = FUNC[key]["read"]

def get_content(url):
    res = urlopen(url)
    return read(res)

print(get_content("http://tarunlalwani.com"))

您可以尝试将所有三个库导入为 request,并检查 get_content 函数中的属性以识别导入的库。
作为嵌套 try-except 块的替代方案,您可以在单独的 try-except 块中导入 requests, 如果安装了模块,它将覆盖 urllib / urllib2

try: 
    import urllib.request as request
except ImportError: 
    import urllib2 as request
try: 
    import requests as request
except ImportError:
    pass

def get_content(url):
    return request.get(url).content if hasattr(request, 'get') else request.urlopen(url).read()

这会产生干净简单的代码,并允许您提交更复杂的请求,例如,

def get_content(url, headers={}):
    if hasattr(request, 'get'):
        return request.get(url, headers=headers).content
    else:
        return request.urlopen(request.Request(url, headers=headers)).read()