zoukankan      html  css  js  c++  java
  • 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="
    ")

    同样的网页,我们通过捕获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="
    ")
    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="
    ")
        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="
    ")

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

      

  • 相关阅读:
    3-为什么很多 对 1e9+7(100000007)取模
    6-关于#include<bits/stdc++.h>
    7-n!的位数(斯特灵公式)
    5-math中函数汇总
    6-找数的倍数
    6-Collision-hdu5114(小球碰撞)
    5-青蛙的约会(ex_gcd)
    4-圆数Round Numbers(数位dp)
    4-memset函数总结
    一种Furture模式处理请求中循环独立的任务的方法
  • 原文地址:https://www.cnblogs.com/yhrfighting123/p/11018141.html
Copyright © 2011-2022 走看看