urllib库是Python内置的HTTP请求库,它包含了4个模块:php
request:最基本的HTTP请求模块,用来模拟发送请求html
error:异常处理模块。出现请求错误后,咱们能够捕获异常,而后进行下一步的操做。python
parse:工具模块。提供了不少URL处理方法。浏览器
robotparse:主要用来识网站的robots.text文件,用的比较少服务器
urllib的request模块能够帮助咱们方便的发送请求并获得响应。下面咱们来看一下用法:socket
urlopen()参数有url、data、timeout、context、cafile、capath、cadefault,咱们只详细了解三个,他们比较重要:工具
url:要请求的网址post
下面咱们以Python官网为例,咱们来把它抓取下来:测试
import urllib.request
response = urllib.request.urlopen("https://www.python.org/")
print(response)
咱们充满期待的想要看到网页内容,却发现是这样:fetch
显而易见,咱们获得的结果是一个HTTPResponse类的对象,咱们想要获得它的内容得使用它的read()方法,另外read()获得的是bytes类型的数据,咱们将它解码,方便观看
import urllib.request
response = urllib.request.urlopen("https://www.python.org/") print(response.read().decode("UTF-8"))
此次获得的结果,如咱们所愿,不就是网页的源代码嘛:
下来咱们说一下这个HTTPResponse类型的对象,他有不少属性和方法,
https://www.cnblogs.com/kissdodog/archive/2013/02/06/2906677.html 这个博客中有详细说明,我就很少提了^_^
data:data参数是可选的,但必须给bytes类型,一旦设置data参数,请求的方式就会变成POST,若是不设置则默认为GET
咱们经过代码看一下:
import urllib.parse import urllib.request
data = bytes(urllib.parse.urlencode({"word":"hello"}),encoding="UTF-8") #咱们借助urllib库的parse中的urlencode方法将字典转化为字符串 response = urllib.request.urlopen("http://httpbin.org/post",data=data) #这个网站用来作HTTP请求测试,咱们用来进行POST请示测试 print(response.read().decode("UTF-8"))
咱们来看一下结果:
咱们能够很清楚的看到咱们传进的data参数出如今了form里面,咱们知道只有POST请求才会把数据封装到form表单中。data设置的参数会被封装到form表单中,封装到请求头中传递给服务器。
timeout:设置超时时间,单位为秒,当超过这个时间,请求没有获得响应,就会抛出异常。
import urllib.request
response = urllib.request.urlopen("http://httpbin.org/get",timeout=0.1) #咱们将这个时间设置的短一点,请求不可能这么快获得响应,这样就会抛出异常 print(response.read())
果真不出所料:
可是,咱们都知道Python有try except异常处理机制,咱们在这里能够用到
import socket import urllib.request import urllib.error
try: response = urllib.request.urlopen("http://httpbin.org/get",timeout=0.1) except urllib.error.URLError as e: if isinstance(e.reason,socket.timeout): print("超时了")
因此咱们经过设置timeout,一旦超时进行异常处理,这样能很好的提升爬取速率
context:用来指定SSL设置。
cafile:CA证书。
capath:CA证书的路径。
cadefault:如今已弃用,默认值为false。
咱们能够利用urlopen()方法来发送请求,可是urlopen()中几个参数并不能构成一个完整的请求。咱们须要借助Request类构建对象,对象中能够携带Headers等信息,从而实现完整的请求。
咱们先来看一下Request的用法:
import urllib.request
request = urllib.request.Request("https://python.org") print(type(request)) response = urllib.request.urlopen(request) #咱们仍然使用urlopen()来发送请求,可是方法的参数不是URL,而是一个request对象,咱们将参数独立成对象,能够灵活的进行参数配置。 print(response.read().decode("UTF-8"))
下面咱们看一下Request对象的参数有哪些:
url:用于请求的URL,必传参数,其余都是选传。
data:必须传bytes类型的。若是是字典,咱们能够借助urllib.parse模块中的urlencode()进行编码。
headers:请求头,是一个字典。咱们能够直接构造,也能够经过调用add_header()方法添加
ps:请求头中最常改的就是User-Agent,默认的User-Agent为Python-urllib,咱们能够经过修改它来伪造浏览器。好比咱们要假装为火狐浏览器,能够设置为:
Mozilla/4.0 (compatible; MSIE 5.5; Window NT)
origin_req_host:请求放的host名称或IP地址
unverifiable:用户用没有权限来选择接收请求的结果。默认为False,若是没有权限贼为True,不然为False。
method:请求使用的方法,如GET、POST和PUT
话很少说,咱们来看一下:
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" } dict = { "name":"Germey" } data = bytes(parse.urlencode(dict),encoding="UTF-8") req = request.Request(url=url,data=data,headers=headers,method="POST") response = request.urlopen(req) print(response.read().decode("UTF-8"))
明显的看到,咱们成功的设置了headers,method,data。
headers信息咱们还可使用add_headers来添加
from urllib import request,parse url = "http://httpbin.org/post" dict = { "name":"Germey" } data = bytes(parse.urlencode(dict),encoding="UTF-8") req = request.Request(url=url,data=data,method="POST") req.add_header("User-Agent","Mozilla/4.0 (compatible; MSIE 5.5; Windows NT") #add_headers(key,value) response = request.urlopen(req) print(response.read().decode("UTF-8"))
咱们已经了解了怎样发送请求,可是请求不是每次都能获得响应,可能会出现异常,不去处理它程序可能所以停掉,因此颇有必要去处理异常。
urllib的error模块定义了由request模块产生的异常。若是出现问题,request模块会抛出error模块中定义的异常。
1.URLError
URLError类来自error模块,是error异常模块的基类,由request模块产生的异常均可以经过捕获这个类来处理,它的属性reason能够返回错误缘由。
from urllib import request,error try: response = request.urlopen("https://cuiqingcai.com/index.htm") #咱们打开的页面不存在 except error.URLError as e: print(e.reason)
咱们打开的页面不存在,按理说会报错,可是咱们将异常捕获,并输出异常得缘由,经过这样的操做,咱们能够避免程序异常终止,并且异常获得了很好的处理。
2.HTTPError
他是URLError的子类,专门用来处理HTTP请求错误。他有三个属性:
code:返回HTTP状态码,好比404表示页面不存在,500表示服务器内部出错
reason:返回错误的缘由
headers:返回请求头
from urllib import request,error try: response = request.urlopen("http://cuiqingcai.com/index.htm") except error.HTTPError as e: print(e.reason,e.code,e.headers,sep="\n")
一样的网页,咱们经过捕获HTTPError来得到reson,code和headers属性。由于URLError是HTTPError的子类,因此咱们能够选择捕获子类的错误,若是没有再去捕获父类错误,代码以下:
from urllib import request,error try: response = request.urlopen("http://cuiqingcai.com/index.htm") except error.HTTPError as e: print(e.reason,e.code,e.headers,sep="\n") except error.URLError as e: print(e.reason) else: print("Request Successfully")
可是有时候reason属性返回的不是字符串,而是一个对象:
from urllib import request,error try: response = request.urlopen("http://cuiqingcai.com/index.htm",timeout=0.01) except error.HTTPError as e: print(e.reason,e.code,e.headers,sep="\n") print(type(e.reason)) except error.URLError as e: print(e.reason) print(type(e.reason)) else: print("Request Successfully")
咱们能够很清楚的看到e.reson是一个socket.timeout对象,咱们简单的记住就好了,请求超时后,返回的错误为timed out,是socket.timeout类的对象
学了处理异常后,经过捕获异常并处理,可使咱们的程序更加的稳健。
前面提到了urllib库里的parse模块,他提供了不少处理连接的方法,实现了URL各部分的抽取、合并、以及连接转化。
该方法能够实现URL的识别和分段,共有三个参数,返回的结果是一个元组咱们能够经过属性或索引拿到相应的值
urlstring:待解析的URL,必选项
scheme:默认的协议,若是连接没有带协议会解析出默认协议,不然解析出连接带的协议
allow_fragments:是否忽略fragment,若是为false,fragment部分就会被忽略,会被解析成path、params或query的一部分
urlparse()会把URL解析成6个部分,标准的连接格式以下:
scheme://netloc/path;params?query#fragment
协议://域名/访问路径;参数?查询条件#锚点
查询条件通常用做GET类型的URL,锚点用于定位页面内部的下拉位置
from urllib.parse import urlparse result = urlparse("http://www.baidu.com/index.html;user?id=5#comment") print(type(result),result,sep="\n")
该方法接受一个可迭代对象,长度必须是6,对可迭代对象进行遍历,构造URL
from urllib.parse import urlunparse data = ["http","www.baidu.com","index.html","user","a=6","comment"] print(urlunparse(data))
该方法与urlparse()方法相似,只不过它再也不单独解析params,params合并到path中,返回的也是个元组,长度为5
from urllib.parse import urlsplit result = urlsplit("http://www.baidu.com/index.html;user?a=6#comment") print(result)
参照urlunparse(),可是长度为5
有两个参数,第一个为base_url(基础连接),第二个为new_url(新的连接),该方法会解析base_url的scheme,netloc和path,若是新的连接存在就是用新的连接的部分,若是不存在就补充。
from urllib.parse import urljoin print(urljoin("http://www.baidu.com","FAQ.html")) print(urljoin("http://www.baidu.com","https://cuiqingcai.com/FAQ.html")) print(urljoin("http://www.baidu.com/about.html","https://cuiqingcai.com/FAQ.html")) print(urljoin("http://www.baidu.com/about.html","http://cuiqingcai.com/FAQ.html?question=2")) print(urljoin("http://www.baidu.com?wd=abc","https://cuiqingcai.com/index.php")) print("http://www.baidu.com","?category=2#comment") print("www.baidu.com","?category=2#comment") print("www.baidu.com#comment","?category=2")
在构造GET请求的参数时,能够将字典类型的数据转化为GET请求参数
from urllib.parse import urlencode params = { "name":"heesch", "age":18 } base_url = "http://baidu.com?" url = base_url + urlencode(params) print(url)
与urlencode()相反,将GET请求的参数化为字典的形式
from urllib.parse import parse_qs query = "name=heesch&age=18" print(parse_qs(query))
他是将GET请求的参数转化为元组组成的列表
from urllib.parse import parse_qsl query = "name=heesch&age=18" print(parse_qsl(query))
将内容转化为URL编码的格式,URL中带有中文参数时,有时可能会致使乱码的问题。
from urllib.parse import quote keyword = "壁纸" url = "http://www.baidu.com" + quote(keyword) print(url)
与quote相反,将URL进行解码
from urllib.parse import unquote url = "http://www.baidu.com%E5%A3%81%E7%BA%B8" print(unquote(url))
Robots协议也称做爬虫协议、机器人协议,用来告诉爬虫和搜索引擎哪些页面能够爬取,哪些不能够抓取。一般是一个叫作robots.txt的文本文件,放在网站的根目录下
当搜索爬虫访问一个站点是,首先会检查这个站点的根目录下是否存在robots.txt文件,若是存在,搜索爬虫则会根据其中定义的爬取范围来爬取。若是没有,则搜索爬虫会访问全部可直接访问的站点。
下面看一个robots.text样例:
User-agent:* Disallow:/ Allow:/public/
User-agent:设置爬虫的名称,* 表示该协议对任何爬虫都有效
Dislalow:制定了不容许抓取的目录,若为/ 表示不容许抓取任何页面
Allow:和Disallow一块儿出现,限制访问路径
了解完Robots协议以后,咱们可使用robotparser模块来解析robot.txt。该模块提供一个类RobotFileParse,它能够根据robots.txt文件来判断一个爬虫是否有权限来爬取这个页面
声明:urllib.robotparser.RobotFileParser(url="")
这个类经常使用的方法:
set_url():设置robots.txt文件的连接。若是建立RobotFileParse对象时传入连接,就不须要用这个方法进行设置了
read():读取robots.text文件并分析,不会返回结果,可是执行了读取操做,若是不执行,下面的方法判断都为false
parse():用来解析robots.txt文件
can_fetch():有两个参数,第一个是User-agent,第二个是要抓取的URL,返回的结果为True或False,用来判断是否能抓取这个页面
mtime():返回上次抓取robot.txt的时间,对于长时间分析或抓取的搜索爬虫须要按期检查
modified():将当前时间设置为上次抓取和分析robots.txt的时间
咱们先用can_fetch()方法来判断网页是否能抓取:
from urllib.robotparser import RobotFileParser rp = RobotFileParser() rp.set_url("http://jianshu.com/robot.txt") rp.read() print(rp.can_fetch("*","http://www.jianshu.com/p/b67554025d7d")) print(rp.can_fetch("*","http://www.jianshu.com/search?q=python&page=1&type=collections"))
两个false,都不让抓取。
咱们再用parse()方法来判断网页是否能抓取:
from urllib.robotparser import RobotFileParser from urllib import request,parse url = "https://www.jianshu.com/robots.txt" headers = { "User-Agent":"Mozilla/4.0 (compatible; MSIE 5.5; Windows NT", } rp = RobotFileParser() req = request.Request(url=url,headers=headers,method="GET") rp.parse(request.urlopen(req).read().decode("UTF-8").split("\n")) print(rp.can_fetch("*","http://www.jianshu.com/p/b67554025d7d")) print(rp.can_fetch("*","http://www.jianshu.com/search?q=python&page=1&type=collections"))
咱们学完robotparser模块后,咱们能够方便的判断哪些一面能够抓取,哪些页面不能抓取。