效果图
功能
动态抓包,并分析网络接口网络质量,超过限制响应时间,自动触发,推送飞书群
1、支持mac系统和window系统
2、支持飞书机器人通知
3、支持开启代理、关闭代理、安装证书
后续新增功能,待开发… …
实现逻辑
pyqt5界面 + mitmproxy网络抓包 + 分析
proxy_driver代码如下:
import time
import mitmproxy.http
from data.cachedriver import cache
class ProxyDriver:
def __init__(self, urlList):
self.urlList = urlList
self.urlCounter = {}
def request(self, flow: mitmproxy.http.HTTPFlow):
pass
def response(self, flow: mitmproxy.http.HTTPFlow):
for filterUrl in self.urlList:
if filterUrl in flow.request.url:
method = flow.request.method
url = flow.request.url.encode("utf-8").decode("utf-8")
responseTime = (time.time() - flow.request.timestamp_start) * 1000
if url in self.urlCounter.keys():
sumTime = self.urlCounter[url]["avageTime"] * self.urlCounter[url]["count"]
self.urlCounter[url]["count"] += 1
self.urlCounter[url]["avageTime"] = (sumTime + responseTime)/self.urlCounter[url]["count"]
self.urlCounter[url]["maxTime"] = responseTime if responseTime>self.urlCounter[url]["maxTime"] else self.urlCounter[url]["maxTime"]
self.urlCounter[url]["minTime"] = responseTime if responseTime<self.urlCounter[url]["minTime"] else self.urlCounter[url]["minTime"]
else:
self.urlCounter[url] = {"count": 1, "avageTime": responseTime, "maxTime": responseTime, "minTime": responseTime}
self.urlCounter[url]["method"] = method
if self.urlCounter[url]["maxTime"]>=MAXTIME:
cache.add_cache(url, self.urlCounter[url])
else:
return
界面代码
兼容了mac系统和window系统开启关闭代理的不同方式
import platform
import subprocess
from gui.widgetsmain import Ui_MainWindow
from PyQt5.QtWidgets import QMainWindow, QMessageBox, QAbstractItemView, QHeaderView, QMenu, QAction
from PyQt5.QtCore import Qt, QSortFilterProxyModel
from PyQt5.QtGui import QIcon
from applescript import tell
from data.cachedriver import Cache
from data.data_child_thread import DataChildTread
from model.model_response_data import ModelResponseData
from gui.my_qss import *
class WidgetsMain(QMainWindow, Ui_MainWindow):
def __init__(self):
super(WidgetsMain, self).__init__()
self.setupUi(self)
self.pushBotton_connectEvent()
self.choose_system()
self.table_view_config()
self.sheet_style()
def sheet_style(self):
self.setStyleSheet(qss_mainWindow)
def table_view_config(self):
self.dataModel = ModelResponseData()
self.proxymodel = QSortFilterProxyModel(self)
self.proxymodel.setSourceModel(self.dataModel)
self.tableView.setModel(self.proxymodel)
self.tableView.setSortingEnabled(True)
self.tableView.sortByColumn(2,Qt.SortOrder.DescendingOrder)
self.tableView.horizontalHeader().setVisible(True)
self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.tableView.horizontalHeader().setSectionResizeMode(0, QHeaderView.Fixed)
self.tableView.setWordWrap(True)
# 列宽
self.tableView.setColumnWidth(0,420)
self.right_menu_config()
def right_menu_config(self):
self.rightMenu = QMenu(self)
copyAction = QAction("复制选择内容", self)
self.rightMenu.addAction(copyAction)
self.tableView.setContextMenuPolicy(Qt.CustomContextMenu)
self.tableView.setSelectionMode(QAbstractItemView.ContiguousSelection)
self.tableView.customContextMenuRequested.connect(self.slots_right_menu)
def choose_system(self):
if "macOS" in platform.platform():
self.radioButtonMac.setChecked(True)
else:
self.radioButtonWindows.setChecked(True)
def closeEvent(self, event) -> None:
event.accept()
os._exit(0)
def pushBotton_connectEvent(self):
self.pushButtonOpenProxy.clicked.connect(self.slots_open_proxy_pushBotton)
self.pushButtonCloseProxy.clicked.connect(self.slots_close_proxy_pushBotton)
self.pushButtonInstallCer.clicked.connect(self.slots_install_cer)
self.pushButtonstart.clicked.connect(self.slots_start)
self.pushButtonEnd.clicked.connect(self.slots_end)
self.pushButtonClear.clicked.connect(self.slots_clear_model)
def slots_right_menu(self, pos):
self.rightMenu.exec_(self.tableView.mapToGlobal(pos))
def slots_open_proxy_pushBotton(self):
'''开启代理'''
port = self.lineEditPort.text()
if self.radioButtonMac.isChecked():
openhttpcmd = f'''networksetup -setwebproxy "Wi-Fi" 127.0.0.1 {port}'''
openhttpscmd = f'''networksetup -setsecurewebproxy "Wi-Fi" 127.0.0.1 {port}'''
os.system(openhttpcmd)
os.system(openhttpscmd)
elif self.radioButtonWindows.isChecked():
self.window_system_proxy_config(True, "127.0.0.1", int(port))
messagebox = QMessageBox()
messagebox.setWindowTitle("提示")
messagebox.setWindowIcon(QIcon("gui/images/title.png"))
messagebox.setText(f"{port}端口 代理开启成功!")
messagebox.exec_()
def slots_close_proxy_pushBotton(self):
'''关闭代理'''
port = self.lineEditPort.text()
if self.radioButtonMac.isChecked():
closehttpcmd = '''networksetup -setwebproxystate "Wi-Fi" off '''
closehttscmd = '''networksetup -setsecurewebproxystate "Wi-Fi" off'''
os.system(closehttpcmd)
os.system(closehttscmd)
elif self.radioButtonWindows.isChecked():
self.window_system_proxy_config(False, "", 0)
messagebox = QMessageBox()
messagebox.setWindowTitle("提示")
messagebox.setWindowIcon(QIcon("gui/images/title.png"))
messagebox.setText(f"{port}端口 代理已关闭!")
messagebox.exec_()
def slots_install_cer(self):
'''安装证书'''
if self.radioButtonMac.isChecked():
installCommand = '''sudo security add-trusted-cert -d -p ssl -p basic -k /Library/Keychains/System.keychain .mitmproxy/mitmproxy-ca-cert.pem'''
tell.app("Terminal", 'do script"' + installCommand + '"')
elif self.radioButtonWindows.isChecked():
installCommand = 'start .mitmproxy\mitmproxy-ca-cert.p12'
subprocess.run(installCommand, shell=True)
def slots_start(self):
'''开启监控'''
self.thread = DataChildTread()
self.thread.dataSignal.connect(self.dataModel.update_model)
self.thread.start()
port = self.lineEditPort.text()
self.chanage_proxy_driver_filter_url()
basePath = os.getcwd()
activePath = 'venv/bin/activate'
runType = 'mitmweb' if self.checkBoxOpenWeb.isChecked() else 'mitmproxy'
if self.radioButtonMac.isChecked():
startCommand = f'''cd {basePath} && source {activePath} && {runType} -s proxy_driver.py -p {port}'''
tell.app('Terminal', 'do script"' + startCommand + '"')
elif self.radioButtonWindows.isChecked():
startCommand = f'start {runType} -s proxy_driver.py -p {port}'
subprocess.run(startCommand, shell=True)
def slots_end(self):
'''关闭监控'''
self.thread.isRun = False
runType = 'mitmweb' if self.checkBoxOpenWeb.isChecked() else 'mitmproxy'
if self.radioButtonMac.isChecked():
# 关闭监控进程
endCommandA = "ps aux | grep %s | grep -v grep | awk \'{print $2}\' | xargs kill -9" % runType
# 关闭终端
endCommandB = "ps aux | grep MacOS | grep Terminal |awk '{print $2}' | xargs kill -9"
elif self.radioButtonWindows.isChecked():
# 关闭监控进程
endCommandA = f'taskkill /F /IM {runType}.exe'
endCommandB = ''
# 关闭终端
os.system(endCommandA)
os.system(endCommandB)
messagebox = QMessageBox()
messagebox.setWindowTitle("提示")
messagebox.setWindowIcon(QIcon("gui/images/title.png"))
messagebox.setText(f"已关闭监控进程: {runType}")
messagebox.exec_()
def slots_clear_model(self):
'''清空缓存和model'''
self.dataModel.update_model([])
Cache.clean_all_cache()
cache = Cache(".data")
def chanage_proxy_driver_filter_url(self):
'''
修改驱动文件
:return:
'''
fiterUrls = self.plainTextEdit.toPlainText()
maxFileterTime = self.lineEditTime.text()
fiterUrls = fiterUrls.split("\n")
with open("proxy_driver.py", 'r', encoding='utf=8') as file:
olddata = file.readlines()
olddata[0] = f"MAXTIME = {int(maxFileterTime)}\n"
olddata[-1] = f"addons = [ProxyDriver({fiterUrls})]"
with open("proxy_driver.py", 'w', encoding='utf=8') as file:
file.write(''.join(olddata))
def window_system_proxy_config(self, enable: bool, server: str, port: int):
'''
window 系统开启/关闭网络代理
:param enable: 是否开启
:param server: ip
:param port: 端口
:return:
'''
import winreg
# 打开注册表编辑器
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER,
r"Software\Microsoft\Windows\CurrentVersion\Internet Settings",
0, winreg.KEY_WRITE)
# 开启代理
if enable:
winreg.SetValueEx(key, "ProxyEnable", 0, winreg.REG_DWORD, 1)
winreg.SetValueEx(key, "ProxyServer", 0, winreg.REG_SZ, f"{server}:{port}")
# 关闭代理
else:
winreg.SetValueEx(key, "ProxyEnable", 0, winreg.REG_DWORD, 0)
winreg.SetValueEx(key, "ProxyServer", 0, winreg.REG_SZ, "")
# 关闭注册表编辑器
winreg.CloseKey(key)
通过此工具,可解决测试中对项目所有接口的质量分析