• 接口测试


    一、基本用法

    1.1 实例引入

    import requests
    
    r = requests.get("https://www.baidu.com")
    print("r:", r)
    print("type r:", type(r))
    print("r.status_code:", r.status_code)
    print("type r.text:", type(r.text))
    print("r.text:", r.text)
    print("r.cookes:", r.cookies)
    

    运行结果

    r: <Response [200]>
    type r: <class 'requests.models.Response'>
    r.status_code: 200
    type r.text: <class 'str'>
    r.text: <!DOCTYPE html>
    <!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css><title>ç™¾åº¦ä¸€ä¸‹ï¼Œä½ å°±çŸ¥é“</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus=autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn" autofocus></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新闻</a> <a href=https://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地图</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>视频</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>贴吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登录</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登录</a>');
                    </script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多产品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>关于百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>&copy;2017&nbsp;Baidu&nbsp;<a href=http://www.baidu.com/duty/>使用百度前必读</a>&nbsp; <a href=http://jianyi.baidu.com/ class=cp-feedback>意见反馈</a>&nbsp;京ICP证030173号&nbsp; <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>
    
    r.cookes: <RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>
    
    这里我们调用 `get()方法`得到一个 Response对象,然后分别输出了 Response 的类型、状态码、响应体的类型、内容以及 Cookies。
    
    通过运行结果可以发现,它的返回类型是 requests.models.Response,响应体的类型是字符串str,Cookies的类型是RequestsCookieJar。
    
    使用 get() 方法成功实现一个 GET 请求,这倒不算什么,更方便之处在于其他的请求类型依然可以用一句话来完成:
    
    r = requests.post('http://httpsbin.org/post')
    r = requests.put('http://httpsbin.org/put')
    r = requests.delete('http://httpsbin.org/delete')
    r = requests.head('http://httpsbin.org/get')
    r = requests.options('http://httpsbin.org/get')
    

    1.2 GET 请求

    首先,构建一个最简单的GET请求,请求的连接为 http://httpbin.org/get ,该网站会判断如果客户端发起的是GET 请求的话,它返回相应的请求信息:
    
    import requests
    
    r = requests.get("http://httpbin.org/get")
    print(r.text)
    
    运行结果如下:
    
    {
      "args": {}, 
      "headers": {
        "Accept": "*/*", 
        "Accept-Encoding": "gzip, deflate", 
        "Host": "httpbin.org", 
        "User-Agent": "python-requests/2.24.0", 
        "X-Amzn-Trace-Id": "Root=1-600ed10f-0f0cb6bc14f6daa030ecaceb"
      }, 
      "origin": "39.182.7.4", 
      "url": "http://httpbin.org/get"
    }
    
    如果要添加两个参数,该怎么写?
    
    r = requests.get('http://httpbin.org/get?name=germey?age=22')
    
    这样写不是不可以,但是非常不人性化。一般情况下,这种信息数据会用字段来存储。
    
    import requests
    
    data = {
        'name': 'germey',
        'age': 22
    }
    
    r = requests.get("http://httpbin.org/get", params=data)
    print(r.text)
    
    运行结果
    
    {
      "args": {
        "age": "22", 
        "name": "germey"
      }, 
      "headers": {
        "Accept": "*/*", 
        "Accept-Encoding": "gzip, deflate", 
        "Host": "httpbin.org", 
        "User-Agent": "python-requests/2.24.0", 
        "X-Amzn-Trace-Id": "Root=1-600ed27c-7752b72b3cf473ca141478b6"
      }, 
      "origin": "39.182.7.4", 
      "url": "http://httpbin.org/get?name=germey&age=22"
    }
    
    通过运行结果可以判断,请求的链接自动被构造成了:`http://httpbin.org/get?name=germey&age=22`
    
    网页会犯的类型虽然是str类型,但是是json格式的。所以,如果想解析返回结果,得到一个字典格式的话,可以直接调用 json()方法。
    
    import requests
    
    r = requests.get("http://httpbin.org/get")
    print(type(r.text))
    print(r.json())
    print(type(r.json()))
    
    运行结果
    
    <class 'str'>
    {'args': {}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.24.0', 'X-Amzn-Trace-Id': 'Root=1-601039cf-7d2c52606d54bde54c153c80'}, 'origin': '39.182.7.4', 'url': 'http://httpbin.org/get'}
    <class 'dict'>
    
    如果返回的不是 json 格式的数据,便会解析错误,抛出 `json.decoder.JSONDecodeError`异常
    

    1.2.1 抓取网页

    如果请求普通的网页,则肯定能获得响应的内容。(这里的 headers信息要加,不然会被知乎屏蔽)
    

    import requests
    import re
    
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36 Edg/88.0.705.50"
    }
    
    r = requests.get("https://www.zhihu.com/explore", headers=headers)
    pattern = re.compile('ExploreSpecialCard-contentTitle.*?noopener noreferrer.*?>(.*?)</a>', re.S)
    titles = re.findall(pattern, r.text)
    print(titles)
    
    运行结果
    
    ['人没有食物的时候吃自己的屎能活命吗?', '灵魂存在吗?为什么?', '有什么值得做的手工?', '如何看待 2020 年山东 GDP 73129 亿元,增长 3.6% ?山东未来的发展前景如何?', '数字寿光,味道如何?', '山东创新怎么办:解放思想,放手让人们去想、去看、去试、去干', '有哪些收纳神器能让你家里焕然一新?', '有哪些美好寓意适合过年的植物?', '有哪些年味十足的餐具或摆盘推荐?', '2020 年你听到的最劲爆的公司八卦是什么?', '2021 年,你们公司的年会是怎么办的?你中奖了吗?', '有哪些 2020 年工作中不能忍受的事,是你匿名才敢说出来的?']
    
    

    1.2.2 抓取二进制数据

    上面返回的 HTML 文档,如果要抓取图片、音频、视频等文件,该怎么办?
    
    以github 的站点图标为例来看一下
    
    import requests
    
    r = requests.get("https://github.com/favicon.ico")
    print(r.text)
    print("读取r.content:", r.content)
    
    
    运行结果:
    

    前者是纯粹的乱码(打印一张二进制图片,当然会乱码),后者结果前带有一个b,这代表是bytes类型的数据。
    
    尝试保存下来
    
    import requests
    
    r = requests.get("https://github.com/favicon.ico")
    with open("favicon.ico", "wb") as f:
        f.write(r.content)
    
    运行结果
    

    同样的,音频和视频也可以用这种方式获取。

    1.3 POST 请求

    import requests
    
    data = {"name": "germey", "age": "22"}
    r = requests.post("http://httpbin.org/post", data=data)
    print(r.text)
    

    运行结果

    {
      "args": {}, 
      "data": "", 
      "files": {}, 
      "form": {
        "age": "22", 
        "name": "germey"
      }, 
      "headers": {
        "Accept": "*/*", 
        "Accept-Encoding": "gzip, deflate", 
        "Content-Length": "18", 
        "Content-Type": "application/x-www-form-urlencoded", 
        "Host": "httpbin.org", 
        "User-Agent": "python-requests/2.24.0", 
        "X-Amzn-Trace-Id": "Root=1-600edbed-6bfedbd7231caafc0adb04c4"
      }, 
      "json": null, 
      "origin": "39.182.7.4", 
      "url": "http://httpbin.org/post"
    }
    

    1.4 响应

    发送请求后,得到的自然就是响应。在上面的实例中,我们使用text和content获取了响应的内容。此外,还有很多属性和方法可以用来获取其他信息,比如状态码、响应头、Cookies等。
    
    import requests
    
    r = requests.get("http://www.jianshu.com")
    print(type(r.status_code), r.status_code)
    print(type(r.headers), r.headers)
    print(type(r.cookies), r.cookies)
    print(type(r.url), r.url)
    print(type(r.history), r.history)
    
    运行结果
    
    <class 'int'> 403
    <class 'requests.structures.CaseInsensitiveDict'> {'Server': 'Tengine', 'Date': 'Mon, 25 Jan 2021 15:00:01 GMT', 'Content-Type': 'text/html', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'Vary': 'Accept-Encoding', 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload', 'Content-Encoding': 'gzip'}
    <class 'requests.cookies.RequestsCookieJar'> <RequestsCookieJar[]>
    <class 'str'> https://www.jianshu.com/
    <class 'list'> [<Response [301]>]
    
    状态码常用来判断请求是否成功,而requests还提供了一个内置的状态码查询对象 `requests.codes`
    
    import requests
    
    r = requests.get("http://www.jianshu.com")
    exit() if not r.status_code == requests.codes.ok else print("Request Successfully")
    
    或者
    import requests
    
    r = requests.get("http://www.jianshu.com")
    exit() if not r.status_code == requests.codes.not_found else print("Request Successfully")
    

    这里通过比较返回码和内置的成功的返回码,来保证请求得到了正常响应,输出成功请求的消息,否则程序终止,这里我们用 request.codes.ok 来代码200 状态码。not_found 表示404。状态码和查询条件如下:

    http://tools.jb51.net/table/http_status_code

    二、高级用法

    2.1 文件上传

    注意点:``Content-Type: multipart/form-data; boundary=${bound}这个 headers不要传入

    import requests
    
    files = {"file": open("favicon.ico", "rb")}
    r = requests.post("http://httpbin.org/post", files=files)
    print(r.text)
    

    运行结果

    {
      "args": {}, 
      "data": "", 
      "files": {
        "file": "data:application/octet-stream;base64,AAABAAIAEBAAAAEAIAAoBQAAJgAAACAgAAABACA..."
      }, 
      "form": {}, 
      "headers": {
        "Accept": "*/*", 
        "Accept-Encoding": "gzip, deflate", 
        "Content-Length": "6665", 
        "Content-Type": "multipart/form-data; boundary=0229b636b80f365a842431005fcc3b87", 
        "Host": "httpbin.org", 
        "User-Agent": "python-requests/2.24.0", 
        "X-Amzn-Trace-Id": "Root=1-600ee40b-25f57563781bc63e62e290de"
      }, 
      "json": null, 
      "origin": "39.182.7.4", 
      "url": "http://httpbin.org/post"
    }
    

    2.2 Cookies

    import requests
    
    r = requests.get("https://www.baidu.com")
    print(r.cookies)
    for key, value in r.cookies.items():
        print(key + "=" + value)
    
    运行结果
    
    <RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>
    BDORZ=27315
    
    当然,我们也可以直接用 Cookie 来维持登录状态,下面以博客园为例。先登录,再抓取
    

    放置到 Headers 里面,然后发送请求:
    
    import requests
    
    headers = {
        "Cookie": "_ga=GA1.2.1413714814.1594529717; UM_distinctid=175e989f222d0f-04911..."
    
    }
    
    r = requests.get("https://www.cnblogs.com/", headers=headers)
    print(r.text)
    

    运行结果

    当然,你也可以通过cookies参数来设置,不过这样就需要构造 RequestsCookieJar对象,而且要分割一下 cookies。相对繁琐,但是效果是相同的。
    
    import requests
    
    cookies = "_ga=GA1.2.1413714814.1594529717; UM_distinctid=175e989f222d0f-0491105d9619f4-5a30124d-384000-175e989f223cb3; CNZZDATA5897703=cnzz_eid=951549392-1608963889-https%3A%2F%2Fwww.baidu.com%2F&ntime=1608963889; CNZZDATA1274152299=842200016-1611152649-https%3A%2F%2Fhome.cnblogs.com%2F|1611152649; __gads=ID=f93b14b1270a542b-2234aa7bcec50020:T=1611378123:R:S=ALNI_Mb2gIxmcINNv-gOKrzt7Zt9SbDvRw..."
    jar = requests.cookies.RequestsCookieJar()
    
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36 Edg/88.0.705.50",
        "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"
    }
    for cookie in cookies.split(";"):
        key, value = cookie.split("=", 1)
        jar.set(key, value)
    
    r = requests.get("https://www.cnblogs.com/", cookies=jar, headers=headers)
    print(r.text)
    
    如上,先新建了一个 RequestCookieJar 对象,然后将复制下来的 cookies 利用 split() 方法分割,接着利用 set() 方法设置好每个 Cookie 的 key 和 value,然后通过调用 requests 的 get() 方法并传递给 cookies 参数几颗。
    

    2.3 会话维持

    正常情况下,如果第一个请求利用 post() 方法登录了某个网站,第二次想获取成功登录后的自己的个人信息,你又用了一次 get() 方法去请求个人信息页面。这时候是不能获取个人信息的。
    
    解决方法一就是设置cookies。但是这样做很麻烦。方法二是利用 Session 对象,这样可以方便的维护一个会话,而且不用担心 cookies 的问题,它会帮我们自动处理好。
    
    import requests
    
    requests.get("http://httpbin.org/cookies/set/number/123456789")
    r = requests.get("http://httpbin.org/cookies")
    print(r.text)
    

    运行结果

    {
      "cookies": {}
    }
    

    set/number/123456789 实际上设置了一个cookie,但是第二次get的时候没有取到。

    换上 Session 试试看:

    import requests
    
    s = requests.Session()
    s.get("http://httpbin.org/cookies/set/number/123456789")
    r = s.get('https://httpbin.org/cookies')
    print(r.text)
    

    运行结果

    {
      "cookies": {
        "number": "123456789"
      }
    }
    

    利用 Session,可以做到模拟同一个会话而不用担心 Cookies 的问题。它通常用于模拟登录成功之后再进行下一步的操作。

    2.4 SSL 证书验证

    当https网站,且此网站证书没有被官方CA机构信任,会出现证书验证错误的结果(您的链接不是私密链接)的开关, 用于认证SSL证书, 默认为True。(之前12306网站不是CA机构信任的。现在应该试了。)
    

    import requests
    
    r = requests.request('GET', 'https://kyfw.12306.cn', verify=False)
    print(r.text)
    

    如果直接禁用,会有警告,它建议我们给它指定证书。我们可以设置忽略警告来屏蔽

    import requests
    from requests.packages import  urllib3
    
    urllib3.disable_warnings()
    r = requests.request('GET', 'https://kyfw.12306.cn', verify=False)
    print(r.text)
    

    或者通过捕获警告到日志的方式忽略警告

    import logging
    import requests
    
    logging.captureWarnings(True)
    
    r = requests.request('GET', 'https://kyfw.12306.cn', verify=False)
    print(r.status_code) 
    

    当然,我们也可以制定一个本地证书用作客户端证书,这可以是单个文件(包含秘钥和证书)或一个包含两个文件路径的元组:

    import requests
    
    response = requests.get("https://www.12306.cn",cert=("/path/server.crt","./path/key"))
    print(response.status_code)
    

    2.5 代理设置

    如果大规模爬取,对于频繁的请求,网站直接回封禁客户端IP。这时候就需要代理

    import requests
    
    proxies = {
        "http": "http://10.10.1.10:3128",
        "http": "http://10.10.1.10:1080"
    }
    
    requests.get("https://www.taobao.com", proxies=proxies)
    

    若代理需要使用 HTTP Basic Auth ,可以使用类似 http://user:password@host:port 这样的语法来设置代理

    import requests
    
    proxies = {
        "http": "http://user:password@10.10.1.10:3128/",
    }
    
    requests.get("https://www.taobao.com", proxies=proxies)
    

    除了基本的 HTTP 代理外,requests 还支持 SOCKS 协议的代理。

    首先要安装 socks 这个库:

    pip3 install 'requests[socks]'
    

    然后就可以使用 SOCKS 协议代理了,示例如下:

    import requests
    
    proxies = {
        "http": "socks5://user:password@10.10.1.10:3128",
        "https": "socks5://user:password@10.10.1.10:3128"
    }
    requests.get("https://www.taobao.com", proxies=proxies)
    

    2.6 超时设置

    用于设定超时时间, 单位为秒,当发起一个get请求时可以设置一个timeout时间, 如果在timeout时间内请求内容没有返回, 将产生一个timeout的异常。

    import request
    
    r = requests.get("https://www.taobao.com", timeout=1)
    print(r.status_code)
    

    实际上,请求分为两个阶段,即连接(connect)读取(read)。单独设置的话,为两者的 timeout 总和。如果要分别指定,就可以传入一个元祖:

    r = requests.get("https://www.taobao.com", timeout=(5,11,30))
    

    如果想永久等待,可以直接将timeout设置为None,或者不设置直接留空,因为默认是None。

    2.7 身份认证

    requests自带的身份认证功能
    ​```python
    import requests
    from requests.auth import HTTPBasicAuth
    
    r = requests.get("http://localhost:5000", auth=HTTPBasicAuth("username", "password"))
    print(r.status_code)
    

    ​ 成功会返回200状态码,失败会返回401状态码

    ​ 有一个简便写法

    import requests
    
    r = requests.get("http://localhost:5000", auth=("username", "password"))
    print(r.status_code)
    

    ​ 此外,requests 还提供了其他认证方式,如OAuth认证,不过此时需要安装oauth包,安装命令如下:

    pip3 install requests_oauthlib
    

    ​ 使用 OAuth1认证的方法如下:

    import requests
    from requests_oauthlib import OAuth1
    
    url = "https://api.twitter.com/1.1/account/verify_credentials.json"
    auth = OAuth1("YOUR_APP_KEY","YOUR_APP_SECRET","USER_OATH_TOKEN", "USER_OATH_TOKEN_SECRET")
    requests.get(url, auth=auth)
    

    2.8 Prepared Request

    可以将请求表示为数据结构,其中各个参数都可以通过一个Request对象来表示。

    url = "http://httpbin.org/post"
    data = {
        "name": "germey"
    }
    
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36 Edg/88.0.705.50"
    }
    s = Session()
    req = Request("POST", url, data=data, headers=headers)
    prepped = s.prepare_request(req)
    r = s.send(prepped)
    print(r.text)
    

    ​ 这里我们引入了 Request,然后用url,data和headers参数够早了一个 Request对象,这时需要再调用 Session 的prepare_request() 方法将其转换为一个 Prepared Request对象,然后调用 send() 方法发送即可。

    ​ 有了Request这个对象,就可以将请求当做独立的对象来看待,这样在进行队列调度时会非常方便。后面我们会用它来构造一个 Request队列。

  • 相关阅读:
    [转]优秀的程序员不会觉得累成狗是一种荣耀
    .NET读写Excel工具Spire.XlS使用(DataExport )
    WPF之Binding深入探讨
    第一个WPF应用程序
    Visio作图
    唯一的重复元素
    Strange Problem O(∩_∩)O~
    数据库知识点①
    HDU 2825 Wireless Password
    POJ 1625 Censored!
  • 原文地址:https://www.cnblogs.com/dongye95/p/14327864.html
Copyright © 2020-2023  润新知