App 爬虫神器?Mitmproxy 快速带你入坑!

你好,我是悦创。

最近在搞 App 爬虫,不过万事入门难,我得自己研究研究 Mitmproxy 虽然不知道到底是不是那么强啦,就是得要会用,所以我就亲自入坑,并把自己的一下观点比较记录下来,毕竟好记性不然烂笔头嘛。

Mitmproxy 是一个支持HTTP和HTTPS的抓包程序,有类似 Fiddler、Charles 的功能,只不过它是一个控制台的形式操作。

Mitmproxy 还有两个关联组件。一个是 Mitmdump,它是 Mitmproxy 的命令行接口,利用它我们可以对接Python 脚本,用 Python 实现监听后的处理。另一个是 Mitmweb,它是一个 Web 程序,通过它我们可以清楚观察 Mitmproxy 捕获的请求。

下面我们来了解它们的用法。

1. 准备工作

1.1 抓包工具

请确保已经正确安装好了 Mitmproxy,并且手机和PC处于同一个局域网下,同时配置好了Mitmproxy的 CA 证书。

这里我简单的来讲一下如何安装,在正式安装之前,我们先来看看 Mitmproxy 需要一个怎样的安装环境:

  1. 基于 Python 环境(我电脑使用的是:Python 3.7)
  2. Windows 操作系统中需要安装 Microsoft Visual C++ V14.0 以上(否则会出现保存无法安装)
  3. Linux 操作系统则直接基于 Python 安装即可

首先,我们需要打开我们的命令行工具 CMD ,直接在命令行命令输入以下命令:

pip install mitmproxy

回车就可以安装了,但是,对于一些新手来讲,有可能不会安装,而网络上的安装教程也已经过时,这里大家可以参考这篇,我 2020 年5月新鲜出炉的,链接:Mitmproxy 安装

对于,有些小伙伴还想安装 Fiddler 这里我也编写了最新的安装教程,可以点击这里:Fiddler 配置及简单操作

1.2 安卓模拟器

为什么要手机模拟器,如果有条件正好有不使用的安卓手机,任何操作都有一定风险,抓取数据之前为了创造一个干净的环境,要进行刷机,适合一个合适的安卓版本,需要格式,所以使用安卓模拟机更方便,性价比也更高。使用安卓模拟机可以随意的定制各种安卓的版本:小米的,华为的,三星的等。方便操作。

注意这里是开发用的安卓模拟器,可不是玩游戏的多开使用的。

模拟器名称 支持的操作系统 支持的虚拟机 运行速度 安装APK方式 支持的机型 调试难易程度
google官方的AVD Windows/linux QEMU ADB 复杂
genumotion Windows/Mac/Linux VirtualBox 一般 ADB/拖拽 一般
夜神模拟器 Windows/Mac VirtualBox ADB/拖拽 简单

夜神模拟器

  • 官网

https://www.yeshen.com/

  • 介绍

夜神安卓模拟器(夜神模拟器),是全新一代的安卓模拟器,与传统安卓模拟器相比,基于ANDROID4.4.2,兼容X86/AMD,在性能、稳定性、兼容性等方面有着巨大优势。优质游戏的提供,软硬件游戏辅助的支持,让用户体验到更强的娱乐性。

因为,本文主要是写 Mitmproxy ,所以对于环境的安装就是提一下,详细安装可以去我博客阅读相关文章,2020版本安装夜神模拟器。

2. Mitmproxy 的功能

Mitmproxy 有如下几项功能。

  • 拦截HTTP和HTTPS请求和响应。
  • 保存HTTP会话并进行分析。
  • 模拟客户端发起请求,模拟服务端返回响应。
  • 利用反向代理将流量转发给指定的服务器。
  • 支持Mac和Linux上的透明代理。
  • 利用Python对HTTP请求和响应进行实时处理。

3. 抓包原理

和Charles一样,mitmproxy运行于自己的PC上,mitmproxy会在PC的8080端口运行,然后开启一个代理服务,这个服务实际上是一个HTTP/HTTPS的代理。

手机和PC在同一个局域网内,设置代理为mitmproxy的代理地址,这样手机在访问互联网的时候流量数据包就会流经mitmproxy,mitmproxy再去转发这些数据包到真实的服务器,服务器返回数据包时再由mitmproxy转发回手机,这样mitmproxy就相当于起了中间人的作用,抓取到所有Request和Response,另外这个过程还可以对接mitmdump,抓取到的Request和Response的具体内容都可以直接用Python来处理,比如得到Response之后我们可以直接进行解析,然后存入数据库,这样就完成了数据的解析和存储过程。

