requests库构建HTTP请求


构建HTTP请求:
1.构建请求URL参数

    什么是url参数?

            比如:

            https://www.baidu.com/s?wd=iphone&rsv_spt=1
            问号后面的部分 wd=iphone&rsv_spt=1 就是 url 参数,

            每个参数之间是用 & 隔开的。

            上面的例子中 有两个参数 wd 和 rsv_spt, 他们的值分别为 iphone 和 1 。

            url参数的格式,有个术语叫 urlencoded 格式。


            使用Requests发送HTTP请求,url里面的参数,通常可以直接写在url里面,比如

            response = requests.get('https://www.baidu.com/s?wd=iphone&rsv_spt=1')

            但是有的时候,我们的url参数里面有些特殊字符,比如 参数的值就包含了 & 这个符号。

            那么我们可以把这些参数放到一个字典里面,然后把字典对象传递给 Requests请求方法的 params 参数,这时候params称为请求消息体,如下

            urlpara = {
                'wd':'iphone&ipad',
                'rsv_spt':'1'
            }

            response = requests.get('https://www.baidu.com/s',params=urlpara)
            
2.构建请求消息头

            有时候,我们需要自定义一些http的消息头

            每个消息头也就是一种 键值对的格式存放数据,如下所示

            user-agent: my-app/0.0.1
            auth-type: jwt-token
            Requests发送这样的数据,只需要将这些键值对的数据填入一个字典。

            然后使用post方法的时候,指定参数 headers 的值为这个字典就可以了,如下

            headers = {
                'user-agent': 'my-app/0.0.1', 
                'auth-type': 'jwt-token'
            }

            r = requests.post("http://httpbin.org/post", headers=headers)
            print(r.text)
            
3.构建请求消息体
            当我们进行API 接口测试的时候, 根据接口规范,构建的http请求,通常需要构建消息体。
            http 的 消息体就是一串字节,里面包含了一些信息。这些信息可能是文本,比如html网页作为消息体,也可能是视频、音频等信息。
            消息体可能很短 ,只有一个字节, 比如字符 a。 也可能很长,有几百兆个字节,比如一个视频文件。
            最常见的消息体格式当然是 表示网页内容的 HTML。
            Web API接口中,消息体基本都是文本,文本的格式主要是这3种: urlencoded ,json , XML。
            注意:消息体采用什么格式,是由 开发人员设计的决定的
            

        3.1 xml格式消息体
            如果设计者决定用 XML 格式传输一段信息,用Requests库,只需要这样

            payload = '''
            <?xml version="1.0" encoding="UTF-8"?>
            
                良好
                30%
                暂无
            

            '''

            r = requests.post("http://httpbin.org/post",
                              data=payload.encode('utf8'))
            print(r.text)    #打印响应消息体的文本内容
                    
            如果作为系统开发的设计者,觉得发送这样一篇报告,只需要核心信息就可以了,不需要这样麻烦的XML格式,也可以直接用纯文本,像这样

            payload = '''
             report 
             Overall:良好 
             Progress: 30% 
             Problems:暂无
            '''
            r = requests.post("http://httpbin.org/post", data=payload.encode('utf8'))
            print(r.text)            
            #data用于存放消息体,headers用于存放消息头                    如果传入的参数是字符串类型,那么requests库使用缺省编码latin-1编码为字节串放到http消息体中,发送出去,但是中文是无法使用latin-1编码的,需要使用utf8编码为字节串对象Bytes传入给data参数.
        
        3.2 urlencoded 格式消息体 
            这种格式的消息体就是一种 键值对的格式存放数据,如下所示

            key1=value1&key2=value2
            Requests发送这样的数据,当然可以直接把这种格式的字符串传入到data参数里面。

            但是,这样写的话,如果参数中本身就有特殊字符,比如等号,就会被看成参数的分隔符,就麻烦了。

            我们还有更方便的方法:只需要将这些键值对的数据填入一个字典。
            
            urlpara = {
                'wd':'iphone&ipad',
                'rsv_spt':'1'
            }
            
            
            response = requests.get('https://www.baidu.com/s',params=urlpara) 
            #传递给params的参数是放在url中的
            #传递给data/json的参数是放在消息体中的
            
            然后使用post方法的时候,指定参数 data 的值为这个字典就可以了,如下

            payload = {'key1': 'value1', 'key2': 'value2'}

            r = requests.post("http://httpbin.org/post", data=payload)
            print(r.text)
            
            效果等同于:
            r = requests.post("http://httpbin.org/post", data='key1=value1&key2=value2'.encoe('utf8'))  
            只不过当它里面包含&或者=字符时,就会把他当作分隔符,所以不建议使用这种
            
            requests消息体格式不管是哪种类型,最终都会被编码成字节串
            如果requests库发现你所传递的data格式既不是字符串,又不是字节串,而是一个字典格式,那么他会自动将它转化为urlencode格式消息体 
            
            #post方法中,data用于存放消息体
        3.3 json 格式消息体
            json 是一种表示数据的语法格式。 它和Python 表示数据的语法非常像。

            比如要表示上面的报告信息,可以这样

            {
                "Overall":"良好",
                "Progress":"30%",
                "Problems":[
                    {
                        "No" : 1,
                        "desc": "问题1...."
                    },
                    {
                        "No" : 2,
                        "desc": "问题2...."
                    }
                ]
            }
            它的优点是:比xml更加简洁、清晰, 所以程序处理起来效率也更高。


            我们怎样才能构建一个json 格式的字符串呢?


            可以使用json库的dumps方法,如下

            import requests,json

            payload = {
                "Overall":"良好",
                "Progress":"30%",
                "Problems":[
                    {
                        "No" : 1,
                        "desc": "问题1...."
                    },
                    {
                        "No" : 2,
                        "desc": "问题2...."
                    },
                ]
            }

            r = requests.post("http://httpbin.org/post", data=json.dumps(payload))   #headers用于存放消息头,data用于存放消息体,json.dumps(payload)将字典对象转换为字符串(json字符串)
            json.dumps会默认使用将非ascii码字符(如中文) 进行unicode编码,所以在抓包中看到的中文是一串编码, 如果不希望这样,可以使用json.dumps(payload,ensure_asscii=False)),但是又存在一个问题,requests库data串字符串时,会默认进行latin-1编码,  而中文并不能被latin-1编码,所以使用json.dumps(payload,ensure_asscii=False).encode(utf8)),这样比较麻烦,所以不建议使用这种
            如果直接传data = payload, requests库会认为你传递的是一个字典,那么就认为你传的时rulencode消息格式
            
            
            也可以将 数据对象 直接 传递给post方法的 json参数,如下

            r = requests.post("http://httpbin.org/post", json=payload)   
            #如果requests发现消息体传给json,底层会自动调用json.dumps进行 格式转换
            
