Python_requests_最简单易用的HTTP库

requests是python最简单易用的HTTP库。

模块安装

默认是没有安装requests模块的,需要通过pip安装

pip install requests

常用请求方法

方法解释
requests.request(method, url, **kwargs) 构造以下请求的一个基础方法
requests.get(url, params=None, **kwargs) 构造一个get请求
requests.post(url, data=None, json=None, **kwargs) 构造一个post请求
requests.put(url, data=None, **kwargs) 构造一个put请求
requests.delete(url, data=None, **kwargs) 构造一个delete请求

参数说明

  • method:请求方式
  • url:网站地址
  • params:请求参数,多用于请求参数为表单的情况。
  • data:请求数据,用于post请求传参,字符类型为字典,request.body的内容为a=1&b=2这种形式
  • json:请求数据,用于post请求参数,字符类型为json字符串或字典,request.body的内容为'{"a": 1, "b": 2}'这种形式。Content-Type为application/json 一般使用该参数。
  • **kwargs为缺省参数
    • headers:请求的头部,数据类型为字典
    • cookies:请求的cookie,数据类型为字典或CookieJar
    • timeout:请求超时时间,单位为秒
    • auth:http认证,数据类型为元组
    • files:向服务器传输文件,数据类型为字典。key值为接口定义的字段,value值为元组:('filename', fileobj, 'content_type') ,使用了该参数不要加Content-Type
    • proxies:设置访问代理服务器,数据类型为字典
    • allow_redirects: 是否允许对url进行重定向, 默认为True。
    • stream: 是否对获取内容进行立即下载, 默认为True。
    • verify:是否认证SSL证书, 默认为True。
    • cert: 设置保存本地SSL证书路径

获取响应内容

r.encoding              # 获取当前的编码
r.encoding = 'utf-8'    # 设置编码
r.text                  # 以encoding解析返回内容。字符串方式的响应体,会自动根据响应头部的字符编码进行解码。
r.content               # 以字节形式(二进制)返回。字节方式的响应体,会自动为你解码 gzip 和 deflate 压缩。

r.headers               # 以字典对象存储服务器响应头,但是这个字典比较特殊,字典键不区分大小写,若键不存在则返回None

r.status_code           # 响应状态码
r.raw                   # 返回原始响应体,也就是 urllib 的 response 对象,使用 r.raw.read()   
r.ok                    # 查看r.ok的布尔值便可以知道是否登陆成功
 #*特殊方法*#
r.json()                # Requests中内置的JSON解码器,以json形式返回,前提返回的内容确保是json格式的,不然解析出错会抛异常
r.raise_for_status()    # 失败请求(非200响应)抛出异常

代码演示

 get请求

import requests


rsp = requests.get("http://www.baidu.com")
print(rsp)
print(rsp.text)
print(rsp.encoding)
print(rsp.headers)

上传文件或图片

以下图接口为例,接口传入了id和file字段

 响应如下

使用request模块,从接口完成图片上传。代码如下:

import requests


url = "https://xxxxxx/uploadIdCard"
params = {"id": "ipJO6Yys3Zql0YXXXXXXXXXXXXXXXXXXXX"}

# 文件或图片传入格式说明:file为接口定义的字段,字段值传入元组格式
files = {
    "file": ("123.pdf", open("./123.pdf", "rb")),
}
# 非图片(或文件)字段正常入参, 图片(或文件)以files传入
rsp = requests.post(url, params=params, files=files)
print(rsp.json())

注意:请求时不要加{"Content-Type": "multipart/form-data"}的header,否则请求会报错

执行结果:

常见问题

1、响应header中的中文乱码

现象:接口响应的文件名称的中文部分乱码

解决方法:使用了Python中的urllib库来进行URL解码

代码如下:

from urllib import parse

filename = parse.unquote("test_data_%E5%85%A5%E7%BB%84%E6%98%8E%E7%BB%86_20230511194810.xlsx")
print(filename)

2、向https地址发起请求报错ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] 

报错原因是证书验证失败,从Python 2.7.9版本开始,服务器证书验证功能默认启动。如果证书验证失败,将拒绝后续操作。这种做法能够防止中间人攻击,并能确保客户端认定的服务器身份是真实的。然而,对于自签名证书,由于系统的CA证书中通常不包含自签名CA证书的内容,因此证书验证可能会失败。

解决方法:

将参数verify设置为False

设置为False后,能正常请求接口,但是会有告警。

忽略告警:

# 导入模块
from requests.packages import urllib3

# 在发起请求添加忽略告警代码
urllib3.disable_warnings()

再次执行就没有告警提示了~

简单封装

# -*- coding:utf-8 -*-
"""model 基类, 无需变动
"""
import json
import os

import requests
from requests.packages import urllib3