4. 设置代理

首先,我们需要运行 mitmproxy,命令如下所示:

启动 mitmproxy 的命令如下:

mitmproxy

之后会在8080端口上运行一个代理服务,如下图所示。

右下角会出现当前正在监听的端口。

或者启动 mitmdump,它也会监听 8080 端口,命令如下所示:

mitmdump

运行结果如下图所示。

将手机和 PC 连接在同一局域网下,设置代理为当前代理。首先看看PC的当前局域网IP。

Windows 上的命令如下所示:

ipconfig

Linux 和 Mac 上的命令如下所示:

ifconfig

输出结果如下图所示。

Linux/Mac:

Windows:

一般类似 10.*.*.*172.16.*.*192.168.1.* 这样的 IP 就是当前 PC 的局域网 IP,例如此图中 (Linux)PC 的 IP 为192.168.220.130,手机代理设置类似如下图所示。

这样我们就配置好了 mitmproxy 的的代理。

5. Mitmproxy 的使用

5.1 安装证书

确保 mitmproxy 正常运行,并且手机和 PC 处于同一个局域网内,设置了 mitmproxy 的代理,具体的配置方法可以参考官方文档。

运行 mitmproxy,命令如下所示:

mitmproxy

设置成功之后,我们只需要在手机浏览器上访问任意的网页或浏览任意的 App 即可。

不过还要安装证书才可以使用,操作方法启动 mitmproxy 之后,在手机上也设置代理之后,打开浏览器输入:mitm.it 下载手机平台的证书并安装。操作如下动图(我会把代理设置也会重新操作一遍):

5.2 测试抓包

例如在手机上打开 https://www.aiyc.top/ ,mitmproxy 页面便会呈现出手机上的所有请求,如下图所示。

这就相当于之前我们在浏览器开发者工具监听到的浏览器请求,在这里我们借助于 mitmproxy 完成。Charles 完全也可以做到。

这里是刚才手机打开 AI悦创页面时的所有请求列表,左下角显示的 1/36 代表一共发生了 36 个请求,当前箭头所指的是第一个请求。

每个请求开头都有一个 GET 或 POST,这是各个请求的请求方式。紧接的是请求的 URL。第二行开头的数字就是请求对应的响应状态码,后面是响应内容的类型,如 text/html 代表网页文档、image/gif 代表图片。再往后是响应体的大小和响应的时间。

当前呈现了所有请求和响应的概览,我们可以通过这个页面观察到所有的请求。

5.3 详细操作

5.3.1 查看详细请求

如果想查看某个请求的详情,我们可以敲击回车,进入请求的详情页面,如下图所示。

可以看到 Headers 的详细信息,如 Host、Cookies、User-Agent 等。

最上方是一个 Request、Response、Detail 的列表,当前处在Request这个选项上。这时我们再点击 TAB 键,即可查看这个请求对应的响应详情,如下图所示。

最上面是响应头的信息,下拉之后我们可以看到响应体的信息。针对当前请求,响应体就是网页的源代码。

这时再敲击 TAB 键,切换到最后一个选项卡 Detail,即可看到当前请求的详细信息,如服务器的 IP 和端口、HTTP 协议版本、客户端的 IP 和端口等,如下图所示。

5.3.2 Mitmproxy 编辑

Mitmproxy 还提供了命令行式的编辑功能,我们可以在此页面中重新编辑请求。

敲击 e 键即可进入编辑功能,这时它会询问你要编辑哪部分内容,如 Cookies、Query、URL 等,每个选项的第一个字母(或数字)会高亮显示。

敲击要编辑内容名称的首字母(或数字)即可进入该内容的编辑页面,如敲击 5 即可编辑请求的方式,敲击 6 即可修改 GET 请求参数 Query 。

这时我们敲击 6,进入到编辑 Query 的页面。我们可以看到已经有一些参数了,我们可以敲击 a 来增加一行,然后就可以输入参数对应的 Key 和 Value,如下图所示。

这里我们输入 Key 为 wd,Value 为 www.aiyc.top。

然后再敲击 esc 键和 q 键,返回之前的页面,再敲击 e 和 4 键修改 Path 。和上面一样,敲击 a 增加 Path 的内容,这时我们将 Path 修改为 aiyuechuang,如下图所示。

再敲击 esc 和 q 键返回,我们就再修改一下请求,也是敲击 e 和 5 修改 method,这个时候会出现如下结果,操作如下动图:

