zoukankan      html  css  js  c++  java
  • Python之urllib库的用法

      参考:https://zhuanlan.zhihu.com/p/146016738

      urllib库的作用

      爬虫的第一个步骤是获取网页,urllib库是用来实现这个功能:想服务器发送请求,得到服务器响应,获取网页的内容。

      Python的强大在于提供了功能齐全的类库,来帮助我们完成这个请求,通过调用urllib库,我们不需要了解请求的数据结构,HTTP,TCP,IP层网络传输同学,以及服务器应答原理等。

      我们只需要关心以下三点,然后通过几行调用urllib库的代码,就能够获得我们想要的网页内容。

      • 请求的URL是什么
      • 传递的参数是什么
      • 如何设置可选的请求头

       urllib库的构成

      在python2中,曾经有urllib和urllib2两个库来实现请求的发送,但目前通用的python3版本中,两个库的功能已经合并成一个库,统一为urllib,它是python内置函数,不需要额外安装即可使用。

      urllib的四个模块

      

      【1】requset:HTTP请求模块,可以用来模拟发送请求,只需要传入URL及额外参数,就可以模拟浏览器访问网页的过程。

      【2】error:异常处理模块,检测请求是否报错,捕捉异常错误,进行重试或其他操作,保证程序不会终止。

      【3】parse:工具模块,提供许多URL处理方法,如拆分、解析、合并等。

      【4】robotparser:识别网站的robots.txt文件,判断哪些网站可以爬,哪些网站不可以爬,使用频率较少。

      发送请求

      urlopen是request模块中的方法,用于抓取网络。

      官方文档:https://docs.python.org/3/library/urllib.request.html

      我们以代码示例,我们抓取百度的网页

    # 调用urllib库中的request模块
    import urllib.request
    # 发送请求获取百度网页的响应
    response = urllib.request.urlopen("http://www.baidu.com")
    # 打印响应内容
    # read()是把响应对象内容全部读取出来,读取出来为bytes码
    # decode('utf-8')把bytes码解码
    print(response.read().decode('utf-8'))
    

      返回的结果比较多,随便截取其中一部分,可以看出是百度的网页HTML源代码。

       我们只用几行代码,就完成了百度的抓取,并打印了网页的源代码,接下来,我们看一看我们获得的响应内容response到底是什么?利用type()方法来输出响应的类型。

    print(type(response))
    # <class 'http.client.HTTPResponse'>
    

      它是一个HTTPResponse类型的对象

      包含方法:read()、readinto()、getheader()、getheaders()、fileno()等

      包含属性:msg、version、status、reason、debuglevel、closed等属性。

      通过调用以上的方法和属性,就能返回我们所需的信息。

      比如一开始我们打印获取网页内容时,就用到了read()方法。

      调用status属性则可以得到返回结果的状态码,200代表强求成功,404代表网页未找到等。

      再看一个代码示例加深理解:

    # 打印响应状态码
    print(response.status,"
    ")
    # 200
    # 打印响应头信息
    print(response.getheaders(),"
    ")
    # [('Bdpagetype', '1'), ('Bdqid', '0x90ae03e0000443e3'), ('Cache-Control', 'private'), ('Content-Type', 'text/html;charset=utf-8'), ('Date', 'Tue, 24 Aug 2021 01:18:50 GMT'), ('Expires', 'Tue, 24 Aug 2021 01:18:50 GMT'), ('P3p', 'CP=" OTI DSP COR IVA OUR IND COM "'), ('P3p', 'CP=" OTI DSP COR IVA OUR IND COM "'), ('Server', 'BWS/1.1'), ('Set-Cookie', 'BAIDUID=162948399648CA18CD24B2476E039F6B:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com'), ('Set-Cookie', 'BIDUPSID=162948399648CA18CD24B2476E039F6B; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com'), ('Set-Cookie', 'PSTM=1629767930; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com'), ('Set-Cookie', 'BAIDUID=162948399648CA18CA2A9672599961C8:FG=1; max-age=31536000; expires=Wed, 24-Aug-22 01:18:50 GMT; domain=.baidu.com; path=/; version=1; comment=bd'), ('Set-Cookie', 'BDSVRTM=17; path=/'), ('Set-Cookie', 'BD_HOME=1; path=/'), ('Set-Cookie', 'H_PS_PSSID=34437_34441_31253_34004_34092_26350_34390; path=/; domain=.baidu.com'), ('Traceid', '1629767930035569306610425274448017114083'), ('Vary', 'Accept-Encoding'), ('Vary', 'Accept-Encoding'), ('X-Frame-Options', 'sameorigin'), ('X-Ua-Compatible', 'IE=Edge,chrome=1'), ('Connection', 'close'), ('Transfer-Encoding', 'chunked')]
    # 打印响应头的Server值
    print(response.getheader('Server'),"
    ")
    # BWS/1.1
    

      在打印的三行代码中

      第一行输出了响应的状态码(200代表正常)

      第二行输出了响应的头信息

      第三行通过调用getheader()方法并传递参数Server,获取了第二行响应头信息中的Server对应的值BWS/1.1。

      参数

      利用基本的urlopen()方法可以完成最基本的简单网页GET方法抓取。

      如果想达成更复杂一些的任务,需要给链接传递一些参数,该如何实现。

      urlopen()的函数原型如下:

    urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None) 
    

      除了第一个参数传递URL之外,我们还可以传递其他参数,比如data(附加数据),timeout(超时时间)等。

      data参数

      data用来指明往服务器请求中的额外参数信息,data默认是None,此时以GET方式发送请求;当用户给出data参数的时候,改为POST方式发送请求。

      data参数是可选的,如果需要添加该参数,需要使用bytes()方法将参数转化为二进制数据。

      还是通过代码理解

      首先了解模块urllib.parse.urlencode的用法

      urllib.parse.urlencode用于把一个字典转换成对应的str

      举例说明

    # urllib.parse.urlencode把字典转换成字符串str
    # 如果字典包含多个元素则之间使用&分隔
    import urllib.parse
    print(urllib.parse.urlencode({"word":"hello"}))
    # word=hello
    print(urllib.parse.urlencode({"word":"hello","word1":"hello1"}))
    # word=hello&word1=hello1
    

      下面代码演示POST方法

    import urllib.parse
    import urllib.request
    # urllib.parse.urlencode({'word':'hello'})把字典转换为字符串 "word=hello"
    # bytes("word=hello",encoding='utf-8')把字符串转换成二进制b"word=hello"
    data = bytes(urllib.parse.urlencode({'word':'hello'}),encoding='utf-8')
    # 以下方法和上面转换的结果一致
    # data = urllib.parse.urlencode({'word1':'hello'}).encode('utf-8')
    response = urllib.request.urlopen("http://httpbin.org/post",data=data)
    print(response.read().decode('utf-8'))
    

      首页这里请求的站点是,它是一个HTTP请求测试网站,记住它后面举例会经常用到它。

      这次我们要使用post方式请求,而不是get,因为post是需要携带表单信息的(类似登陆的用户名和密码)所以我们要在urlopen函数中传递data参数。

      前面讲了data参数必须是bytes型。

      bytes()这个方法第一个参数需要str(字符串类型),这里就需要用到urllib库的parse模块里的urlencode()方法来将参数字典转化为字符串。第二个参数是  指定编码格式,这里指定为utf-8。

      运行结果如下:

    {
      "args": {},
      "data": "",
      "files": {},
      "form": {
        "word": "hello"
      },
      "headers": {
        "Accept-Encoding": "identity",
        "Content-Length": "10",
        "Content-Type": "application/x-www-form-urlencoded",
        "Host": "httpbin.org",
        "User-Agent": "Python-urllib/3.6",
        "X-Amzn-Trace-Id": "Root=1-6124521e-0844c17c161a90e06ad543c0"
      },
      "json": null,
      "origin": "116.25.236.109",
      "url": "http://httpbin.org/post"
    }
    

      该网页通过POST传递了表单数据,在form内输出一个字典格式,如果传递字典有多个元素则form下也对应多个元素。

      可以看到,我们传递的参数data中的字典键值对"word":"hello"出现在了form字段中,这表明了表单提交的方式,以POST方式传输数据。

      timeout参数

      timeout参数用于设置超时时间,单位为秒,意思是如果请求时间超过了设置的时间,还没有得到响应,就会抛出异常,在实际爬虫中,我们要对许多URL发起请求,中途肯定会出现爬取异常的URL,短时间无法获得响应,我们需要识别出这种异常,就需要用到timeout参数。

      如果不指定timeout参数,会使用全局默认时间。它支持HTTP、HTTPS、FTP请求。

      通过代码示例理解:

    # 调用urllib库中的request模块
    import urllib.request
    response = urllib.request.urlopen("http://httpbin.org/get",timeout=0.01)
    print(response.read().decode('utf-8'))
    

      这里我们把timeout参数设成0.01秒,网页反应时间是没这么快的,所以一定会报错,我们来看一下报错的信息:

    urllib.error.URLError: <urlopen error timed out>
    

      显示异常属于urllib.error模块,错误原因是超时。

      在实际爬虫中,我们可以用过设置这个超时时间来控制一个网页在长时间未响应时,就跳过它的抓取。这可以利用try except 语句来实现。

      代码示例:

    import socket
    import urllib.request
    
    try:
        response = urllib.request.urlopen("http://httpbin.org/get",timeout=0.01)
        print(response.read().decode('utf-8'))
    except urllib.error.URLError as e :
        if isinstance(e.reason,socket.timeout):
            print("time out !")
    

      在代码中加入try except 语句后,如果响应超时,便会跳过抓取,我们捕获了URLError异常后,接着判断异常是socket.timeout类型(意思就是超时异常),于是打印出了time out !

      输出结果如下:

    time out !
    

      其他参数(少用)

      context参数:它必须是ssl.SSLContext类型,用来指定SSL设置,实现SSL加密传输。

      cafile、capath:分别指定CA证书和它的路径,用于实现可信任的CA证书的HTTP请求。

      cadefault:已经弃用,默认False。

      2,Request

       上文已经讲解了如何利用urlopen()方法实现最基本的请求发起。但这几个简单的参数并不足以构建一个完整的请求(过于简单的请求会被浏览器识别为爬虫而拒绝响应)。如果请求中需要加入Headers等信息,就需要用到Requset类来构建。

      同样通过实例展示Request用法:

    # 展示Request
    import urllib.request
    
    request = urllib.request.Request('http://httpbin.org/get')
    response = urllib.request.urlopen(request)
    print(response.read().decode('utf-8'))
    

      我们依然是用urlopen()方法来发送请求,只是这次的参数不在是url,而是一个Request类型对象。通过构造这个数据类型,我们可以将请求独立为一个对象,并且更灵活的配置参数。

      Reque的函数原型如下:

    class urllib.request.Request(url,data = None,headers ={ },origin_req_host =None,unverifiable = False,method = None)
    

      下面来看它的具体参数:

      url:用于请求的URL,这是毕传参数,其他都是可选参数

      data:必须是bytes类型,如果它是字典,可以先用urllib.parse模块里的urlencode方法编码(用于POST请求)

      headers:是一个字典,它就是请求头,我们可以在构造请求时,通过headers参数直接构造,也可以通过调用请求示例的add_header()方法添加

      origin_req_host:指的是请求方的host名称或IP地址。

      unverifiable:表示这个请求是否无法验证,默认为False,意思是说用户没有足够权限来选择接收这个请求的结果。比如我们请求一个HTML文档中的图片,但是我们没有自助抓取图象的权限,这是unverifiable的值就是True。

      method:是一个字符串,用来指示请求使用方法,如GET、POST、PUT等。

    # Request请求多个参数
    import urllib.request,urllib.parse
    
    url = "http://httpbin.org/post"
    headers = {
     "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3314.0 Safari/537.36 SE 2.X MetaSr 1.0",
     "Host":"httpbin.org" 
    }
    
    dict = {'name':'Germey'}
    data = bytes(urllib.parse.urlencode(dict),encoding='utf-8')
    req = urllib.request.Request(url=url,data=data,headers=headers,method='POST')
    response = urllib.request.urlopen(req)
    print(response.read().decode('utf-8'))
    

      在这个示例中,我们通过4个参数构造了一个请求,我们加入了url、data、headers、method,其中最重要的是把头信息(包含User-Agent和Host)写入了  Request,让请求变得更完整。

      运行结果如下:

    {
      "args": {},
      "data": "",
      "files": {},
      "form": {
        "name": "Germey"
      },
      "headers": {
        "Accept-Encoding": "identity",
        "Content-Length": "11",
        "Content-Type": "application/x-www-form-urlencoded",
        "Host": "httpbin.org",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3314.0 Safari/537.36 SE 2.X MetaSr 1.0",
        "X-Amzn-Trace-Id": "Root=1-61246460-1f4d152f11557e9f6670f33a"
      },
      "json": null,
      "origin": "116.25.236.109",
      "url": "http://httpbin.org/post"
    }
    

      可以看到,我们成功通过新建的参数,设置了data、headers 和 method。

      另外之前提到的headers也可以用add_header()方法添加,示例代码如下:

    req = urllib.request.Request(url=url,data=data,method='POST')
    req.add_header('User-Agent',"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3314.0 Safari/537.36 SE 2.X MetaSr 1.0")
    

      传递参数格式为key value格式

  • 相关阅读:
    MongoDB数据查询详解
    MongoDB增加数据
    laravel安装初体验
    操作MongoDB
    MongoDB基本概念和安装配置
    tp5操作mongo
    c语言运算符优先级与while循环案例
    tp5下通过composer实现日志记录功能
    c语言中类型转换与赋值运算符、算术运算符、关系运算符、逻辑运算符。原码、反码、补码。小解。
    scanf使用与运算符
  • 原文地址:https://www.cnblogs.com/minseo/p/15179611.html
Copyright © 2011-2022 走看看