# from utils import log


class Model(object):
    """请求模型基类
    """
    url = None  # protocol://hostname[:port]

    def __init__(self):
        self.public_headers = {}   # 请求头
        self.setup()

    # @property
    # def _log(self):
    #     """记录请求中的日志
    #     """
    #     return log.create_logger("API_LOG")

    def setup(self):
        """发送请求的前预置操作
        比如设置公共headers:在该方法中使用 add_public_headers 方法添加
        """
        pass

    def add_public_headers(self, headers):
        """添加公共headers
        :param headers: 添加的请求头
        :type headers: dict
        """
        self.public_headers.update(headers)

    def del_public_headers(self, key):
        """删除公共headers"""
        try:
            self.public_headers.pop(key)
        except KeyError:
            pass

    def handle_request_headers(self, headers):
        """ 处理请求传入的headers """
        headers = headers or {}
        for k in self.public_headers.keys():
            if k in headers:
                continue
            headers.update({k: self.public_headers[k]})
        return headers

    def _request(self, method, interface, body=None, params=None, timeout=60, encode=None, headers=None, files=None):
        """api请求基础方法
        :param method: 请求方式
        :param interface: uri
        :param body: body请求参数。 Content-Type为application/json 一般使用该参数
        :param params: 链接请求参数
        :param timeout: 超时时间
        :param encode: 请求转码
        :param headers: 添加header。这里添加的header为请求当前请求添加或修改的header。下次请求默认使用公共 public_headers
        :param files: 上传文件使用
        :return: response
        """
        # 如果发起http请求,则必须配置类属性url
        if self.url is None:
            raise NotImplementedError("需要在%s定义url类属性才能发起http请求" % self.__class__)
        url = self.url + interface
        # req_log = body or params
        headers = self.handle_request_headers(headers)
        # self._log.info("=============[%s %s]=============" % (method.upper(), interface))
        # self._log.info(">>> [header] %s" % headers)
        # self._log.info(">>> [Request] %s" % req_log)
        urllib3.disable_warnings()
        self._response = requests.request(
            method, url, json=body, params=params, timeout=timeout, headers=headers, files=files, verify=False
        )
        if encode:
            self._response.encoding = encode
        rsp = self._response.content
        try:
            rsp = json.loads(rsp)
        except Exception:
            pass
        # if isinstance(rsp, bytes):
        #     self._log.info("<<< [Response] 二进制内容不记录日志\n\n")
        # else:
        #     self._log.info("<<< [Response] %s\n\n" % rsp)
        return rsp

    def request_post(self, interface, body=None, params=None, timeout=60, encode=None, headers=None):
        return self._request("POST", interface, body, params, timeout, encode, headers)

    def request_get(self, interface, body=None, params=None, timeout=60, encode=None, headers=None):
        return self._request("GET", interface, body, params, timeout, encode, headers)

    def request_put(self, interface, body=None, params=None, timeout=60, encode=None, headers=None):
        return self._request("PUT", interface, body, params, timeout, encode, headers)

    def upload_file(self, interface, file_path, upload_file_name=None, params=None, file_field="file"):
        """上传文件
        file_path:导入文件路径
        upload_file_name:上传文件的名称。注意:修改后的名称文件格式需要一致
        params:除了file字段外的其它字段入参
        file_field:上传文件二进制的字段名称,默认为file
        """
        upload_file_name = upload_file_name or os.path.basename(file_path)
        # 文件或图片传入格式说明:file为接口定义的字段,字段值传入元组格式
        files = {file_field: (upload_file_name, open(file_path, "rb"))}
        headers = {"Content-Type": None}
        return self._request("POST", interface, params=params, headers=headers, files=files)

    def download_file(self, interface, save_dir, filename, body=None, params=None, timeout=60, headers=None):
        """下载文件
        download_dir:下载的目录
        filename:保存的文件名称
        """
        # 获取响应的二进制文件
        rsp = self.request_post(interface, body=body, params=params, timeout=timeout, headers=headers)
        if not isinstance(rsp, bytes):
            return rsp

        # 指定下载路径
        file_path = os.path.join(save_dir, filename)
        if not os.path.exists(save_dir):
            os.makedirs(save_dir)

        # 保存文件
        with open(file_path, 'wb') as f:
            f.write(rsp)
        print("文件保存成功:" + file_path)
        return file_path

    @property
    def response(self):
        """ 最后一次请求的实例对象
        """
        return self._response

    @property
    def response_header(self):
        """ 记录最后一次请求的响应头
        """
        return self._response.headers

    @property
    def response_status_code(self):
        """ 记录最后一次请求的响应码
        """
        return self._response.status_code

 

posted @ 2019-06-24 16:26  码上测  阅读(3586)  评论(0编辑  收藏  举报