这时我们可以看到最上面的请求链接变成了:https://www.aiyc.top/usr/themes/handsome/libs/Get.php/aiyuechuang?type=song&wd=www.aiyc.top&media=netease&id=1363948882

敲击 a 保存修改,敲击 r 重新发起修改后的请求,即可看到上方请求方式前面多了一个回旋箭头,这说明重新执行了修改后的请求。这时我们再观察响应体内容,即可看到请求失败,因为我的博客是不存在这个请求链接的,如下图所示。

以上内容便是 mitmproxy 的简单用法。利用 mitmproxy ,我们可以观察到手机上的所有请求,还可以对请求进行修改并重新发起。

接下来,我们对编辑操作进行总结:

键名 功能
e 进入 Part
a 增加一行

5.4 Mitmproxy 快捷键

主界面操作快捷键:

快捷键 功能
? 帮助文档
q 返回/退出程序
b 保存 response body
f 输入过滤条件
k
j
h
l
space 翻页
enter 进入接口详情
z 清屏
e 编辑
r 重新请求

5.5 Mitmproxy 过滤表达式(Filter expressions)

过滤请求,和上面的操作类似,只不过接下来的操作,是按键 f 开始,输入 f 之后你会看见如下位置会有相对应的显示:

接下来,我们就可以过滤请求了,这个地方我们来筛选请求结果不是 200 的(也就是得到请求不是 200 的结果),我们需要添加的命令为: !(~c 200) 也就是如下命令:

: set view_filter '!(~c 200)'

详细的呢,我们来下面来讲:

5.5.1 Filter expressions

Many commands in the mitmproxy tool make use of filter expressions. Filter expressions consist of the following operators:

mitmproxy 工具中的许多命令都使用了过滤表达式。过滤器表达式由以下操作符组成:

注意: 这里把英文写出来不是为了炫技,而是希望看见这篇的小伙伴也顺便学习一下英文,英文真的很重要!

命令(The command) 作用(Role) 翻译(translate)
~a Match asset in response: CSS, Javascript, Flash, images. 匹配响应中的:CSS、JavaScript、Flash、Images。
~b regex Body 使用正则表达式,匹配响应体 Body
~bq regex Request body 请求体
~bs regex Response body 响应体
~c int HTTP response code 匹配 HTTP 状态码
~d regex Domain 域名(可以理解成 url,但不准确)
~dst regex Match destination address 匹配目的地址
~e Match error 匹配错误
~h regex Header 匹配头部
~hq regex Request header 匹配请求头
~hs regex Response header 匹配响应头
~http Match HTTP flows 匹配 HTTP 流
~m regex Method 匹配模式(方法)
~marked Match marked flows 匹配标记流
~q Match request with no response 匹配请求无响应
~s Match response 匹配响应
~src regex Match source address 匹配源地址
~t regex Content-type header 内容类型的头
~tcp Match TCP flows 与 TCP 流
~tq regex Request Content-Type header 请求的 content - type 头
~ts regex Response Content-Type header 响应的 content - type 头
~u regex URL URL
~websocket Match WebSocket flows 匹配 WebSocket 流量
! unary not not
& and and
| or or
(...) grouping 分组
  • Regexes are Python-style
  • Regexes can be specified as quoted strings
  • Header matching (~h, ~hq, ~hs) is against a string of the form “name: value”.
  • Strings with no operators are matched against the request URL.
  • The default binary operator is &.

我想抓取所有跟 aiyc.top 相关的数据包,该如何过滤呢?

使用如下命令:

# 命令一
~u aiyc.top

# 命令二:
~d aiyc.top

接下来,如果我们想查询包含我们 aiyc.top 的,并且请求的模式是 post 请求的,如何来写呢?

~m post & ~d aiyc.top

这样就可以同时匹配到了。

5.5.2 断点拦截(请求拦截)

接下来,我来分享一下如何使用 mitmproxy 实现断点拦截,和上面一样,我们可以在我们的数据流框里面输入一个按键:i ,比如我们要拦截域名是 baidu.com 的,并且请求模式是 get 请求的,我们输入 i 之后,编写的内容:

~d baidu.com & ~m get

前面没有把命令截图 给大家看,这次我就截图一下:

然后敲一下回车,视频如下:

上面,我们修改了原本请求的百度,返回了腾讯,修改之后,输入 a 回复;

当然,我们还可以修改响应体,像如下操作:

6. Mitmdump 的使用

这时候有人会说,我用 Fiddler、Charles 也有这个功能,而且它们的图形界面操作更加方便。那么 Mitmproxy 的优势何在?

