21

我正在尝试使用 检索文件urlretrieve,同时添加自定义标头。

在检查代码源时,urllib.request我意识到urlopen可以Request在参数中获取一个对象而不仅仅是一个字符串,从而允许放置我想要的标题。但是如果我尝试对 做同样的事情urlretrieve,我会得到一个TypeError: expected string or bytes-like object,正如另一篇文章中提到的那样。

我最终做的是重写我自己的 urlretrieve,删除抛出错误的行(该行与我的用例无关)。

它工作正常,但我想知道是否有更好/更清洁的方法,而不是重写我自己的urlretrieve. 如果可以将自定义标头传递给urlopen,感觉应该可以对urlretrieve?

4

2 回答 2

62

我找到了一种方法,您只需添加几行额外的代码...

import urllib.request

opener = urllib.request.build_opener()
opener.addheaders = [('User-agent', 'Mozilla/5.0')]
urllib.request.install_opener(opener)
urllib.request.urlretrieve("type URL here", "path/file_name")

如果您想了解详细信息,可以参考 python 文档:https ://docs.python.org/3/library/urllib.request.html

于 2017-10-01T10:22:05.313 回答
6

内部的urllib.request.urlretrieve()使用urllib.request.urlopen()(至少在 Python 3 中)。因此,您可以使用相同的方式来影响urlopen.

urlopen(params)被调用时,它实际上首先查看特殊的全局变量urllib.request._opener,如果是,Noneurlopen使用默认的开启程序集设置变量,否则它将保持原样。在下一步中,它将调用urllib.request._opener.open(<urlopen_params>)(在接下来的部分中,我将urllib.request._opener仅将其称为opener)。

opener.open()包含不同协议的处理程序列表。当opener.open()被调用时,它将执行以下操作:

  1. 从 URLurllib.request.Request对象创建(或者如果您直接提供Request它只会使用它)。
  2. Request对象中提取协议(它从 URL 方案推导出来)。
  3. 根据协议,它将尝试查找并使用这些方法:
    • protocol_request(例如http_request) - 它用于在打开连接之前预处理请求。
    • protocol_open- 实际上创建与远程服务器的连接
    • protocol_response- 处理来自服务器的响应
    • 对于其他方法,请查看Python 的文档

对于您自己的开瓶器,您必须执行以下 3 个步骤:

  1. 创建自己的处理程序
  2. 处理程序的构建列表包含您的自定义处理程序(函数urllib.request.build_opener
  3. 将新的开瓶器安装到urllib.request._opener(功能urllib.request.install_opener

创建包含您的自定义处理程序的urllib.request.build_opener开启程序并添加默认开启程序,但您的自定义处理程序继承自处理程序。

因此,要添加自定义标头,您可以编写如下内容:

import urllib.request as req

class MyHTTP(req.HTTPHandler):
    def http_request(self, req):
        req.headers["MyHeader"] = "Content of my header"
        return super().http_request(req)

opener = req.build_opener(MyHTTP())
req.install_opener(opener)

从这一点开始,当您调用urllib.request.urlretrieve()或任何使用urlopen()它的东西时,它将用于您的处理程序的 HTTP 通信。当您想返回默认处理程序时,您可以调用:

import urllib.request as req   

req.install_opener(req.build_opener())

老实说,我不知道它是否比您的解决方案更好/更清洁,但它在urllib.

于 2017-07-25T20:46:16.793 回答