我们通常使用的抓包工具就是Fiddler和Charles这种图形化的,Charles的优点是跨平台,Windows和Mac都可以使用,Fiddler的优点是功能“极其”] ^ - H强大,不仅拥C F 4 4 Y Y )有抓包功能,还拥有中间人攻击的功能,但是使用成本太高了,我们做爬虫? { H a j O d R开发,使用到Fiddler的功能不过十之二三罢了。今天我们主8 X ; b e O要讲的是mitmproxy这款工具,这是一款专业的中间人攻击工具,mitmproxy 不仅可以截获请求帮m n 6 Q H U S 6助开发者查看、分析,最最重要的是支持E - w e N ~ f u ^Python进行定制化二次开发。例如:截获浏览器的请求内容,并将Z d 9 R ` b ^ =数据处理后存储到数据库,再将内容交给浏览器;如果出现异常时,发出邮件通知,并返回给浏览器一个空的页面。mitmproxy有以下几个特点:
- 像正常代理一样转发请求,保证服务器和客户端之间的通信
- 可以拦截请求/返回,并可以修改请求/返回
- 可以加载Pa [ % + { g :ython脚本执行
安装mia - tmproxy
pip install miw # C L * . ]tmproxy
在Python环境中安装使用pip最为简洁。mitm6 } A _ & Y 4 %proxy安装完成以后会包含三个工具:mitmproxy、mitmdump、mitmweb。安装完成以后直t H l { a ; 7 f接在控制台输入mitmproxy --version就可以查看版本信息。
查看版本信息
注意如果是在Windows系统中安装,需要先安装Microsoft Visual C++ V14.0以上版本,并且mitmproxy是不能在Windows系统中进行抓包的,在执行mitmproxy --version命令的时候会得到一个错误提示。
Error: mitX v @ 1mproxy\'s cg ? 7 sonsole interface is not supported on Windows. You can run mitmdump orZ G f E G + B N mitmweb instead.
在Windows系统中我们主要使% d用的是安装完以后的另外两个工具mitmdump和mitmweb。| * X ~
安装浏览器代理插件SwitchyOmega
为什么G _ { B L Z =要先安装浏览器代理插件呢?因为我们在使用抓包工具的时候,必须要通过代理访问网络,才能抓到包,可以通过设置系统代理的方式来实现,但是直接设置浏览器代理会更加方便,而且使用代理插件我们可以配A R )置多种代理模式。Chrome浏览器安装插件需要科学上网, g z W只要在度娘搜索谷歌上网助手,安装以后重启浏览器,就可以访问谷歌商Z X / S a 2 k店来安装插件了,插件我们这里推荐SwitchyOmega。安装完以后要进行设置。
- 打开选项
打开设置项 - 新建情景模式
- 设置代理地址和端口
然后在浏览器中访问地址前,先选择代理方式,再进行访问
安装证书
正常情况下,mitmproxy启动后,} l J _ y - s W只能抓取到HTTP请求的% c r信息,我们要抓取HTTPS请求e ! * & o Z 3 =信息需要安装证书。证书安装有两种方式:
第一种
- 首先打M ) a P开F E S 6 z ymitmproxy进行抓包,K G N x ; $ S 3 z即运行: mitmproxy或者另外两个命令
- 访问http://mitm.it/
如果你没有打开mitmproxy进行抓包的话,在这一步你会得到如下错误
上面这种方法L ~ l = t 2 6 + )我一直访问不到mitm.i& E 2 I ^t这个页面,可以采用以下方式进行安装
第二种
- 首先打开mitmpr$ w X u 2 _ L / Goxy进行抓包,即运行: mitmproxy或者另外两个命令。执行了这一步以后,在操作系统对应的用户名目录下会产生一个.mitmprJ D ? e w $ [ `oxy目录
.cer是Mac或LinuV 7 X * Q qx下的证书6 a _ _,.pT 3 R D W , f r12是Windows下的证书,.pem是安x r Z h卓下的s Z w证A ( W ; - b A书。
通过上述两种方式 O z x a得到证书文件后,证书按照步骤在网上找,非常多,这里就不再敖述了。
使用mitmproxy
要启动 mitmproxy 用 mitmproxy、mitmdump、mitmweb 这三个命令中的任意4 y C 5 ( $ 4一个即可,这三个命令功能一致,且都可) P N M以加载自定义 Y 4脚本,唯一的区别是交互界面的不同。但是他们各有特点,mitmproxy是进行抓包调` s ?试使用的,mitmweb是miK N L : t v [ c Mtmproxy的可视版本,mitmdump主要是加载脚本执行的,因为mitmdump抓取的信息是不主动显示的,由我们在脚本中使用特定打S 7 . x印方式,输出到界面,方便我们调X X ` x H E试,当然也可以直接使用print打印。
在控制台# r ` .中输入mitmdump -h,可以查看命令行帮助,我们主要使用的是-s和-p参数,-a Q t * pp指定监听端口,默认端口为8080,如果和其他软件有冲突,可j c 9 3以通过此参数修改;-s指定执行脚本,这个就是我们用mitmproxy的主要作用,h F F通过加载脚本,} $ $ 9 O执行请求过程的中间处理,修改请求数据或者保存返回数据。目前有两种使用方式:
from mitmproxy import http
from mitme O : b } a J 1proxy import ctx
def reM Z W F !sponse(flow: http.HTTPFlow):
\"_ Y / \"\"
flow为参数,后面跟http.HTTPFlow表示声明其类型,
这样在IDE中就可以自动提示其属性和方法,这是Python为我们提供的一种
便携的方式,尤其是{ J - ~ S D $对外提供接口时,可以s J 1 - (告知参数类型,这种方式是可选
的,当然你也可以使用常用方式,即不知道参数类型,只写参数名即可
\"4 ! t K D M Q\"\"
ctx.log.info(flow.request.url)
ctx.log.warn(flow.request.headers)
mitmproxy.ctx.log为mitmproxy为我们提供的日志P P P g k - L打印方式。
from mitmproxy import ht^ q 6 e 9 Dtp
class Counter:
def __init__(self):
self.num = 0
def reque1 b z W ` J ( 4st(self, flow: http.HTT+ j kPFlow):
self.num += 1
print(\"We\'ve seen %d flows\" % self.num)
print(flow.request.url)
print(fl` F 7 C %ow.reqC ? wuest.query)
addons = [
Counter()
]
官方推荐使用类的方式,上面的代码可能让你有点迷茫,无论是使用类方式还是函数方式def reqeust函数都是在mi) * f f @ #tmdump内部回调时会调用的,mitmdump就是使用这种事件回调的方式,为我们提供T 6 & I | U #了数据流的操作方式,那首先我们要了解mitT @ 9 y N ;mproxy为我们提供的事件(我们只关注HTTP相关的事件)。
class Events:
def reques, [ = t 7 B St(s~ D j 3 ~ Kelf, flow: http.HTTPFlow):
\"\"\X T H e z"
The full HTTP requesN R vt has been read.
\"\Y l W ; ) E a 5"\"
def response(self, flow: httpC , n w Q q . }.HTTPFlow):
\N Q A l $ ` H"\"\"
The full HX * n H L 5TTP responseh A 0 | K { - w has been read.
\"\"\"
request为请求发送至服务器前的回调函数,如果我们想对发送给服务器的请求进行修改,可以在这里进行处理。response为服务器将请求数据返回给我们时的回调函数,这里就是我们爬取到的数据,在这里我们可u N P以对数据进行处理,做入库处理。
我们在爬虫中使用mitmproxy,主要就是对Request和Response对象进行操作,下面我在源码中把对应的属性和方法都找^ % k f O b出来,作为参考Z 0 $ W a 5 E 5,就当作是字典一样来查询即+ E ) = . E g j可。源t _ w b h码在GitHub上下载Q ) v 8 s x c S q,路径为:mitmproxy/net/http/request.py和mitmproxy/net/http/response.py。
Request
flow.request.cookies #r M z获取请求的cookies
flow.request.headers # 获取所有头信息,包含Host、User-Agent、Content-type等字段
flow.request.url # 完整的请求地址,y 9 Z T E l 7 d包含域名及请求参q i I %数,但是不包含放在body里面的请求参数
flow.request.host # 域名
flow.requestT 0 | : Q.method # 请求方式。POST、GET等
flow.request.scheme # 请求类型 ,如 http、https
flow.request.path # 请求的路径,url除域名之外的内容
flow.request.teP G ( ~ [ U bxt # 请求中bodO G g ~y内容,可以获取某些请求的参数,返回字典类型
flow.request.replace()O : $ w s # 使用正则替换content中的内容
flow.request.query # 返回MultiDictView类型的数据,url直接带的键值参数,一般是GET请求的{ o q参数
flow.request.content # bytes,结果如flou B | + 7 # & u ;w.request.text
flow.request.raw_content # bytes,结果如flow.requy l Test.get_content()
flow.request.urlencoded_form # MultiX L O { x | #DictView,content-type:ao ( - r ( & 3 F ]pplication/x-www-foI v ^ ` V Crm-urlencoded 时的请求参数,不包含url直接带的键值参数
fz d b y : { P Xlow.request.multipart_form # MultiDictView,contentW l 4-type:mu V x ~ltipart/q L p s mform-data 时的请求参数,不包含url直接带的键值参数
Response
flow.response.status_code # 状态码
flow.response.text # 返回内容,已解码
flow.response.content # 返回内容,二进制
flow.response.cookies # 返回的cookies
flow.reJ 4 /sponse.headers # 返回的请求头
flow.response.repi d 3 C E 2 x 3lace() # 使用正则替换content中的内容
要特别注意,返回值为字典的类型的,不能直接在控] . H z E S n Y Q制台打印,可以使用str修饰,或者按照字典方式进行输出。
以下为测试示例:
- 测试代码
from mitmproxy import http
class Demo1:
def request(self, flow: htj , , T $ 8tp.HTTPFlow):
print(\'requu o Q dest url\', flow.request.url)
print(\'request nameV ^ 4 d\', flow.request.query.get(\'name\'))
printD , j(\'reqs W 1 ? / |uest age\', flow.request.query.get(\'age\'))
flow.request.query[\'name\'] = \d a ) _ s'yuehan\'
class Demo2:
def response(self, flow: http.HTTPFlow):
print(\'response name\', flow.request.query.get(\'name\'))
addons = [
Demo1(),
Demo2()
]
- 请求url:https://httpbin.org/get?name=jieke&age=23
- 输出结果:
示例Z , X w v (中使 0 % Y 1 P用两个类Demo1、Demo2,主要是为大家展示类方式的好处,即可以使用多个类,每个类处理进行独立的逻辑处理,每个类当中都可以同时使用request、reo n Usponse函数,希望不要因为例子里面而误* ` G导了大家。下面再说一点进阶用法,每一个处理类,都可以单独~ 9 y r ( w写一个py文件,再统一j { V o定义一个py文件,导入处理类,定义一个列表变量addons,变量中存储所有处理类的实例,示例如下:demo1.py
from mitmproxy import http
class Demo1:
def request(self, flow: http.HTTPF8 E 1 % ?low):
print(\'request url\', flow: Z a.request.url)
print(\'request name\', flow.request.query.get(\'name\'))
print(\'request ageq 5 } D U 0 B M\', flow.request.query.get(\'age\')3 [ Y M j 6)
flow.request.query[\'name\'] = \'yuehan\'
demo2.z 1 b C ) Qpy
from mitmproxy import http
class DemU ~ + | ko2:
def response(self, flow: httpi 2 g R 4 B + _.HTTPFlow):
print(\'response name\', flow.req] * ] Uuest.query.get(\'name\'))
sk P 1 e G K Zpider.py
import demo1
import demo2
addons = [
demo1.Demo1(),
demo2.Demo2()
]
- 抓包命令
mitmdump -p 8888 -s spide! 3 R 9r.py
参考文章:
1.使用 mit~ 9 Bmproxy + python 做拦截代理 https://blo~ : xg.wolfogre.com/posts/usage-of-mitmpr% 6 $oxy/
2.如何突破网站对seleQ A ?nium的屏蔽 https://blog.csdn.net/qq_26877377/articlw ( 8 S We/details/83307208