Mitmproxy 的强大之处体现在它的另一个工具 mitmdump,有了它我们可以直接对接 Python 对请求进行处理,下面我们来看看 mitmdump 的用法。

Mitmdump 是 Mitmproxy 的命令行接口,同时还可以对接 Python 对请求进行处理,这是相比 Fiddler、Charles 等工具更加方便的地方。有了它我们可以不用手动截获和分析 HTTP 请求和响应,只需写好请求和响应的处理逻辑即可。它还可以实现数据的解析、存储等工作,这些过程都可以通过 Python 实现。

简而言之:Mitmdump 命令最大的特点就是可以自定义脚本,你可以在脚本中对请求或者响应内容通过编程的方式来控制,实现数据的解析、修改、存储等工作

6.1 实例引入

我们可以使用命令启动 Mitmproxy ,并把截获的数据保存到文件中,命令如下所示:

mitmdump -w outfile

其中outfile的名称任意,截获的数据都会被保存到此文件中。

指定启动监听得端口:

mitmdump -p 8889

# 当然,我还可以添加保存,可以一起添加:
mitmdump -p 8889 -w outfile

# 添加脚本
mitmdump -p 8889 -w outfile -s cralwer.py

还可以指定一个脚本来处理截获的数据,使用-s参数即可:

mitmdump -s crawler.py

这里指定了当前处理脚本为 crawler.py,它需要放置在当前命令执行的目录下。

我们可以在脚本里写入如下的代码:

# file: data.py
# 示例一
def request(flow):
    flow.request.headers['User-Agent'] = 'MitmProxy'
    print(flow.request.headers)

# 示例二
def request(flow):
    """
    :function_name must:request
    :param flow:
    :return: None
    """
    print(flow.request.headers)

示例一,我们定义了一个request()方法,参数为flow,它其实是一个HTTPFlow对象,通过request属性即可获取到当前请求对象。然后打印输出了请求的请求头,将请求头的 User-Agent 修改成了 MitmProxy。

运行之后我们在手机端访问 http://httpbin.org/get,可以看到如下情况发生。

手机端的页面显示如下图所示。

手机端返回结果的 Headers 实际上就是请求的 Headers,User-Agent 被修改成了 Mitmproxy。PC 端控制台输出了修改后的 Headers 内容,其 User-Agent 的内容正是 mitmproxy。

所以,通过这三行代码我们就可以完成对请求的改写。print()方法输出结果可以呈现在 PC 端控制台上,可以方便地进行调试。

示例二,就是单纯的输出请求头。

但是,上面的请求刚刚好,是只有一个 headers 但有时候往往是还有有一些其他的后台请求,然后 headers 也会拦截到并且输出。那我们该如何区分哪个是哪个呢?别急,我们下面就来讲 mitmproxy 的日志功能。

6.2 日志输出

Mitmdump 提供了专门的日志输出功能,可以设定不同级别以不同颜色输出结果。我们把脚本修改成如下内容:

# 示例一
from mitmproxy import ctx

def request(flow):
    flow.request.headers['User-Agent'] = 'MitmProxy'
    ctx.log.info(str(flow.request.headers)) # 灰色
    ctx.log.warn(str(flow.request.headers)) # 黄色
    ctx.log.error(str(flow.request.headers)) # 红色


# 补充代码
"""
project = 'Code', file_name = 'mitmdump_test', author = 'AI悦创'
time = '2020/5/20 8:19', product_name = PyCharm, 公众号:AI悦创
code is far away from bugs with the god animal protecting
    I love animals. They taste delicious.
"""
from mitmproxy import ctx
def request(flow):
    """
    :function_name must:request
    :param flow:
    :return: None
    """
    # print(flow.request.headers)
    flow.request.headers['User-Agent'] = 'MitmProxy'
    ctx.log.info(str(flow.request.headers))  # 灰色
    ctx.log.warn(str(flow.request.headers))  # 黄色
    ctx.log.error(str(flow.request.headers))  # 红色
    ctx.log.error(str(flow.request.url)) # 输出 url
    ctx.log.error(str(flow.request.host))
    ctx.log.error(str(flow.request.method))
    ctx.log.error(str(flow.request.path))

这里调用了 ctx 模块,它有一个 log 功能,调用不同的输出方法就可以输出不同颜色的结果,以方便我们做调试。例如,info()方法输出的内容是白色的,warn()方法输出的内容是黄色的,error()方法输出的内容是红色的。运行结果如下图所示。

