Python之urllib库的用法
参考:https://zhuanlan.zhihu.com/p/146016738
urllib库的作用
爬虫的第一个步骤是获取网页,urllib库是用来实现这个功能:想服务器发送请求,得到服务器响应,获取网页的内容。
Python的强大在于提供了功能齐全的类库,来帮助我们完成这个请求,通过调用urllib库,我们不需要了解请求的数据结构,HTTP,TCP,IP层网络传输同学,以及服务器应答原理等。
我们只需要关心以下三点,然后通过几行调用urllib库的代码,就能够获得我们想要的网页内容。
- 请求的URL是什么
- 传递的参数是什么
- 如何设置可选的请求头
urllib库的构成
在python2中,曾经有urllib和urllib2两个库来实现请求的发送,但目前通用的python3版本中,两个库的功能已经合并成一个库,统一为urllib,它是python内置函数,不需要额外安装即可使用。
urllib的四个模块
【1】requset:HTTP请求模块,可以用来模拟发送请求,只需要传入URL及额外参数,就可以模拟浏览器访问网页的过程。
【2】error:异常处理模块,检测请求是否报错,捕捉异常错误,进行重试或其他操作,保证程序不会终止。
【3】parse:工具模块,提供许多URL处理方法,如拆分、解析、合并等。
【4】robotparser:识别网站的robots.txt文件,判断哪些网站可以爬,哪些网站不可以爬,使用频率较少。
发送请求
urlopen是request模块中的方法,用于抓取网络。
官方文档:https://docs.python.org/3/library/urllib.request.html
我们以代码示例,我们抓取百度的网页
1 2 3 4 5 6 7 8 | # 调用urllib库中的request模块 import urllib.request # 发送请求获取百度网页的响应 response = urllib.request.urlopen( "http://www.baidu.com" ) # 打印响应内容 # read()是把响应对象内容全部读取出来,读取出来为bytes码 # decode('utf-8')把bytes码解码 print (response.read().decode( 'utf-8' )) |
返回的结果比较多,随便截取其中一部分,可以看出是百度的网页HTML源代码。
我们只用几行代码,就完成了百度的抓取,并打印了网页的源代码,接下来,我们看一看我们获得的响应内容response到底是什么?利用type()方法来输出响应的类型。
1 2 | print ( type (response)) # <class 'http.client.HTTPResponse'> |
它是一个HTTPResponse类型的对象
包含方法:read()、readinto()、getheader()、getheaders()、fileno()等
包含属性:msg、version、status、reason、debuglevel、closed等属性。
通过调用以上的方法和属性,就能返回我们所需的信息。
比如一开始我们打印获取网页内容时,就用到了read()方法。
调用status属性则可以得到返回结果的状态码,200代表强求成功,404代表网页未找到等。
再看一个代码示例加深理解:
1 2 3 4 5 6 7 8 9 | # 打印响应状态码 print (response.status, "\n" ) # 200 # 打印响应头信息 print (response.getheaders(), "\n" ) # [('Bdpagetype', '1'), ('Bdqid', '0x90ae03e0000443e3'), ('Cache-Control', 'private'), ('Content-Type', 'text/html;charset=utf-8'), ('Date', 'Tue, 24 Aug 2021 01:18:50 GMT'), ('Expires', 'Tue, 24 Aug 2021 01:18:50 GMT'), ('P3p', 'CP=" OTI DSP COR IVA OUR IND COM "'), ('P3p', 'CP=" OTI DSP COR IVA OUR IND COM "'), ('Server', 'BWS/1.1'), ('Set-Cookie', 'BAIDUID=162948399648CA18CD24B2476E039F6B:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com'), ('Set-Cookie', 'BIDUPSID=162948399648CA18CD24B2476E039F6B; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com'), ('Set-Cookie', 'PSTM=1629767930; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com'), ('Set-Cookie', 'BAIDUID=162948399648CA18CA2A9672599961C8:FG=1; max-age=31536000; expires=Wed, 24-Aug-22 01:18:50 GMT; domain=.baidu.com; path=/; version=1; comment=bd'), ('Set-Cookie', 'BDSVRTM=17; path=/'), ('Set-Cookie', 'BD_HOME=1; path=/'), ('Set-Cookie', 'H_PS_PSSID=34437_34441_31253_34004_34092_26350_34390; path=/; domain=.baidu.com'), ('Traceid', '1629767930035569306610425274448017114083'), ('Vary', 'Accept-Encoding'), ('Vary', 'Accept-Encoding'), ('X-Frame-Options', 'sameorigin'), ('X-Ua-Compatible', 'IE=Edge,chrome=1'), ('Connection', 'close'), ('Transfer-Encoding', 'chunked')] # 打印响应头的Server值 print (response.getheader( 'Server' ), "\n" ) # BWS/1.1 |
在打印的三行代码中
第一行输出了响应的状态码(200代表正常)
第二行输出了响应的头信息
第三行通过调用getheader()方法并传递参数Server,获取了第二行响应头信息中的Server对应的值BWS/1.1。
参数
利用基本的urlopen()方法可以完成最基本的简单网页GET方法抓取。
如果想达成更复杂一些的任务,需要给链接传递一些参数,该如何实现。
urlopen()的函数原型如下:
1 | urllib.request.urlopen(url, data = None , [timeout, ] * , cafile = None , capath = None , cadefault = False , context = None ) |
除了第一个参数传递URL之外,我们还可以传递其他参数,比如data(附加数据),timeout(超时时间)等。
data参数
data用来指明往服务器请求中的额外参数信息,data默认是None,此时以GET方式发送请求;当用户给出data参数的时候,改为POST方式发送请求。
data参数是可选的,如果需要添加该参数,需要使用bytes()方法将参数转化为二进制数据。
还是通过代码理解
首先了解模块urllib.parse.urlencode的用法
urllib.parse.urlencode用于把一个字典转换成对应的str
举例说明
1 2 3 4 5 6 7 | # urllib.parse.urlencode把字典转换成字符串str # 如果字典包含多个元素则之间使用&分隔 import urllib.parse print (urllib.parse.urlencode({ "word" : "hello" })) # word=hello print (urllib.parse.urlencode({ "word" : "hello" , "word1" : "hello1" })) # word=hello&word1=hello1 |
下面代码演示POST方法
1 2 3 4 5 6 7 8 9 | import urllib.parse import urllib.request # urllib.parse.urlencode({'word':'hello'})把字典转换为字符串 "word=hello" # bytes("word=hello",encoding='utf-8')把字符串转换成二进制b"word=hello" data = bytes(urllib.parse.urlencode({ 'word' : 'hello' }),encoding = 'utf-8' ) # 以下方法和上面转换的结果一致 # data = urllib.parse.urlencode({'word1':'hello'}).encode('utf-8') response = urllib.request.urlopen( "http://httpbin.org/post" ,data = data) print (response.read().decode( 'utf-8' )) |
首页这里请求的站点是http://httpbin.org,它是一个HTTP请求测试网站,记住它后面举例会经常用到它。
这次我们要使用post方式请求,而不是get,因为post是需要携带表单信息的(类似登陆的用户名和密码)所以我们要在urlopen函数中传递data参数。
前面讲了data参数必须是bytes型。
bytes()这个方法第一个参数需要str(字符串类型),这里就需要用到urllib库的parse模块里的urlencode()方法来将参数字典转化为字符串。第二个参数是 指定编码格式,这里指定为utf-8。
运行结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | { "args" : {}, "data" : "", "files" : {}, "form" : { "word" : "hello" }, "headers" : { "Accept-Encoding" : "identity" , "Content-Length" : "10" , "Content-Type" : "application/x-www-form-urlencoded" , "Host" : "httpbin.org" , "User-Agent" : "Python-urllib/3.6" , "X-Amzn-Trace-Id" : "Root=1-6124521e-0844c17c161a90e06ad543c0" }, "json" : null, "origin" : "116.25.236.109" , "url" : "http://httpbin.org/post" } |
该网页通过POST传递了表单数据,在form内输出一个字典格式,如果传递字典有多个元素则form下也对应多个元素。
可以看到,我们传递的参数data中的字典键值对"word":"hello"出现在了form字段中,这表明了表单提交的方式,以POST方式传输数据。
timeout参数
timeout参数用于设置超时时间,单位为秒,意思是如果请求时间超过了设置的时间,还没有得到响应,就会抛出异常,在实际爬虫中,我们要对许多URL发起请求,中途肯定会出现爬取异常的URL,短时间无法获得响应,我们需要识别出这种异常,就需要用到timeout参数。
如果不指定timeout参数,会使用全局默认时间。它支持HTTP、HTTPS、FTP请求。
通过代码示例理解:
1 2 3 4 | # 调用urllib库中的request模块 import urllib.request response = urllib.request.urlopen( "http://httpbin.org/get" ,timeout = 0.01 ) print (response.read().decode( 'utf-8' )) |
这里我们把timeout参数设成0.01秒,网页反应时间是没这么快的,所以一定会报错,我们来看一下报错的信息:
1 | urllib.error.URLError: <urlopen error timed out> |
显示异常属于urllib.error模块,错误原因是超时。
在实际爬虫中,我们可以用过设置这个超时时间来控制一个网页在长时间未响应时,就跳过它的抓取。这可以利用try except 语句来实现。
代码示例:
1 2 3 4 5 6 7 8 9 | import socket import urllib.request try : response = urllib.request.urlopen( "http://httpbin.org/get" ,timeout = 0.01 ) print (response.read().decode( 'utf-8' )) except urllib.error.URLError as e : if isinstance (e.reason,socket.timeout): print ( "time out !" ) |
在代码中加入try except 语句后,如果响应超时,便会跳过抓取,我们捕获了URLError异常后,接着判断异常是socket.timeout类型(意思就是超时异常),于是打印出了time out !
输出结果如下:
1 | time out ! |
其他参数(少用)
context参数:它必须是ssl.SSLContext类型,用来指定SSL设置,实现SSL加密传输。
cafile、capath:分别指定CA证书和它的路径,用于实现可信任的CA证书的HTTP请求。
cadefault:已经弃用,默认False。
2,Request
上文已经讲解了如何利用urlopen()方法实现最基本的请求发起。但这几个简单的参数并不足以构建一个完整的请求(过于简单的请求会被浏览器识别为爬虫而拒绝响应)。如果请求中需要加入Headers等信息,就需要用到Requset类来构建。
同样通过实例展示Request用法:
1 2 3 4 5 6 | # 展示Request import urllib.request request = urllib.request.Request( 'http://httpbin.org/get' ) response = urllib.request.urlopen(request) print (response.read().decode( 'utf-8' )) |
我们依然是用urlopen()方法来发送请求,只是这次的参数不在是url,而是一个Request类型对象。通过构造这个数据类型,我们可以将请求独立为一个对象,并且更灵活的配置参数。
Reque的函数原型如下:
1 | class urllib.request.Request(url,data = None ,headers = { },origin_req_host = None ,unverifiable = False ,method = None ) |
下面来看它的具体参数:
url:用于请求的URL,这是毕传参数,其他都是可选参数
data:必须是bytes类型,如果它是字典,可以先用urllib.parse模块里的urlencode方法编码(用于POST请求)
headers:是一个字典,它就是请求头,我们可以在构造请求时,通过headers参数直接构造,也可以通过调用请求示例的add_header()方法添加
origin_req_host:指的是请求方的host名称或IP地址。
unverifiable:表示这个请求是否无法验证,默认为False,意思是说用户没有足够权限来选择接收这个请求的结果。比如我们请求一个HTML文档中的图片,但是我们没有自助抓取图象的权限,这是unverifiable的值就是True。
method:是一个字符串,用来指示请求使用方法,如GET、POST、PUT等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # Request请求多个参数 import urllib.request,urllib.parse url = "http://httpbin.org/post" headers = { "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3314.0 Safari/537.36 SE 2.X MetaSr 1.0" , "Host" : "httpbin.org" } dict = { 'name' : 'Germey' } data = bytes(urllib.parse.urlencode( dict ),encoding = 'utf-8' ) req = urllib.request.Request(url = url,data = data,headers = headers,method = 'POST' ) response = urllib.request.urlopen(req) print (response.read().decode( 'utf-8' )) |
在这个示例中,我们通过4个参数构造了一个请求,我们加入了url、data、headers、method,其中最重要的是把头信息(包含User-Agent和Host)写入了 Request,让请求变得更完整。
运行结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | { "args" : {}, "data" : "", "files" : {}, "form" : { "name" : "Germey" }, "headers" : { "Accept-Encoding" : "identity" , "Content-Length" : "11" , "Content-Type" : "application/x-www-form-urlencoded" , "Host" : "httpbin.org" , "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3314.0 Safari/537.36 SE 2.X MetaSr 1.0" , "X-Amzn-Trace-Id" : "Root=1-61246460-1f4d152f11557e9f6670f33a" }, "json" : null, "origin" : "116.25.236.109" , "url" : "http://httpbin.org/post" } |
可以看到,我们成功通过新建的参数,设置了data、headers 和 method。
另外之前提到的headers也可以用add_header()方法添加,示例代码如下:
1 2 | req = urllib.request.Request(url = url,data = data,method = 'POST' ) req.add_header( 'User-Agent' , "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3314.0 Safari/537.36 SE 2.X MetaSr 1.0" ) |
传递参数格式为key value格式
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
2017-08-24 PPTP客户端无法拨号778故障解决