2.urllib库的使用

urllib库的使用

urllib库是Python内置的HTTP请求库,它包含了4个模块:

  request:最基本的HTTP请求模块,用来模拟发送请求

  error:异常处理模块。出现请求错误后,我们可以捕获异常,然后进行下一步的操作。

  parse:工具模块。提供了很多URL处理方法。

  robotparse:主要用来识网站的robots.text文件,用的比较少

1.发送请求

  urllib的request模块可以帮助我们方便的发送请求并得到响应。下面我们来看一下用法:

  1.urlopen()

   urlopen()参数有url、data、timeout、context、cafile、capath、cadefault,我们只详细了解三个,他们比较重要:

      url:要请求的网址

      下面我们以Python官网为例,我们来把它抓取下来:

      

import urllib.request
response
= urllib.request.urlopen("https://www.python.org/")
print(response)

 我们充满期待的想要看到网页内容,却发现是这样:

显而易见,我们得到的结果是一个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。

    2.Request

    我们可以利用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"))

2.处理异常

我们已经了解了怎样发送请求,但是请求不是每次都能得到响应,可能会出现异常,不去处理它程序可能因此停掉,所以很有必要去处理异常。

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类的对象

学了处理异常后,通过捕获异常并处理,可以使我们的程序更加的稳健。

3.解析链接

前面提到了urllib库里的parse模块,他提供了很多处理链接的方法,实现了URL各部分的抽取、合并、以及链接转化。

  1.urlparse()  

    该方法可以实现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")

  2.urlunparse()

   该方法接受一个可迭代对象,长度必须是6,对可迭代对象进行遍历,构造URL

  

from urllib.parse import urlunparse

data = ["http","www.baidu.com","index.html","user","a=6","comment"]
print(urlunparse(data))

  3.urlsplit()

  该方法与urlparse()方法类似,只不过它不再单独解析params,params合并到path中,返回的也是个元组,长度为5

  

from urllib.parse import urlsplit

result = urlsplit("http://www.baidu.com/index.html;user?a=6#comment")
print(result)

 

  4.urlunsplit()

  参照urlunparse(),但是长度为5

  5.urljoin()

   有两个参数,第一个为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")

  6.urlencode() 

   在构造GET请求的参数时,可以将字典类型的数据转化为GET请求参数

  

from urllib.parse import urlencode

params = {
    "name":"heesch",
    "age":18
}
base_url = "http://baidu.com?"
url = base_url + urlencode(params)
print(url)

  7.parse_qs()

   与urlencode()相反,将GET请求的参数化为字典的形式

  

from urllib.parse import parse_qs
query = "name=heesch&age=18"
print(parse_qs(query))

  8.parse_qsl()

  他是将GET请求的参数转化为元组组成的列表

  

from urllib.parse import parse_qsl
query = "name=heesch&age=18"
print(parse_qsl(query))

  9.quote()

   将内容转化为URL编码的格式,URL中带有中文参数时,有时可能会导致乱码的问题。

  

from urllib.parse import quote

keyword = "壁纸"
url = "http://www.baidu.com" + quote(keyword)
print(url)

  10.unquote()

   与quote相反,将URL进行解码

  

from urllib.parse import unquote

url = "http://www.baidu.com%E5%A3%81%E7%BA%B8"
print(unquote(url))

 4.分析Robots协议

  1.Robots协议

    Robots协议也称作爬虫协议、机器人协议,用来告诉爬虫和搜索引擎哪些页面可以爬取,哪些不可以抓取。通常是一个叫做robots.txt的文本文件,放在网站的根目录下

    当搜索爬虫访问一个站点是,首先会检查这个站点的根目录下是否存在robots.txt文件,如果存在,搜索爬虫则会根据其中定义的爬取范围来爬取。如果没有,则搜索爬虫会访问所有可直接访问的站点。

   下面看一个robots.text样例:

User-agent:*
Disallow:/
Allow:/public/

   User-agent:设置爬虫的名称,* 表示该协议对任何爬虫都有效

      Dislalow:制定了不允许抓取的目录,若为/ 表示不允许抓取任何页面

    Allow:和Disallow一起出现,限制访问路径

  2.常见爬虫

  

  3.robotparse

   了解完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模块后,我们可以方便的判断哪些一面可以抓取,哪些页面不能抓取。

  

posted @ 2019-06-17 17:26  杨鸿儒  阅读(386)  评论(0编辑  收藏  举报