使用urllib
urllib库是Python内置的请求库,可以用来模拟发送请求,它包含四个模块:
- request:它是最基本的HTTP请求模块,可以用来模拟发送请求
- error:异常处理模块,保证程序不会意外终止
- parse:一个工具模块,提供了许多Url的处理方法
- robotparser:主要用来识别网站的robots.txt文件,用的较少
urlopen()
urllib.request模块提供了最基本的HTTP请求的方法,同时它还带有处理授权验证、重定向、浏览器Cookies等功能。
使用urlopen()方法,API如下:
1 | def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, |
1 | import urllib.request |
我们使用type方法查看响应的类型
1 | import urllib.request |
<class 'http.client.HTTPResponse'>
可以看出返回的是一个HTTPResponse
,我们可以这些方法和属性查看一些信息。
1 | import urllib.request |
200
[('Server', 'nginx'), ('Content-Type', 'text/html; charset=utf-8'), ('X-Frame-Options', 'DENY'), ('Via', '1.1 vegur'), ('Via', '1.1 varnish'), ('Content-Length', '48510'), ('Accept-Ranges', 'bytes'), ('Date', 'Sat, 10 Aug 2019 02:56:05 GMT'), ('Via', '1.1 varnish'), ('Age', '3403'), ('Connection', 'close'), ('X-Served-By', 'cache-iad2138-IAD, cache-lax8627-LAX'), ('X-Cache', 'HIT, HIT'), ('X-Cache-Hits', '2, 360'), ('X-Timer', 'S1565405765.160733,VS0,VE0'), ('Vary', 'Cookie'), ('Strict-Transport-Security', 'max-age=63072000; includeSubDomains')]
urlopen的data参数:
data参数是可选的,如果添加该参数,若它是字节流编码格式的内容,则需要通过bytes()
方法转换,请求方式变为POST方式。
1 | import urllib.parse |
b'{\n "args": {}, \n "data": "", \n "files": {}, \n "form": {\n "word": "hello"\n }, \n "headers": {\n "Accept-Encoding": "identity", \n "Content-Length": "10", \n "Content-Type": "application/x-www-form-urlencoded", \n "Host": "httpbin.org", \n "User-Agent": "Python-urllib/3.7"\n }, \n "json": null, \n "origin": "112.26.77.217, 112.26.77.217", \n "url": "https://httpbin.org/post"\n}\n'
可以看到上述的form
表单中的word
:hello
,即我们请求的数据。
urllib的time参数:用于设置超时时间
1 | import urllib.request |
最下面一行的:URLError
,我们设置的超时时间为0.1s,超过时间后,抛出该异常。
因此可以通过设置这个超时时间来控制一个网页如果长时间未响应,就跳过它的抓取。我们通过try except语句来实现。
1 | import socket |
Time out
Request
我们利用urlopen()
方法可以实现最基本请求的发起,但这几个参数并不足以构建一个完整的请求。如果在请求中加入Headers
等信息,我们可以利用更强大的Request类构建。
1 | import urllib.request |
查看一下Request
的构造方法:
1 | def __init__(self, url, data=None, headers={}, |
同样第二个参数data
如果要传,则必须要tytes(字节流)类型的,如果是字典,可以先用urllib.parse
模块里的urlencode()
编码。
第三个参数headers
是一个字典,它就是请求头,最常用的方法就是修改User-Agent
来伪装浏览器。比如伪装Chrome,设置为:
1 | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36 |
第四个参数origin_req_host
为请求方的host名称或者ip地址,第六个为请求方法,比如GET,POST等
下面我们传入多个参数来看一下:
1 | from urllib import request,parse |
b'{\n "args": {}, \n "data": "", \n "files": {}, \n "form": {\n "name": "Jack"\n }, \n "headers": {\n "Accept-Encoding": "identity", \n "Content-Length": "9", \n "Content-Type": "application/x-www-form-urlencoded", \n "Host": "httpbin.org", \n "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"\n }, \n "json": null, \n "origin": "112.26.77.217, 112.26.77.217", \n "url": "https://httpbin.org/post"\n}\n'
观察结果,我们已经成功设置了headers,data数据了。
headers也可以使用add_header()方法来添加,更加方便。如:
1 | from urllib import request,parse |
b'{\n "args": {}, \n "data": "", \n "files": {}, \n "form": {\n "name": "Jack"\n }, \n "headers": {\n "Accept-Encoding": "identity", \n "Content-Length": "9", \n "Content-Type": "application/x-www-form-urlencoded", \n "Host": "httpbin.org", \n "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"\n }, \n "json": null, \n "origin": "112.26.77.217, 112.26.77.217", \n "url": "https://httpbin.org/post"\n}\n'
异常处理
我们了解了请求的发送过程,但是在网络不好的情况下出现了异常怎么办?这时候如果不处理异常,程序很可能因为报错而终止,所以异常的处理是非常必要的。
urllib的eroor模块定义了由request模块产生的异常。
URLError
ERLERROR类来自于urllib库中的error模块,它有一个属性reason
,即返回错误的原因。
1 | from urllib import error,request |
Not Found
程序没有直接报错,而是输出了如上内容,可以避免程序的异常终止。
HTTPError
它是URLError的子类,专门处理HTTP的请求错误,它有三个属性:
- code:返回HTTP状态码,比如404表示页面不存在,500表示服务器内部错误
- reason:同父类一样,用于返回错误的原因
- headers:返回请求头
因为URLError是HTTPError的父类,所以可以先选择捕获子类的异常,再去捕获父类的错误。
1 | from urllib import error,request |
404
Not Found
Server: nginx
Date: Sat, 10 Aug 2019 06:28:46 GMT
Content-Type: text/html
Content-Length: 146
Connection: close
有时候reason属性返回的不一定是字符串,也可能是一个对象。
1 | import socket |
<class 'socket.timeout'>
TIME OUT