Mitmproxy - Python抓包神器

做App爬虫、Web接口调试的时候,你是不是经常遇到这些痛点?

  • Fiddler:主打Windows,Mac/Linux体验一般,扩展性有限;
  • Charles:功能全面但收费不菲;
  • tcpdump/Wireshark:纯命令行操作,手动筛选HTTP/HTTPS流量繁琐且不直观。

Mitmproxy 完美填补了这些空白——它是一款基于Python编写的免费开源、跨平台、支持Python脚本深度定制的交互式HTTP/HTTPS代理。无论是临时抓接口,还是搭建自动化爬取/流量修改管线,它都是开发者与爬虫er的首选神器。

本文将带你从安装、基础模式,到动手打造一个App定向流量拦截脚本,最后分享一些实战中总结出的最佳实践,一次搞定Mitmproxy的核心用法。


一、安装与三种运行模式

Mitmproxy 核心提供了三个独立工具,覆盖从可视化操作自动化脚本的不同场景:

1.1 安装

只需一条 pip 命令即可,建议使用 Python 3.8 及以上版本:

pip install mitmproxy

1.2 模式一览

# 模式1:Web可视化模式(新手友好首选)
# 启动后自动打开浏览器访问 http://127.0.0.1:8081 进行操作
mitmweb -p 8081

# 模式2:纯命令行交互模式(适合终端控/临时调试)
# 终端内带彩色UI,支持类Vim快捷键浏览流量
mitmproxy -p 8082

# 模式3:自动化脚本模式(核心功能,本文重点)
# 直接运行自定义Python脚本,批量处理流量
mitmdump -s your_script.py -p 8080

三种模式各有侧重:mitmweb 适合新手快速查看请求;mitmproxy 让老手在终端中高效操作;mitmdump 则是自动化处理流量的基石。下面我们通过脚本实战深入第三种模式。


二、实战脚本:App定向流量拦截与分析

下面这个脚本是一个为App爬虫量身打造的核心工具,它具备以下能力:

  • 自动识别包含 apimobileappsdkanalytics 等关键词的请求;
  • 完整提取请求与响应数据(包括 JSON、Headers、Query 参数);
  • 自动将结果保存到本地 JSON 文件;
  • 统计 API 调用次数及异常状态码。

将代码保存为 app_traffic_analyzer.py 即可使用。

from mitmproxy import http, ctx
import json
import re
import time
import os
from datetime import datetime
from typing import Dict, Any

class AppTrafficAnalyzer:
    """App定向流量拦截与分析器"""

    def __init__(self):
        self.intercepted_data = []
        # 可根据目标应用/网站的域名特征自由添加、删减
        self.target_features = {
            'domains': [r'.*api\..*', r'.*mobile\..*', r'.*app\..*', r'.*sdk\..*'],
            'ua_keywords': ['mobile', 'android', 'ios', 'app'],
            'path_keywords': ['/api/', '/mobile/', '/sdk/', '/v1/', '/v2/']
        }
        self.output_dir = 'mitmproxy_analysis'
        os.makedirs(self.output_dir, exist_ok=True)

    def request(self, flow: http.HTTPFlow) -> None:
        """请求拦截入口"""
        if not self._is_target_flow(flow):
            return

        req_info = {
            'type': 'request',
            'timestamp': datetime.fromtimestamp(flow.request.timestamp_start).isoformat(),
            'method': flow.request.method,
            'url': flow.request.pretty_url,
            'host': flow.request.host,
            'path': flow.request.path,
            'query': dict(flow.request.query) if flow.request.query else {},
            'headers': dict(flow.request.headers),
            'text': flow.request.text if flow.request.content else ''
        }
        self.intercepted_data.append(req_info)
        ctx.log.info(f"🎯 捕获目标请求: {flow.request.pretty_url}")

    def response(self, flow: http.HTTPFlow) -> None:
        """响应拦截入口"""
        if not self._is_target_flow(flow):
            return

        resp_info = {
            'type': 'response',
            'timestamp': datetime.fromtimestamp(flow.response.timestamp_end).isoformat(),
            'status_code': flow.response.status_code,
            'url': flow.response.url,
            'headers': dict(flow.response.headers),
            'text': flow.response.text if flow.response.content else ''
        }
        self.intercepted_data.append(resp_info)
        ctx.log.info(f"📡 捕获目标响应: {flow.response.url} - {flow.response.status_code}")

    def _is_target_flow(self, flow: http.HTTPFlow) -> bool:
        """判断是否为目标流量"""
        # 检查域名
        for pattern in self.target_features['domains']:
            if re.search(pattern, flow.request.host, re.IGNORECASE):
                return True

        # 检查User-Agent
        ua = flow.request.headers.get('User-Agent', '').lower()
        if any(k in ua for k in self.target_features['ua_keywords']):
            return True

        # 检查路径
        path = flow.request.path.lower()
        if any(k in path for k in self.target_features['path_keywords']):
            return True

        return False

    def done(self) -> None:
        """mitmdump停止时自动执行:保存所有拦截数据"""
        output_file = os.path.join(self.output_dir, f"traffic_{int(time.time())}.json")
        with open(output_file, 'w', encoding='utf-8') as f:
            json.dump(self.intercepted_data, f, ensure_ascii=False, indent=2)

        ctx.log.info(f"✅ 分析完成,结果已保存到: {output_file}")

