简易实现一个APP录制 自动保存所触发接口信息的小脚本

应用场景:比如你操作一个APP,从登录,添加商品到购物车,然后点击去结算,支付,最后成功支付,最后到达订单详情,而这个脚本,就是将所这些操作触发的接口全部保存起来

原理:实际上就是利用MitmProxy做中间人代理,后台通过一系列规则,然后获取到每个flow(也可以叫做一个请求上下文,也就是我们常说的,请求+响应一个完整的会话),然后将这些flow的数据全部存起来,最后写到excel中去

下面来开始讲实现过程:

一:搭建MitmProxy环境

mac的用户参考文章:https://blog.csdn.net/Tester_xjp/article/details/104933501

二:编写代理脚本,这里命令成 one.py

依赖模块 pandas,xlsxwriter(这个直接pip安装即可)

# @project_name :person
# @file_name :one
# @auth :xiejiangpeng
# @time :2020-02-29
# @software :PyCharm
# @message


import mitmproxy.http
import pandas as pd
from mitmproxy import ctx
from urllib.parse import unquote

# 过滤
fittler_suffix = (".jpg", ".png", ".gz")
# 白名单域名
whitelist_host = ["mall.xsyxsc.com", "trade.xsyxsc.com", "mall-store.xsyxsc.com", "user.xsyxsc.com", "supertest"]
# 生成的api_info_excel地址
excel_filepath = "/Users/xiejiangpeng/python/mygithub/mypython/person/mitmproxj_api/api_info.xlsx"
# 没有在过滤规则中,同时在白名单域名中的host,当请求数达到下方设置值时,生成excel,同时断开代理
flow_max = 50


def fittler(flow=None):
    """ 过滤

    :param flow:
    :return: 满足过滤条件返回True 不满足返回False
    """
    if fittler_suffix and flow.request.url.endswith(fittler_suffix):
        return True
    return False


def whitelist(flow=None):
    """白名单域名

    :param flow:
    :return: 在白名单中的域名返回True 没有在白名单中的域名返回False
    """
    if not whitelist_host:
        return True
    request_url = flow.request.url
    sizer_url_list = whitelist_host
    for sizer_url in sizer_url_list:
        if sizer_url in request_url:
            return True
    return False


def convert(data):
    """byte类型->str类型

    :param data:
    :return:
    """
    if isinstance(data, dict):
        curr_data = {}
        for key, value in data.items():
            if isinstance(key, bytes):
                key = convert(key)
            if isinstance(value, bytes):
                value = convert(value)
            curr_data.update({key: value})

        return curr_data

    if isinstance(data, bytes):
        return data.decode("utf-8")
    return data


def url_decode(data=None):
    return unquote(data, 'utf-8')


class FlowsData(object):
    num = 1
    flow_id = []
    flow_url = []
    flow_request_headers = []
    flow_request_cookies = []
    flow_request_data = []
    flow_method = []
    flow_response_data = []
    flow_response_headers = []
    flow_response_cookies = []
    flow_http_version = []
    __instance = None

    def __new__(cls, *args, **kwargs):
        if not cls.__instance:
            cls.__instance = super().__new__(cls)
        return cls.__instance

    def __init__(self, flow: mitmproxy.http.HTTPFlow):
        self.flow = flow
        self.append_flowdata()

    def append_flowdata(self):
        self.flow_id.append(self.num)
        self.num += 1
        self.flow_url.append(self.flow.request.url)
        self.flow_request_data.append(url_decode(self.flow.request.text))
        self.flow_request_headers.append(convert(dict(self.flow.request.headers.get_state())))
        self.flow_request_cookies.append(self.flow.request.cookies)
        self.flow_method.append(self.flow.request.method)
        self.flow_response_data.append(self.flow.response.text)
        self.flow_response_headers.append(convert(dict(self.flow.response.headers.get_state())))
        self.flow_response_cookies.append(self.flow.response.cookies)
        self.flow_http_version.append(self.flow.request.http_version)
        if self.num == flow_max + 1:
            save_flow_to_excel(self,
                               filepath=excel_filepath)
            exit()


def save_flow_to_excel(flowsdata: FlowsData = None, filepath: str = None) -> None:
    flow_excel_dict = {
        "请求ID": flowsdata.flow_id,
        "请求地址": flowsdata.flow_url,
        "请求参数": flowsdata.flow_request_data,
        "请求头": flowsdata.flow_request_headers,
        "请求cookie": flowsdata.flow_request_cookies,
        "请求方法": flowsdata.flow_method,
        "请求响应": flowsdata.flow_response_data,
        "响应头": flowsdata.flow_response_headers,
        "响应cookie": flowsdata.flow_response_cookies,
        "http协议版本": flowsdata.flow_http_version,
    }
    if not filepath:
        filepath = f'api_info.xlsx'
    writer = pd.ExcelWriter(filepath)
    df = pd.DataFrame(flow_excel_dict)
    df.to_excel(writer, columns=flow_excel_dict.keys(), index=False, encoding='unicode-escape',
                sheet_name='api_info', engine="xlsxwriter")  # 其中engine=xlsxwriter是用来替换excel非法字符
    writer.save()


class Counter:
    def __init__(self):
        self.num = 0

    def response(self, flow: mitmproxy.http.HTTPFlow):
        """如果过滤了or 不在域名白名单里面,则直接跳过"""
        if fittler(flow) or not whitelist(flow):
            return None
        self.num += 1
        ctx.log.info(f"{flow.request.url[-100:]}这是我们过滤后接受到的{self.num}个请求上下文")
        ctx.log.info(f"{flow.response.text[-100:]}这是我们过滤后接受到的{self.num}个响应数据")
        FlowsData(flow)


addons = [
    Counter()

]

三:运行脚本:

前提是你安装好了MitmProxy,同时安装好了证书,也就是说你的MitmProxy必须保证可以正常抓包

终端运行脚本

mitmdump -s /Users/xiejiangpeng/python/mygithub/mypython/person/mitmproxj_api/one.py -p 9999

ps:如果端口占用,则修改下端口即可

当然如果没有太多问题的话,你就可以跟我看到一样的输出结果了,这时候,你手机滑动你的目标软件,终端就会输出当前获取到到接口信息是多少条,当达到你设置当最大条数当时候,就会停止保存

ps:(这里暂时小编使用的是根据请求数来进行停止运行,后续可以优化成在终端捕获ctrl+c用户输入来进行触发退出)

如果没有什么问题,在五十个请求之后,你的代理就会自动断开

最后打开我们生成的excel,就可以看到操作触发的接口信息了

 

四:关于参数设置:

根据自己的需求设置即可,如果不想过滤任何东西,就都设置成None就行了

简易实现一个APP录制 自动保存所触发接口信息的小脚本_第1张图片

 

后续:有时候可能会遇到pandas.to_excel写入的时候,会因为一些非法字符报错,这个后续在解决

你可能感兴趣的:(python,小技巧)