urllib的使用
urllib是python内置的HTTP请求库,包含如下四个模块:
request:它是最基本HTTP请求模块,可以用来模拟发送请求,就像在浏览器里输入网址然后按回车一样,只需要传入URL以及额外的参数,就可以模拟实现这个过程了。
error:异常处理模块,如果出现请求错误,可以捕获到这些异常,然后进行重试或其他操作以保证程序不会意外终止。
parse:一个工具模块,提供了许多URL处理方法,比如拆分、解析、合并等。
robotparse:主要用来识别网站的robots.txt文件,然后判断哪些往回走哪可以爬,哪些网站不可以爬。
1.request模块
urllib.request 模块提供了最基本的构造HTTP请求的方法,利用它可以模拟浏览器发送请求,同时还有处理授权验证、重定向、Cookies以及其他内容。
# 导入模块
from urllib import request
# 调用urlopen()方法发送请求
response = request.urlopen('https://www.python.org')
print(response.read().decode('utf-8'))
通过简单的两行代码就可以将python官网的源代码抓取下来了,上面得到的response是一个HTTPResponse 类型的对象,通过该对象调用read()、readinto()、getheader(name)、getheaders()、fileno()等方法,以及msg、version、status、reason、debuglevel、closed等属性。下面来详细分析urlopen()函数的API:
urllib.request.urlopen(url,data=None,[timeout],cafile=None,capath=None,cadefault=False,content=None)
除了url参数以外,其余的参数都是可选的。
data参数:如果要添加该参数,并且如果它是字节流编码格式的内容(bytes类型),则需要通过bytes()方法转换。另外,如果传递了这个参数,则它的请求方式不再是GET,而是POST请求。
import ulllib.parse import urllib.request data = bytes(urllib.parse.urlencode({'name':'jonas'}),encoding='utf-8') response = urllib.request.urlopen('http://httpbin.org/post',data=data) print(response.read())
这里传递了一个参数name,值是jonas。它需要被转码成bytes类型,其中转转字节流采用了bytes()方法,该方法的第一个参数需要时str类型,需要用urllib.parse模块的urlencode()方法来将dict转化为str类型;第二个参数指定编码方式。
timeout参数:用于设置超时时间,单位为秒。如果超出设置的时间还没有得到响应,就会抛出URLError异常。
import urllib.request response = urllib.request.urlopen('http://httpbin.org/get',timeout=1)
如果超过了1秒没有得到响应,程序就会抛出URLError异常,该异常属于urllib.error模块,错误原因是超时。
上面通过urlopen()方法可以实现最基本的请求发起,但这几个简答的参数并不足以构建一个完整的请求,如果请求中需要加入Headers信息,就可以利用更强大的Request类来构建。
import urllib.request request = urllib.request.Request('https://python.org') response = urllib.request.urlopen(request) print(response.read().decode('utf-8'))
可以发现,上面依然是用urlopen()方法来发送请求,只不过该方法的参数不再是url,而是一个Request对象。
urllib.request.Request(url,data=None,headers={},origin_req_host=None,unverifiable=False,method=None)
第一个参数 url 是必须必须的,用于请求的URL。
第二个参数data如果要传,则必须传bytes()类型,跟上面的一样,如果是字典,可以通过urllib.parse.urlencode()来编码。
第三个参数headers是一个字典,它就是请求头,可以在构造请求时通过headers参数直接构造,也可以通过调用请求实例的add_header()方法添加。请求头最常用的用法就是通过修改User-Agent来伪装浏览器,默认的User-Agent是Python-urllib,如果不修改该值,则很有可能会被识别为爬虫。
第四个参数origin_req_host指的是请求方的host名称或者IP地址。
第五个参数unverifiable表示这个请求是否是无法验证的,默认是False,意思就是说用户没有足够权限来选择接受这个请求的结果。
第六个参数method是一个字符串,用来指示请求使用的方法。
下面传入多个参数来构建你请求:
from urllib import request,parse url = 'http://httpbin.org/post' headers = { 'User-Agent' : 'Mozilla/4.0(compatible; MSIE 5.5;Windows NT)', 'Host':'httpbin.org' } my_dict = {'name':'jonas'} data = bytes(parse.urlencode(my_dict),encoding='utf-8') req = request.Request(url=url,data=data,headers=headers,method='POST') print(response.read().decode('utf-8'))
在上面构建了请求,成功的伪装成浏览器去发请求拿数据,但是反爬虫的技术不仅仅是检查请求头的User-Agent,还有很多方式,比如,服务器会检测某个IP在单位时间内的请求次数,如果超过了某个值,那么服务器会拒绝服务,返回一些错误信息。所以在时候就需要用到代理技术了(详细在后面介绍)。
在urllib.request模块里有一个BaseHandler类,它是所有其他Handler的父类,它提供了最基本的方法,除此以外还有很多基于这个类的子类:
HTTPDefaultErrorHandler:用于处理HTTP响应错误,错误会怕抛出HTTPError异常。
HTTPRedirectHandler:用于处理重定向
HTTPCookieProcessor:用于处理Cookies
ProxyHandler:用于设置代理,默认代理为空
HTTPPasswordMgr:用于管理密码,它维护了用户名和密码的表
HTTPBasicAuthHandler:用于管理认证,如果一个链接打开时需要认证,那么可以使用它来解决认证问题
除了这些以外,还有一个比较重要的类就是OpernerDirector,称为Opener,上面使用的urlopen()方法的返回值其实就是一个Opener对象,有些网站在打开时就会弹出提示框,直接提示你输入用户名和密码,验证成功后才能查看页面,如果需要请求这样的页面,就可以通过HTTPBasicAuthHandler来完成了。
from urllib.request import HTTPPasswordMgrWithDefaultRealm,HTTPBasicAuthHandler,build_opener from urllib.error import URLError username = 'jonas' password = 123456 url = 'localhost:3000' p = HTTPPasswordMgrWithDefaultRealm() p.add_password(None,url,username,password) auth_handler = HTTPBasicAuthHandler(p) opener = build_opener(auth_handler) try: result = opener.open(url) html = result.read().decode('utf-8') except URLError as e: print(e.reason)
这里首先实例化 HTTPBasicAuthHandler 对象,其参数是 HTTPPasswordMgrWithDefaultRealm 对象,它利用add_password()添加进去用户名和密码,这样就建立了一个处理验证的Handler,接下来就可以利用这个Handler并使用build_opener()方法构建一个Opener,这个Opener在发送请求时就相当于验证成功了,然后利用Opener的open()方法打开链接,就完成验证了,这里获取到的结果就是验证后的页面源码内容。
除了上面的验证以外,在爬虫的时候免不了使用的就是代理技术。
from urllib.error import URLError from urllib.request import ProxyHandler,build_opener proxy_handler = ProxyHandler({ 'http':'http://127.0.0.1:5000', 'https':'https://127.0.0.1:5000' }) opener = build_opener(proxy_handler) try: response = opener.open('https://www.baidu.com') print(response.read().decode('utf-8')) except URLError as e: print(e.reason)
这里使用了ProxyHandler,其参数是一个字典,键名是协议类型,然后利用这个Handler及build_opener()方法构造一个Opener
2.error模块
2.1 URLError
URLError类来自urllib库的error模块,它继承自OSError,是error异常模块的基类,由request模块产生的异常都可以通过捕获这个类类处理。它有一个reason属性,用于返回错误的原因。
from urllib import request,error try: response = request.urlopen('https://www.baidu123.com') except error.URLError as e: print(e.reason)
上面请求了一个不存在的URL,就可以捕获到这个异常。
2.2 HTTPError
它是URLError的子类,专门用于处理HTTP请求错误,它有如下3个属性:
code:返回HTTP状态码
reason:返回错误的原因
headers:返回请求头
3.parse模块
parse模块定义了处理URL的标准接口,例如实现URL各部分的抽取、合并以及连接转换。
3.1 urlparse()
该方法可以实现URL的识别和分段
from urllib.parse import urlparse result = urlparse('http://www.baidu.com/index.html;user?id=5#comment') print(type(result),result)
该方法返回的结果是一个ParseResult对象,它包含6个部分,分别是scheme代表协议、netloc代表域名、path代表访问路径、params代表参数、query代表查询条件和fragment代表锚点
一个标准的连接格式:scheme://netloc/path;params?query#fragment
除了这种最基本的解析方式,该方法还卡伊传其他参数:
urllib.parse.urlparse(urlstring,scheme='',allow_fragments=True)
urlstring:待解析的URL,必须参数
scheme:默认的协议
allow_fragments:是否忽略fragment
3.2 urlunparse()
该方法可以接受一个可迭代对象,但是长度必须是6,如果不足或者过多都会报错。
from urllib import parse data = ['http','www.baidu.com','index.html','user','a=6','comment'] print(parse.urlunparse(data))
这样就实现了URL的构造了
3.3 urlsplit()
该方法和urlparse()非常相似,不同的就是它不再单独解析params这一部分。
3.3 urlunsplit()
与urlunparse()类似,它也是将连接各部分组合成完整连接的方法,传入的参数也是一个可迭代对象,唯一有区别的就是长度必须是5
3.4 urlencode()
该方法常用于构造GET请求的参数,具体用法在上面已经有示例。
3.5 parse_qs()
如果需要处理一串GET请求参数,可以利用该方法进行反序列化。
3.6 quote()
该方法可以将内容转化为URL编码的格式。URL中带有中文参数时,有时可能会导致乱码问题,此时用这个方法可以将中文字符转化为URL编码
from urllib.parse import quote keyword = '壁纸' url = 'https://www.baidu.com/s?wd=' + quote(keyword) print(url)
对应的,还有unquote()方法,用于URL解码
from urllib.parse import unquote url = 'https://www.baidu.com/s?wd=%E5%A3%81%E7%BA%BB' print(unquote(url))