# 必须添加到addons列表,供mitmproxy识别
addons = [AppTrafficAnalyzer()]

三、完整使用流程

从启动代理到查看结果,只需三步。

3.1 启动脚本代理并配置设备

(1)启动 mitmdump

在终端执行以下命令,载入刚才编写的脚本并监听 8080 端口:

mitmdump -s app_traffic_analyzer.py -p 8080

请牢记代理运行的设备 IP

  • 如果在本机抓浏览器流量,IP 就是 127.0.0.1
  • 如果要抓手机 App,需要填写电脑在局域网中的 IP(Windows 用 ipconfig,Mac/Linux 用 ifconfig 查看)。

(2)为浏览器或手机配置代理

  • 浏览器:进入浏览器设置 → 网络 → 代理,选择手动配置 HTTP/HTTPS 代理,填入 IP 和端口 8080
  • 手机
    • iOS:设置无线局域网 → 点击当前连接的 Wi-Fi 右侧的 配置代理手动,填入 IP 和端口。
    • Android:设置WLAN → 长按当前 Wi-Fi → 修改网络高级选项代理手动,填入 IP 和端口。

3.2 安装并信任 Mitmproxy CA 证书(抓取 HTTPS 必做)

配置好代理后,在浏览器或手机浏览器中访问 mitm.it,按提示下载对应平台的证书并进行信任。

  • iOS 特别注意

    1. 安装描述文件后,前往 设置通用VPN 与设备管理 信任该描述文件。
    2. 再到 设置通用关于本机证书信任设置完全信任 Mitmproxy CA 证书。
  • Android 7.0+ 特别注意: 系统默认不信任用户安装的证书,有两种主流方案:

    1. 非 Root 友好型:使用 VirtualXposed、太极等虚拟环境,将目标 App 放入虚拟环境中,仅信任虚拟环境的用户证书。
    2. Root 方案:将下载的证书文件放入 /system/etc/security/cacerts/ 目录,权限设为 644

3.3 开始抓包并查看结果

配置完成后,正常操作目标 App 或网页。mitmdump 的终端会实时打印出捕获的请求与响应日志。

抓包结束后,按 Ctrl+C 停止脚本,前往 mitmproxy_analysis 目录,就能看到按时间戳命名的 JSON 文件,里面包含了所有命中的请求和响应数据,方便进一步分析。


四、进阶与最佳实践

  1. 精准定制目标特征
    脚本中的 target_features 涵盖了常见 App 接口特征,但在实际场景中建议根据具体目标微调。可以先在 mitmweb 中快速抓一波流量,锁定目标 App 的固定域名、UA 特征或路径关键词,再将其填入脚本中,避免干扰。

  2. 功能扩展
    你可以在 requestresponse 方法中添加更多逻辑,例如:

    • 动态修改请求头或参数,测试接口边界;
    • 自动提取 JSON 中的关键字段(如 Token、商品列表、用户信息);
    • 将数据实时写入 MongoDB、MySQL 等数据库,构建立持续积累的分析库。
  3. 移动端抓包避坑指南

    • iOS 设备务必与代理机器在同一局域网,并已完成证书信任;
    • Android 应用如果开启了 SSL Pinning(证书绑定),常规代理将无法抓取 HTTPS 请求。此时可尝试使用 Frida 脱壳并 Hook SSL Pinning 验证函数,或借助 VirtualXposed 等工具绕过。

掌握以上方法,Mitmproxy 就能成为你手中最灵活的网络分析利器。无论是临时调试接口,还是构建自动化爬虫管线,它都能提供强大而自由的网络流量分析能力。快打开终端,用一段 Python 脚本解锁你的专属抓包体验吧!