不同的颜色对应不同级别的输出,我们可以将不同的结果合理划分级别输出,以更直观方便地查看调试信息。

补充:如果你在使用 Windows 抓包之后,转换到 Linux 中测试,手机中修改 ip 地址后,还是出现如下报错。

所以这里要注意的就是,我们的 mitmproxy 一更换 ip 地址就需要重新安装证书(当然,如果相同的 ip 地址之前安装过了,就不会出现这样的问题)

6.3 Request

最开始我们实现了request()方法并且对 Headers 进行了修改。

下面我们来看看 Request 还有哪些常用的功能。我们先用一个实例来感受一下。

from mitmproxy import ctx

def request(flow):
    request = flow.request
    info = ctx.log.info
    info(request.url)
    info(str(request.headers))
    info(str(request.cookies))
    info(request.host)
    info(request.method)
    info(str(request.port))
    info(request.scheme)

我们修改脚本,然后在手机上打开AI悦创:https://www.aiyc.top/ ,即可看到 PC 端控制台输出了一系列的请求,在这里我们找到第一个请求。控制台打印输出了 Request 的一些常见属性,如 URL、Headers、Cookies、Host、Method、Scheme等。输出结果如下图所示。

结果中分别输出了请求链接、请求头、请求 Cookies、请求 Host、请求方法、请求端口、请求协议这些内容。

同时我们还可以对任意属性进行修改,就像最初修改 Headers 一样,直接赋值即可。

例如,这里将请求的URL修改一下,脚本修改如下所示:

def request(flow):
    url = 'https://httpbin.org/get'
    flow.request.url = url

手机端得到如下结果,如下图所示。

比较有意思的是,浏览器最上方还是呈现百度的URL,但是页面已经变成了httpbin.org的页面了。另外,Cookies明显还是百度的Cookies。我们只是用简单的脚本就成功把请求修改为其他的站点。通过这种方式修改和伪造请求就变得轻而易举。

通过这个实例我们知道,有时候URL虽然是正确的,但是内容并非是正确的。我们需要进一步提高自己的安全防范意识。

Request 还有很多属性,在此不再一一列举。更多属性可以参考:https://docs.mitmproxy.org/stable/

只要我们了解了基本用法,会很容易地获取和修改Reqeust的任意内容,比如可以用修改Cookies、添加代理等方式来规避反爬。

6.4 Response

对于爬虫来说,我们更加关心的其实是 Response 的内容,因为 Response Body 才是爬取的结果。对于Response来说,mitmdump也提供了对应的处理接口,就是response()方法。下面我们用一个实例感受一下。

from mitmproxy import ctx

def response(flow):
    response = flow.response
    info = ctx.log.info
    info(str(response.status_code))
    info(str(response.headers))
    info(str(response.cookies))
    info(str(response.text))

将脚本修改为如上内容,然后手机访问:http://httpbin.org/get。

这里打印输出了Response的status_codeheaderscookiestext这几个属性,其中最主要的text属性就是网页的源代码。

PC端控制台输出如下图所示。

控制台输出了 Response 的状态码、响应头、Cookies、响应体这几部分内容。

我们可以通过response()方法获取每个请求的响应内容。接下来再进行响应的信息提取和存储,我们就可以成功完成爬取了。

补充代码:

# crawler.py
from mitmproxy import http

def request(flow: http.HTTPFlow) -> None:
    # 将请求新增了一个查询参数
    flow.request.query["mitmproxy"] = "rocks"

def response(flow: http.HTTPFlow) -> None:
    # 将响应头中新增了一个自定义头字段
    flow.response.headers["newheader"] = "foo"
    print(flow.response.text)

当你在浏览器请求 http://httpbin.org/get ,看到的效果:

你还可以参考这些链接:

  • 官方文档:https://docs.mitmproxy.org/stable/
  • GitHub地址:https://github.com/mitmproxy/mitmproxy
  • 更多脚本例子:https://github.com/mitmproxy/mitmproxy/tree/master/examples/simple
  • 如何在Chrome浏览器设置代理:https://jingyan.baidu.com/article/e52e3615a3ef8e40c60c510f.html

7. Mitmweb

mitmweb

启动 Mitmweb 命令后,会有一个类似 Chrome 开发者工具的Web页面,功能上类似 Mitmroxy,一样可以查看每个请求的详情,包括请求、响应,还可以对请求和响应内容进行修改,包括过滤、重新发送请求等常用功能。

AI悦创·创造不同!
AI悦创 » App 爬虫神器?Mitmproxy 快速带你入坑!

Leave a Reply