4.检查HTTP响应
    4.1检查响应状态码
            要检查 HTTP 响应 的状态码,直接 通过 reponse对象的 status_code 属性获取

            import requests

            response = requests.get('http://mirrors.sohu.com/')
            print(response.status_code)
            运行结果发现返回的状态码就是 200
            如果故意写一个不存在的地址

            import requests

            response = requests.get('http://mirrors.sohu.com/haaaaaaaaa')
            print(response.status_code)
            运行结果发现返回的状态码就是 404
                
    4.2检查响应消息头
            要检查 HTTP 响应 的消息头,直接 通过 reponse对象的 headers 属性获取

            import requests,pprint

            response = requests.get('http://mirrors.sohu.com/')

            print(type(response.headers))

            print(dict(response.headers))#转为dict格式
            运行结果如下

            
            {'Cache-Control': 'no-store',
             'Connection': 'keep-alive',
             'Content-Type': 'text/html; charset=utf8',
             'Date': 'Sat, 21 Sep 2019 09:02:32 GMT',
             'Server': 'nginx',
             'Transfer-Encoding': 'chunked'}
            response.headers 对象的类型 是 继承自 Dict 字典 类型的一个 类。

            我们也可以像操作字典一样操作它,比如取出一个元素的值

            print(response.headers['Content-Type'])
            
    4.3  检查响应消息体                    
                前面我们已经说过,要获取响应的消息体的文本内容,直接通过response对象 的 text 属性即可获取

                import requests

                response = requests.get('http://mirrors.sohu.com/')
                print(response.text)
                那么,requests是 以什么编码格式 把HTTP响应消息体中的 字节串 解码 为 字符串的呢?

                requests 会根据响应消息头(比如 Content-Type)对编码格式做推测。

                但是有时候,服务端并不一定会在消息头中指定编码格式,这时, requests的推测可能有误,需要我们指定编码格式。

                可以通过这样的方式指定

                import requests

                response = requests.get('http://mirrors.sohu.com/')
                response.encoding='utf8'
                print(response.text)

                如果我们要直接获取消息体中的字节串内容,可以使用 content 属性,

                比如

                import requests

                response = requests.get('http://mirrors.sohu.com/')
                print(response.content)
                当然,如果可以直接对 获取的字节串 bytes对象进行解码

                print(response.content.decode('utf8'))
                
                为了 方便处理 响应消息中json 格式的数据 ,我们通常应该把 json 格式的字符串 转化为 python 中的数据对象。

                怎么转化? 前面我们学习过 json库,可以直接使用 json库里面的 loads 函数, 把 json 格式的字符串 转化为 数据对象

                import requests,json
                response = requests.post("http://httpbin.org/post", data={1:1,2:2})

                obj = json.loads(response.content.decode('utf8'))
                print(obj)
                requests库为我们提供了更方便的方法,可以使用 Response对象的 json方法,

                如下:

                response = requests.post("http://httpbin.org/post", data={1:1,2:2})
                obj = response.json()
                print(obj)