Python的urllib.request用于HTTP请求
在本教程中,我们将学习Python的urllib.request并向示例URL发送GET请求。我们还将向示例REST API发送GET请求以获取一些JSON数据。我们还将学习使用urllib.request库发送POST请求。urllib库在使用Python代码打开URL方面起到了重要作用。除了打开URL,这个库还有许多其他功能。稍后我们会探索它的重要功能。让我们简要介绍一下urllib库。
Python的urllib模块
Python的urllib模块允许我们通过Python代码访问网站。urllib这个工具提供了通过Python代码运行GET、POST和PUT方法以及在我们的Python代码中模拟JSON数据的灵活性。我们可以使用urllib库下载数据,访问网站,解析数据并修改头部。urllib有两个版本——urllib2和urllib3。urllib2用于Python 2,urllib3用于Python 3。urllib3有一些高级功能。
一些网站不允许程序侵入其网站数据并给服务器带来负载。当他们发现这样的程序时,他们有时选择阻止该程序或为普通用户使用不同的数据。但是,我们可以用简单的代码克服这种情况。我们只需要修改请求头中包含的用户代理。
对于不了解的人来说,头部是用于与服务器共享数据的数据块,以便服务器能够了解我们。其中,Python版本通知网站我们正在使用urllib和Python版本访问网站。
urllib模块由几个模块组成,用于处理URL。以下是这些模块:
- urllib.request用于打开和阅读。
- urllib.parse用于解析URL。
- urllib.error用于引发异常。
- urllib.robotparser用于解析robot.txt文件。
大多数情况下,urllib包将随Python安装一起提供,如果没有在您的环境中,可以使用以下命令下载:
pip install urllib
它将在您的环境中安装urllib。
HTTP消息的基础
为了更好地理解urllib.request,我们需要了解HTTP消息。HTTP消息可以被视为文本,以字节流的形式传输,遵循RFC 7230指定的指南。解码的HTTP消息可以如下所示。
GET / HTTP/1.1
Host: www.google.com
GET请求使用HTTP/1.1协议在根路径(/)进行。HTTP方法有两个主要部分-元数据和主体。上述消息具有所有元数据但没有主体。响应也由两个部分组成-
HTTP/1.1 200 OK
Content-Type: text/html; charset=ISO-8859-1
Server: gws
(... other headers ...)
<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage"
如我们所见,响应以状态码200 OK开始。这是响应的元数据。我们展示了标准的HTTP消息,但有些可能违反这些规则或者遵循较旧的规范。我们可以获得许多键值对数据,如表示所有的响应头。
urllib.request
让我们了解一下urllib.request.OpenURL()方法的示例。我们需要导入urllib3模块,并将打开的URL分配给一个变量,我们可以使用read()命令来读取数据。
当我们使用urllib.request.urlopen()发出请求时,它返回一个HTTPResponse对象。我们可以使用dir()函数打印HTTPResponse对象,以查看不同的方法和属性。
示例
from urllib.request import urlopen
with urlopen("https://www.google.com") as response:
print(dir(response))
输出:
['__abstractmethods__', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_abc_impl', '_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', '_check_close', '_close_conn', '_get_chunk_left', '_method', '_peek_chunked', '_read1_chunked', '_read_and_discard_trailer', '_read_next_chunk_size', '_read_status', '_readall_chunked', '_readinto_chunked', '_safe_read', '_safe_readinto', 'begin', 'chunk_left', 'chunked', 'close', 'closed', 'code', 'debuglevel', 'detach', 'fileno', 'flush', 'fp', 'getcode', 'getheader', 'getheaders', 'geturl', 'headers', 'info', 'isatty', 'isclosed', 'length', 'msg', 'peek', 'read', 'read1', 'readable', 'readinto', 'readinto1', 'readline', 'readlines', 'reason', 'seek', 'seekable', 'status', 'tell', 'truncate', 'url', 'version', 'will_close', 'writable', 'write', 'writelines']
示例
import urllib.request
request_url = urllib.request.urlopen('https://www.google.com/')
print(request_url.read())
输出:
b'<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="en-IN"><head><meta content="text/html; charset=UTF-8" http-equiv="Content-Type"><meta content="/images/branding/googleg/1x/googleg_standard_color_128dp.png" itemprop="image"><title>Google</title><script nonce="miKBzQYvtiMON4gUmZ+2qA==">(function(){window.google={kEI:\'lj1QYuXdLNOU-AaAz5XIDg\',kEXPI:\'0,1302536,56873,1710,4348,207,4804,2316,383,246,5,1354,4013,1238,1122515,1197791,610,380090,16114,19398,9286,17572,4859,1361,9290,3027,17582,4020,978,13228,3847,4192,6430,22112,629,5081,1593,1279,2742,149,1103,841,6296,3514,606,2023,1777,520,14670,3227,2845,7,17450,16320,1851,2614,13142,3,346,230,1014,1,5444,149,11325,2650,4,1528,2304,6463,576,22023,3050,2658,7357,30,13628,13795,7428,5800,2557,4094,4052,3,3541,1,16807,22235,2,3110,2,14022,1931,784,255,4550,743,5853,10463,1160,4192,1487,1020,2381,2718,18233,28,2,2,5,7754,4567,6259,3008,3,3712,2775,13920,830,422,5835,11862,3105,1539,2794,8,4669,1413,1394,445,2,2,1,6395,4561,10491,2378,701,252,1407,10,1,8591,113,5011,5,1453,637,162,2,460,2,430,586,969,593,5214,2215,2343,1866,1563,4987,791,6071,2,1,5829,227,161,983,3110,773,1217,2780,933,504,1259,957,749,6,601,23,31,748,100,1392,2738,92,2552,106,197,163,1315,1133,316,364,810,3,333,233,1586,229,81,967,2,440,357,298,258,1863,400,1698,417,4,881,219,20,396,2,397,481,75,5444944,1269,5994875,651,2801216,882,444,3,1877,1,2562,1,748,141,795,563,1,4265,1,1,2,1331,4142,2609,155,17,13,72,139,4,2,20,2,169,13,19,46,5,39,96,548,29,2,2,1,2,1,2,2,7,4,1,2,2,2,2,2,2,353,513,186,1,1,158,3,2,2,2,2,2,4,2,3,3,269,1601,141,1002,98,214,133,10,9,9,2,10,3,1,11,10,6,239
上面的代码返回了www.google.com的源代码;我们可以使用正则表达式清理结果。网页使用HTML、CSS和JavaScript编写,但我们的程序通常只提取文本。因此,正则表达式变得有用;我们将在下一节中使用它。
数据传输有两种方法 – GET和POST。GET方法用于获取数据,POST方法用于在服务器上发送数据,与我们发布一些数据并基于发布后收到的请求相同。我们还可以使用GET和POST从/到URL进行请求。
现在让我们看看urllib.parse的用途。
urllib.parse
urllib.parse能够定义函数来操作URL及其组成部分,以创建或破坏它们。有一种方法可以将值硬编码并将其传递给URL,但我们可以使用urllib来完成。让我们理解以下示例。
示例
# used to parse values into the url
import urllib.parse
url = 'https://www.google.com/search'
values = {'query' : 'python tutorial'}
在上述代码中,我们定义了要与POST方法一起使用的值,以与URL一起发送。为此,首先我们需要对所有的值进行编码。我们将它们编码为utf-8字节。我们对字典请求进行了编码。然后,我们使用带有请求的URL打开该URL。最后,我们使用read()函数读取响应。
示例
data = urllib.parse.urlencode(values)
data = data.encode('utf-8') # data should be bytes
req = urllib.request.Request(url, data)
resp = urllib.request.urlopen(req)
respData = resp.read()
print(respData)
输出:
urllib.error.HTTPError: HTTP Error 405: Method Not Allowed
当我们运行上面的代码时,Google将返回405,表示不允许的方法。Google不允许使用Python脚本发送请求。我们可以使用另一个带有搜索栏的网站来使用Python发送请求。
有时,网站不喜欢被垃圾邮件发送者或机器人访问,或者它们可能对它们进行不同的处理。在过去,大多数网站会阻止程序。维基百科曾经直接阻止程序,但现在它们会提供一个类似Google的页面。
每次请求URL时,我们都会发送一个标头,其中包含关于我们的基本信息。标头中有一个用户代理值,用于定义访问网站服务器的浏览器。这就是Google Analytics确定我们使用的浏览器的方式。
默认情况下,如果我们使用Python 3.4版本,则urllib的用户代理为3.4。这要么在网站上是未知的,否则它们将完全阻止它。
示例:
import urllib.parse
import urllib.request
try:
url = 'https://www.google.com/search?q=python-tutorial'
# now, with the below headers, we defined ourselves as a simpleton who is
# still using internet explorer.
headers = {}
headers['User-Agent'] = "Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.27 Safari/537.17"
request = urllib.request.Request(url, headers = headers)
resp = urllib.request.urlopen(request)
respData = resp.read()
saveFile = open('ModifyHeaders.txt','w')
saveFile.write(str(respData))
saveFile.close()
except Exception as e:
print(str(e))
我们已经修改了我们的标题,并运行了相同的内容。我们发出请求,我们的响应确实是不同的。
常见的urllib.request问题
在运行urllib.request程序时,程序可能会抛出错误。在本节中,我们将解决一些最常见的在刚开始时遇到的错误。
在了解具体的错误之前,我们将学习如何实施错误处理。
实施错误处理
Web开发常常出现错误,解决这些错误可能需要很长时间。我们将定义如何处理在使用urllib.request时出现的HTTP、URL和超时错误。
每个响应的状态行中都附带有HTTP状态码。如果我们能够读取响应中的状态码,那么请求就会到达目标。有预定义的状态码与响应配对使用。例如- 200和201代表成功的请求。如果请求的状态码是404和405,则表示有问题。
还可能出现连接错误,或者提供的URL不正确。在这种情况下,urllib.request会引发错误。
最后,服务器需要很长时间才能响应。可能是网络连接慢,服务器停机,或者服务器被编程忽略特定的请求。为了解决这个问题,我们可以通过向 urlopen() 函数传递一个超时参数,在一定时间后引发 TimeoutError 。我们需要先使用try….expect子句捕获这些错误来解决这些问题。
示例
from urllib.error import HTTPError, URLError
from urllib.request import urlopen
def request_func(url):
try:
with urlopen(url, timeout=10) as response:
print(response.status)
return response.read(), response
except HTTPError as error:
print(error.status, error.reason)
except URLError as error:
print(error.reason)
except TimeoutError:
print("Request timed out")
request_func() 函数接受一个字符串参数,将尝试使用 urllib.request 从URL获取请求。如果URL不正确,它将捕捉到URLError。它还会捕捉到引发错误的 HTTPError 对象。如果没有错误,它将打印状态并返回主体和响应。
正如我们所观察到的,我们调用了带有超时参数的 urlopen() 函数,它将在指定的秒数后引发一个 TimeoutError 。程序将等待十秒钟,这是等待响应的一个很好的时间。
处理403错误
403状态表示服务器理解请求但不会履行。在使用网络爬虫时,这是常见的错误。如上所述,这个错误可以通过使用 User-Agent 标头来解决。有时人们会混淆两个主要相关的4xx代码。
- 401未授权
- 403禁止访问
401错误 – 如果用户未经识别或登录,并且必须做些什么才能获得访问权限(如登录或注册),服务器将返回此错误。
403错误 – 如果用户已经经过识别但无权访问资源,则服务器将返回此错误。
幸运的是,我们可以通过修改网络上的User-Agent字符串来处理这种错误,包括一个User-Agent数据库。
请求包生态系统
本节将阐明Python中围绕HTTP请求的包生态系统。有多个包没有明确的标准。我们必须熟悉我们需求的正确包。
什么是urllib2和urllib3
urllib库被分成了几个部分。如果我们回顾早期版本的Python 1.2,当最初的urllib被引入时,大约在版本1.6左右,新增了一种改进的urllib2。但是,当引入Python 3时,原始的urllib被弃用,并且urllib2去掉了”2″,使用了原始的urllib名称。
- urllib.error
- urllib.parse
- urllib.request
- urllib.response
- urllib.robotparser
urllib3是一个独立开发的第三方库,它是在urllib2还存在时开发的。
何时使用requests而不是urllib.requests
重要的是要知道何时使用request或urllib.request库。urllib.request旨在成为一个低级别的库,揭示了关于HTTP请求工作的许多细节。
请求 库在用户与许多不同的 REST API 进行交互时是非常推荐的。请求库的官方文档声称它是“为人类而建”,并成功地创建了一个直观、安全和简单的 HTTP API。请求库使我们能够轻松地执行复杂的任务,特别是在字符编码方面。使用 urllib 库时,我们必须注意编码,并采取措施确保没有错误的体验。
如果您更关注学习更标准的 Python 和如何处理 HTTP 请求,urllib.request 是最好的入门方式。您甚至可以进一步使用非常低级的 http 模块。
结论
我们详细讨论了 urllib 库,并学习了如何使用 Python 脚本进行请求。我们可以在项目中使用这个内置模块,使它们长时间不依赖其他库。本教程探讨了 urllib 库及其低级模块,如 urllib.request,并解释了如何处理常见错误以及如何解决它们。