用 mitmproxy 做 HTTP 代理服务器
mitmproxy 是一个强大的代理工具,其中包括了:
- mitmproxy 交互式的http, https代理工具,有一个控制台界面
- mitmdump 控制台版本的mitmproxy,类似与tcpdump
- mitmweb 网页端的mitmproxy
具体的命令行工具就不解释了,刨坑代填,下面记录一下遇到的问题。
需求
需要爬去flash中的一些内容,但是正常情况下无法在浏览器中操作flash,这时候需要一个代理服务器,当服务器请求一些需要的链接的时候,再来处理这些链接。具体获取的内容可能是图片或者其他信息。
问题
主要的问题在于,我实际上需要实现一个代理服务器,运行在爬虫程序的某个线程,当获取到链接之后,再通知爬虫线程执行一些命令。最新的 mitmproxy 把与python的交互设计成了插件模式,这时候需要另起一个进程来做代理。但是进程通信的复杂度比线程通信高了不少,所以还是希望通过实现代理服务器子类的方式来处理。通过信息收集,mitmproxy实际上很不屑与实现子类的方式,该方式从版本 1.18 开始就被舍弃,变成了现在的插件模式。
解决方案
但是,皇天不负有心人,通过搜索github,发现了一种实现方式,但是再shutdown的时候有一些问题,目前还没解决,但是主要的问题已经解决了。就是继承DumpServer来实现子类和写一个处理请求的插件,具体代码如下。
from mitmproxy.options import Options from mitmproxy.proxy.config import ProxyConfig from mitmproxy.proxy.server import ProxyServer from mitmproxy.tools.dump import DumpMaster class Addon(object): def request(self, flow): # do something in request pass def response(self, flow): # do something in response pass class ProxyMaster(DumpMaster): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def run(self): try: DumpMaster.run(self) except KeyboardInterrupt: self.shutdown() def main(): options = Options(listen_host='0.0.0.0', listen_port=8080) config = ProxyConfig(options) master = ProxyMaster(options, with_termlog=False, with_dumper=False) master.server = ProxyServer(config) master.addons.add(Addon()) master.run() if __name__ == '__main__': main()
只需要实现 Addon 的 request 或者 response 方法就可以了,这个实现具体看自己的需求。
保存相应内容
class Addon(object): def response(self, flow): url = flow.request.url if url != "what you what to catch": return filename = "path to your file" content = flow.response.data.content with open(filename, "wb") as file: file.write(content)
评论