• mitmproxy 使用


    mitmproxy 使用

    powershell无法启动虚拟环境

    0. 准备工作

    参考文档:

    什么是mitmproxy?

    • mitmMan-In-The-Middle attack

    • mitmproxy 即为 中间人攻击代理。

    为什么要用mitmproxy?相比Fiddler 和 Charles它有什么优势?

    • mitmproxy 不仅可以截获请求帮助开发者查看、分析,更可以通过自定义脚本进行二次开发。举例来说,利用 Fiddler 可以过滤出浏览器对某个特定 url 的请求,并查看、分析其数据,但实现不了高度定制化的需求,类似于:“截获对浏览器对该 url 的请求,将返回内容置空,并将真实的返回内容存到某个数据库,出现异常时发出邮件通知”。而对于 mitmproxy,这样的需求可以通过载入自定义 python 脚本轻松实现。

    特征

    • 拦截HTTP和HTTPS请求和响应并即时修改它们
    • 保存完整的HTTP对话以供以后重播和分析
    • 重播HTTP对话的客户端
    • 重播先前记录的服务器的HTTP响应
    • 反向代理模式将流量转发到指定的服务器
    • macOS和Linux上的透明代理模式
    • 使用Python对HTTP流量进行脚本化更改
    • 实时生成用于拦截的SSL / TLS证书
    • 还有更多……

    1. 安装

    1.1 模块安装

    安装:

    pip install mitmproxy
    
    • 1

    查看安装成功与否:

    • **cmd**窗口,查看版本
    mitmdump --version
    
    • 1

    出现以下字眼,则是成功安装了。

    Mitmproxy: 5.2
    Python:    3.7.6
    OpenSSL:   OpenSSL 1.1.1g  21 Apr 2020
    Platform:  Windows-10-10.0.18362-SP0
    
    • 1
    • 2
    • 3
    • 4

    1.2 证书安装

    模块安装完成后,首次运行 mitmproxymitmdump,在当前用户下面会生成几个ca证书。

    从**Windows**用户界面的 **.mitmproxy**中,点击进去,可以看到有多个证书,

    证书作用
    mitmproxy-ca.pemPEM格式的证书和私钥
    mitmproxy-ca-cert.pemPEM格式的证书。使用它可以在大多数非Windows平台上分发。
    mitmproxy-ca-cert.p12PKCS12格式的证书。适用于Windows(安装这个
    mitmproxy-ca-cert.cer与.pem相同的文件,但某些Android设备需要扩展名。

    **Windows**端:

    • mitmproxy-ca-cert.p12

    **手机**端:

    • 配置好wifi连接之后,访问 mitm.it
    • 下载对应手机系统的证书,然后安装即可。

    抓包示例:

    • **Windows**端
      • 要使用代理 + 走指定的端口哦!!!
    • **手机**端
      • 配置**Windows**端的ip + 指定代理!!

    2. 组件

    当我们谈论” mitmproxy“时,我们通常指这三种工具中的任何一种–它们只是同一核心代理的不同前端。

    ToolsDescription
    mitmproxy供交互式界面(**Windows**系统不可用
    mitmdump提供简单明了的终端输出
    mitmweb提供基于浏览器的图形界面

    正常使用用**mitmdump**就足够了。

    所以后面的案例也是使用 mitmdump 去做展示。

    **mitmproxy**默认绑定的端口为 127.0.0.1:8080

    注意一下:

    • 如果端口被占用了,会提示报错哦!

    2.1 mitmproxy

    **Windows**系统不可用,这里暂不展示。

    2.2 mitmdump

    查看所有命令:

    mitmdump --help
    
    • 1

    查看版本:

    mitmdump --version
    
    • 1

    常用命令:

    -p 8888			# 指定端口
    -s xxx.py		# 执行指定脚本
    -w outfile		# 指定输出文件
    -q quiet		# 仅匹配脚本过滤后的数据包
    "~m post"		# 仅匹配Post请求
    
    • 1
    • 2
    • 3
    • 4
    • 5

    带有颜色的print:

    • log,带有输出不同颜色的功能(个人觉得没有什么用
      • info 白色
      • warn 黄色
      • error 红色

    注意这里要使用cmd,使用PowerShell显示出来的颜色效果不完整。

    mitmDemoOne.py

    class Demo:
        def request(self, flow: mitmproxy.http.HTTPFlow):
            """Print different colors"""
            url = flow.request.url
            if 'sunrise' in url:
                print(type(url))
                ctx.log.info('Color White:' + url)
                ctx.log.warn('Color Yellow:' + url)
                ctx.log.error('Color Red:' + url)
    
    
    addons = [
        Demo()
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.3 mitmweb

    监听的端口是 127.0.0.1:8080

    同时提供一个 web 交互界面在 127.0.0.1:8081

    用 百度一下 示例。

    介绍:

    • 拦截

      • 修改请求前数据
      • 修改请求后数据
    • 筛选

    • 高亮

    • 重放请求


    3. 简单使用示例

    3.1 使用示例

    正常使用用**mitmdump**就足够了。所以这里主要用 mitmdump 来做一个展示,

    后面的案例也是使用 mitmdump 去做展示。

    基本操作的话,那只看 常规代理 方式就可以了;

    操作模式:https://docs.mitmproxy.org/stable/concepts-modes/

    脚本编写:https://docs.mitmproxy.org/stable/addons-scripting/

    如何工作:https://docs.mitmproxy.org/stable/concepts-howmitmproxyworks/

    测试网站:

    • http://www.httpbin.org/get

    常用的两个函数简单演示:

    这里介绍一下使用的比较多的两个函数,其他的可以通过官方文档去进行一个系统的学习。

    def request(flow):
    	pass
    	
    def response(flow):
    	pass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • request
    commonDescription
    request = flow.request
    request.urlurl
    request.host域名
    request.headers请求头
    request.method方式:POST、GET等
    request.scheme类型:http、https
    request.path路径,URL除域名之外的内容
    request.query返回MultiDictView类型的数据,URL的键值参数
    request.query.keys()获取所有请求参数键值的键
    request.query.values()获取所有请求参数键值的值
    request.query.get('wd')获取请求参数中**wd** 键的值(前提是要有 wb 参数
    request.query.set_all('wd', ['python'])wd 参数的值修改为 python

    修改请求头:

    mitmDemoTwo.py

    flow.request.headers['User-Agent'] = 'Mozilla/5.0'
    
    • 1

    将百度搜索修改为python:

    mitmDemoThree.py

    def request(flow):
    	if 'https://www.baidu.com' in flow.request.url:
    		# 取得请求参数wd的值
    		print(flow.request.query.get('wd'))
            # 获取所有请求参数键值的键
            print(list(flow.request.query.keys()))
            # 获取所有请求参数键值的值
            print(list(flow.request.query.values()))
            # 修改请求参数
            flow.request.query.set_all('wd',['python'])
            # 打印修改过后的参数
            print(flow.request.query.get('wd'))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    • response
    commonDescription
    response = flow.response
    response.status_code响应码
    response.text文本(同下)
    response.contentBytes类型
    response.headers响应头
    response.cookies响应cookie
    response.set_text()修改响应的文本
    response.get_text()文本(同上)
    flow.response= flow.response.make(404)响应404

    修改文本

    mitmDemoFour.py

    flow.response.set_text(text)
    
    • 1

    拒绝响应

    mitmDemoFour.py

    # 同下
    flow.response = mitmproxy.http.HTTPResponse.make(401)
    # 同下
    flow.response= flow.response.make(404)
    
    • 1
    • 2
    • 3
    • 4

    拒绝响应:在百度搜索 十八禁

    mitmDemoFive.py

    if flow.request.query.get('wd') == '十八禁':
        flow.response = mitmproxy.http.HTTPResponse.make(
            404,  									# (optional) status code
            b"You son of a bitch, Please leave.",  	# (optional) content
            {"Content-Type": "text/html"}  			# (optional) headers
        )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    3.1.1 简单应用

    需求

    1. 修改请求(如果是搜索雷锋,则修改为 是小菜一碟吖
    2. 修改响应(将页面所有 Python 字眼 替换为 是小菜一碟吖
    3. 如果存在少儿不宜字眼(例:十八禁、迷药等),则拒绝响应,引导他向好
    
    • 1
    • 2
    • 3

    代码:

    mitmDemoSix.py

    import mitmproxy.http
    from mitmproxy import ctx
    
    
    class Demo:
        def request(self, flow: mitmproxy.http.HTTPFlow):
            """Do somethings"""
            request = flow.request
            if 'https://www.baidu.com/' in request.url:
                keyword = request.query.get('wd')
                filter_words = ['迷药', '十八禁']
                if keyword == '雷锋':
                    # 修改请求参数
                    flow.request.query.set_all('wd', ['是小菜一碟吖'])
                if keyword in filter_words:
                    flow.response = mitmproxy.http.HTTPResponse.make(
                        status_code=400,
                        content=''' 娘希匹!!!
                                    

    警告!!!即将查水表

    望你善良,愿你向上

    点击跳转: 是小菜一碟吖的学习频道 '''
    , # content="你可拉倒吧!!!查询的什么娘希匹玩意儿!!!" headers={"Content-Type": "text/html"} ) def response(self, flow: mitmproxy.http.HTTPFlow): """Do somethings""" response = flow.response if flow.request.host == 'www.baidu.com': replace_words = ['你好', 'python', 'Python'] text = response.get_text() text = list(map(lambda x: text.replace(x, '是小菜一碟吖'), replace_words))[0] flow.response.set_text(text=text) addons = [ Demo() ]
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    当然,这里超纲了,也就是觉得有趣,就拉出来讲一讲。


    3.2 报错解决

    502 Bad Gateway
    Certificate verification error for xxx: unable to get local issuer certificate (errno: 20, depth: 0)
    
    • 1
    • 2

    网关证书验证错误,解决方法有二:

    1. 执行–ssl-insecure
    2. 下载最新的cacert.pem替换( Python安装路径\Lib\site-packages\certifi )的目录证书

    4. 案例展示

    4.1 mitmproxy + Selenium

    电脑端自动化爬虫

    案例说明:

    • Selenium 自动翻页,
    • mitmproxy 进行信息采集,
    • 在指定网站,输入 指定关键词 以及 爬取的页码数量,即可。

    注意点:

    • 评论数量是另外一个文件,需要另外进行解析。
    • 返回评论适量的链接有两个,要区别做判断。

    selenium JD:

    """输入关键词 + 页码数量 Jd自动翻页程序"""
    
    import time
    from selenium import webdriver
    
    
    class JdSpider:
        """OK"""
    
        def __init__(self, keyword=None, page=None):
            self.url = 'https://www.jd.com/'
            self.browser = None
            self.page = int(page)
            self.keyword = keyword
    
        def __del__(self):
            self.browser.close()
    
        def open_browser(self):
            """打开浏览器"""
            self.browser = webdriver.Chrome()
            # self.browser.maximize_window()
            self.browser.set_window_size(1350, 850)
    
        def search_keyword(self):
            '''搜索关键字'''
            self.browser.get(self.url)
            # 输入内容
            self.browser.find_element_by_xpath('//*[@id="key"]').send_keys(self.keyword)
            # 模拟点击
            self.browser.find_element_by_xpath('//*[@id="search"]/div/div[2]/button').click()
    
        def turn_page(self):
            '''翻页'''
            self.browser.execute_script('window.scrollTo(0,document.body.scrollHeight)')
            time.sleep(3)
            if self.browser.page_source.find('pn-next disabled') == -1:
                self.browser.find_element_by_class_name('pn-next').click()
    
        def main(self):
            '''函数启动接口'''
            self.open_browser()
            self.search_keyword()
            for count in range(self.page):
                self.turn_page()
    
    
    if __name__ == '__main__':
        keyword = input("Enter the keywords to search:")
        page = input("Enter the Page to download:")
        spider = JdSpider(keyword=keyword, page=page)
        spider.main()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    网页解析 及 保存:

    import re
    import os
    import csv
    import json
    from lxml import etree
    
    
    def format_common(_list: list):
        """格式化函数"""
        _str = ''.join(_list)
        _str = _str.replace('\n', '').replace('\t', '').replace('¥', '')
        return _str
    
    
    def format_state(_list: list):
        """格式化函数"""
        _str = ' '.join(_list)
        _str = _str.replace('\n', '').replace('\t', '').replace('¥', '')
        return _str
    
    
    class SaveData:
        """OK"""
    
        def __init__(self, data):
            self.comment_data = data[0]
            self.other_data = data[1]
    
        def judge_exists(self, path):
            """判断文件是否已存在"""
            if os.path.exists(path):
                return
            title = ["商铺名称", "说明", "价格", "评价人数", "商品名称"]
            with open(path, 'a+', encoding='utf-8', newline='') as f:
                writer = csv.writer(f)  # 创建写 对象
                writer.writerow(title)  # 写入单行
    
        def save_to_csv(self, data: list):
            """保存为csv"""
            path = r'./data/JdGoodsInfo.csv'
            self.judge_exists(path)
            with open(path, 'a+', encoding='utf-8', newline='') as f:
                writer = csv.writer(f)  # 创建写 对象
                writer.writerows(data)  # 写入多行
    
        def parse_data(self):
            """解析网页"""
            if not self.other_data or not self.comment_data:
                return
    
            comments_item = json.loads(re.findall("jQuery\d+\((.*?)\);", self.comment_data)[0])['CommentsCount']
            if len(comments_item) != 30:
                return
    
            _data = list()
            xpath_html = etree.HTML(self.other_data)
            xpath_items = xpath_html.xpath('//li[@class="gl-item"]')
            for xpath_item, comment_item in zip(xpath_items, comments_item):
                _parse = xpath_item.xpath
                shop = format_common(_parse('.//div[@class="p-shop"]//text()'))
                icons = format_state(_parse('.//div[@class="p-icons"]//text()'))
                price = format_common(_parse('.//div[@class="p-price"]//text()'))
                name = format_common(_parse('.//div[@class="p-name p-name-type-2"]//text()'))
                comment = comment_item['CommentCountStr']
                _data.append((shop, icons, price, comment, name))
            self.save_to_csv(data=_data)
    
        def main(self):
            """开始干活"""
            self.parse_data()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    mitm代码:

    # -*- coding:utf-8 -*-
    # author   : SunriseCai
    # datetime : 2020/11/21 10:47
    # software : PyCharm
    
    import json
    import mitmproxy.http
    from mitmSaveData import SaveData
    
    
    class Demo:
        def __init__(self):
            self.other_data = None
    
        def response(self, flow: mitmproxy.http.HTTPFlow):
            url = flow.request.url
            if 'jd.com' not in url:
                return
            # 商品信息链接
            if 'https://search.jd.com/s_new.php?keyword=' in url:
                self.other_data = flow.response.text or None
                # 评论链接
            if 'https://club.jd.com/comment' in url:
                comment_data = flow.response.text
                SaveData([comment_data, self.other_data]).main()
                comment_data, self.other_data = None, None
    
    
    addons = [
        Demo(),
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    遗留问题:

    • 搜索的首页不是 XHR 形式加载出来的,这个不想做适配了。

    4.2 mitmproxy + Appium

    手机端自动化爬虫

    案例说明:

    • Appium 自动翻页,
    • mitmproxy 进行信息采集,
    • 在指定 App,输入 指定关键词 以及 向下滑动的数量次数,即可。
  • 相关阅读:
    DLG4NLP
    信号量的使用和示例
    Jmeter快速入门
    mybatisPlus逻辑删除注解@TableLogic
    C# 基础面试题(万字)
    java Boolean 比较
    Leetcode 104. 二叉树的最大深度
    大数据课程L2——网站流量项目的算法分析&&数据处理
    2022 MySQL8.0.29安装详细教程(妈妈级)
    【附源码】计算机毕业设计SSM社区新冠疫苗接种管理系统
  • 原文地址:https://blog.csdn.net/m0_37641835/article/details/127853746