App数据抓取(抓包工具使用)

文章目录

    • fiddler使用
      • 移动端
    • mitmproxy
      • mitmdump
    • 基础实战
      • 使用Python抓取数据
    • 小结

fiddler使用

  • 基础篇介绍了如何设置fiddler做代理,这里继续介绍使用方法
  • 在file中可以选择开始/停止抓包App数据抓取(抓包工具使用)_第1张图片
  • 可以选中左侧的数据包save为.saz格式
    • 保存了自然也可以Load Archive,在File中
    • 也可以import/export
      App数据抓取(抓包工具使用)_第2张图片
  • 对选中的数据包可以在Edit中Copy
    • Edit中有很多操作
      App数据抓取(抓包工具使用)_第3张图片
    • Unlock for Editing即解锁编辑,在右侧可以修改数据
    • 编码转换工具TextWizard
      App数据抓取(抓包工具使用)_第4张图片
    • 可以通过HOSTS改变增加域名和IP的映射
      App数据抓取(抓包工具使用)_第5张图片
    • 最常用的还是Options
      App数据抓取(抓包工具使用)_第6张图片
    • 安装证书到浏览器,破解浏览器发送和收到的请求
      App数据抓取(抓包工具使用)_第7张图片
    • 上一篇讲过使用SwitchyOmega插件设置让fiddler代理,也可通过浏览器设置(比较麻烦)
  • 配置抓取规则(过滤器)
    • 隐藏抓取图片的请求
    • 访问时需要带上用户名密码(代理认证)
      App数据抓取(抓包工具使用)_第8张图片
    • 可在Performance中设置慢速访问:Simulate Modem Speed
  • 工具栏
    App数据抓取(抓包工具使用)_第9张图片
  • 右侧是辅助功能区
  • 最下方是命令行
    app12
  • 就不再保姆式教程了,用到了再提示,先说一下抓包后各字段和图标的含义
    • 会话列表字段:
      App数据抓取(抓包工具使用)_第10张图片
    • 包前面的图标:
      App数据抓取(抓包工具使用)_第11张图片
      App数据抓取(抓包工具使用)_第12张图片
      App数据抓取(抓包工具使用)_第13张图片
    • 了解即可,无需记忆!说一下状态码吧
      • 状态码分五类,分别以12345开头,说常见的
      • 200:表示请求被正常响应并返回
      • 301:永久重定向,一般是网站迁移了
      • 302:临时重定向,URI标识已更新
      • 304:说明请求的东西在本地缓存了,一般包首部会带上标识,文件修改才会从服务器更新,否则使用本地缓存的文件(可以理解成重定向到本地缓存)
      • 404:请求的资源在服务器上找不到,也可能是服务器出错又不想告诉你
      • 403:禁止访问,文件权限不够
      • 500:服务器内部错误,如处理逻辑出错,连接数据库出错等
      • 502:服务器作为代理时从上游服务器收到了一个无效的响应
      • 503:服务器暂时处于超负载或正在进行停机维护
      • 其他状态码和提示信息后台开发可自定义
  • 设置断点
    • 图形界面(不推荐)
      App数据抓取(抓包工具使用)_第14张图片
      • 可以在请求之前和之后设置断点,测试一下:
      • 打开fiddler代理,设置请求前拦截(最下方会出现一个红色的T),访问https://www.jianshu.com
      • 遇到443先放行,然后修改包,删除Hosts(看到左下角的T没?)
        App数据抓取(抓包工具使用)_第15张图片
      • 点击Run to completion即可,然后Go放行,即可转到百度的页面
        app18
      • 拦截响应也是一样的,Choose Response,直接返回状态!
    • 命令行方式(推荐)
      • 下方命令行输入bpu https://www.baidu.com回车,下方会显示Breakpoint…
      • 在百度搜索cba,然后找到相应的请求
      • 在webForms中找到wd,修改为“python 爬虫”
        App数据抓取(抓包工具使用)_第16张图片
      • 点击Break on Response,会生成另外一个请求,做同样的修改操作
      • 然后回到第一个请求,Run to completion
        App数据抓取(抓包工具使用)_第17张图片
    • 两种方式有什么区别呢?很明显,图形界面会拦截所有的请求,而命令行只会拦截指定的Host请求
      • 再次键入bpu就会取消拦截!
      • buafter会拦截指定Host的响应,其他操作和图形界面一样!取消输入bpa
  • 设置重定向
    • 当网站某个文件出现bug,别急着去服务器上修改,重定向成本地文件看看行不行,再改!
    • 例如这里用重定向替换掉一张百度首页的图片
      App数据抓取(抓包工具使用)_第18张图片

移动端

  • 抓取移动端数据
    • 配置模拟器网络,桥接模式直接使用主机IP,NAT的话会转换掉
      App数据抓取(抓包工具使用)_第19张图片
    • 设置中对连接的无线网络长按左键修改网络,手动配置,添加代理:
      App数据抓取(抓包工具使用)_第20张图片
    • 相当于浏览器上使用SwitchyOmega配置fiddler代理;这里的IP得是主机IP,加上端口号确定使用fiddler代理!
    • 配置一下fiddler:
      App数据抓取(抓包工具使用)_第21张图片
    • 在模拟器的浏览器中上网会提示安装证书,输入:主机IP:8889安装即可
      • 如果没弹出安装证书的链接可能需要重启fiddler
      • 或者你的网络连接有问题
    • 接下来就可以正常访问网络并抓取数据包了

mitmproxy

  • 抓取移动端数据
    • 同样的,配置代理;这里使用的是Linux中的mitmproxy(Linux虚拟机的IP),用secureCRT连接
      App数据抓取(抓包工具使用)_第22张图片
    • 使用mitmproxy -p 8889启动,然后进行配置和证书的安装(遇到安全问题先继续)
    • 清楚数据包:z;退出当前查看的数据包:q
  • 基本操作
    • 过滤数据包,启动后,输入f,设置规则
      app26
      • 表示所有的post请求且URL包含baidu的包,注意是~,不是 -
    • 设置断点拦截,输入规则i
      App数据抓取(抓包工具使用)_第23张图片
    • 上面是拦截请求,同样的规则可以拦截响应,直接在Response中修改
    • q返回数据包列表,输入a继续执行

mitmdump

  • 这是一个重要的组件,因为能和Python脚本交互,怎么交互呢?
  • 在pycharm新建工程,编写文件request_test.py
    # 注意名称和参数,是固定的
    def request(flow):
        print(flow.request.headers)
    
  • 打开cmd切换到所在路径:
    app28
  • 打开模拟器配置好IP和端口(之前配置了),打开浏览器请求,cmd就会出现响应
    • 在Linux中我们可以通过级别设置响应为不同的颜色
      from mitmproxy import ctx
      
      def request(flow):
          # print(flow.request.headers)
          ctx.log.info(str(flow.request.headers))	# 黑
          ctx.log.warn(str(flow.request.headers))	# 黄
          ctx.log.error(str(flow.request.headers))	# 红
      
      App数据抓取(抓包工具使用)_第24张图片
    • 注:从Windows换到Linux修改模拟器的网络配置后,证书会失效,需要重新安装
    • 同样,可以通过脚本获取响应:
      def response(flow):
          ctx.log.error(str(flow.response.status_code))
      
  • 到此,我们应该明白了基本的数据抓取流程
    • 中间人代理拦截数据包
    • 使用python脚本分析获取数据
    • 保存在数据库(mongoDB)

基础实战

  • 抓取豆果美食App为例
    App数据抓取(抓包工具使用)_第25张图片
  • 这里使用fiddler抓包
    • fiddler这里只做截获数据包查看信息
    • 至于获取数据需要Python再使用request抓取
    • 而mitmdump的好处是可以交互式的获取,都行!
    • 配置代理就不说了,注意fiddler配置后要重启
  • 打开豆果美食APP,抓取菜谱分类下的菜品
    • 需要的数据包都是有特定Host的,可以通过Find过滤,在json.cn查看是否是想要的数据
    • 这里是api.douguo.net(有规律的)

使用Python抓取数据

  • 需求分析:
    • 我们想要的数据其路径可以分三层,如上图;左侧分类—子分类—子子分类,即理论上来说三次请求即可获取一个菜品列表
    • 点击进入任意子分类:
      App数据抓取(抓包工具使用)_第26张图片
    • 这下面的链接就是我们需要的菜品,在fiddler中也能看到固定Host的数据包:
      App数据抓取(抓包工具使用)_第27张图片
    • 这里翻页后URL会变化,例如https://api.douguo.net/recipe/v2/search/40/20
  • 爬取包括以下步骤:
    • 分析数据包
      • 很多Header中的模拟字段也是需要反复测试的,哪些需要,哪些可不要!
      • 就以抓取红烧肉页面的前20个菜品为例(他的一次请求为20个),即这里从请求每个菜品列表开始哦~(Raw中的数据需要decode且右键—Word Wrap)
      • 将数据包中的header放到sublime中进行字典构造,使用正则!
        App数据抓取(抓包工具使用)_第28张图片
      • 不仅需要分析拿到URL,请求体Client的构造也需要试验,在TextView中
        # 以下是:热门分类下的页面数据爬取
        import json
        from mitmproxy import ctx
        import pymongo
        from concurrent.futures import ThreadPoolExecutor
        from multiprocessing import Queue
        import requests
        
        q_list = Queue()    # 创建队列
        
        def handle_request(url, data):
            '''
            请求收到的URL,通用函数
            :param url:要爬取的页面路径
            :param data: 类似查询字符串,需要携带
            :return:
            '''
            header = {
                   
                # "Cookie":"duid=68162062",	# 一直带着可能会过期或者被发现
                "client": "4",
                "version": "7002.2",
                "device": "MI 9",
                "sdk": "22,5.1.1",
                "channel": "baidu",
                "resolution": "1920*1080",
                "display-resolution": "1920*1080",
                "dpi": "2.0",
                # "pseudo-id":"787646a55fdc401f",
                "brand": "Xiaomi",
                "scale": "2.0",
                "timezone": "28800",
                "language": "zh",
                "cns": "2",
                "carrier": "CMCC",
                # "imsi":"460071450091244",
                "User-Agent": "Mozilla/5.0 (Linux; Android 5.1.1; MI 9 Build/NMF26X; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.136 Mobile Safari/537.36",
                "act-code": "1620205663",
                "act-timestamp": "1620205663",
                "uuid": "ebef7bff-542f-42c6-a475-e8de87fcd3dd",
                "battery-level": "1.00",
                "battery-state": "3",
                "bssid": "8E:18:AE:BD:7C:2B",
                "syscmp-time": "1611650487000",
                "rom-version": "MI 9-user 5.1.1 NMF26X 500210126 release-keys",
                "terms-accepted": "1",
                "newbie": "1",
                "reach": "1",
                "app-state": "0",
                "Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
                "Accept-Encoding": "gzip, deflate",
                "Connection": "Keep-Alive",
                "session-info": "dfK7qegtd182Vj2rpxohKN+VIAVqnhich861Ixxi3TylXN+zRXLyg3UANboiDRarEs1deKbmIEH4Ozb5hDd9qWMlGlCmN60QT0K4Vt9tU2FDnsRNGr5srWR3+UsjZXds",
                "Host": "api.douguo.net",
                "Content-Length": "186"
            }
            response = requests.post(url=url, headers=header, data=data)
            return response
        
        def handle_index():
            '''
            爬取入口
            :return:
            '''
            url = "http://api.douguo.net/recipe/flatcatalogs"    # 包含所有分类页面;虽然在fiddler中没有看到这些信息?
            data = {
                   
                "client": "4",
                # "_session": "1620481668741351564456991249",
                # "v": "1620479743",    # 时间戳
                "_vs": "0",
                "sign_ran": "0ba9ee8141ad954b99e7793cccbcb7ff",
                "code": "0467076e74f79dcf",
            }
            response = handle_request(url=url, data=data)
            # print(response.text)  # 所有分类及子分类
            index_dict = json.loads(response.text)
            # 从入口开始遍历循环,往里钻!
            for i in index_dict['result']['cs']:
                for j in i['cs']:
                    for k in j['cs']:   # 每个k即一列菜品,子子分类
                        # 同样,要带data
                        data_k = {
                   
                            "client": "4",
                            # "_session": "1620481668741351564456991249",
                            "keyword": k['name'],
                            "order": "0",
                            # "_vs": "400",
                            "type": "0",
                            "auto_play_mode": "2",
                            "sign_ran": "9ca4074b6529cebe89b3f8bf8e89dedb",
                            "code": "eeb8ff71d9800702",
                        }
                        # print(data_k)
                        q_list.put(data_k)  # 每个子子分类菜品加入队列准备抓取
        
        
        def handle_detail(data):
            '''
            从队列中取出各子子分类,获取每道菜的信息
            :param data:
            :return:
            '''
            print("当前正在获取菜品分类:",data['keyword'])
            list_url = "https://api.douguo.net/recipe/v2/search/0/20"   # 只请求前20条,也可以循环全部获取
            list_response = handle_request(url=list_url, data=data)
            print(list_response.text)
            list_response_dict = json.loads(list_response.text)
            for i in list_response_dict['result']['list']:
                detail_info = {
                   }
                detail_info['shicai'] = data['keyword'] # 子子类别
                if i['type'] == 13:   # 过滤广告
                    detail_info['user_name'] = i['r']['an']
                    detail_info['shicai_id'] = i['r']['id']
                    detail_info['describe'] = i['r']['cookstory'].replace('\n','').replace(' ','')
                    detail_info['cai_name'] = i['r']['n']
                    detail_info['zhuliao_name'] = i['r']['major']   # 主要食材
                    # 上面只通过子子分类列表获取部分信息,每个菜的详细信息(步骤)需要再次请求
                    detail_step_url = 'https://api.douguo.net/recipe/v2/detail/'+str(i['r']['id'])
                    data_i = {
                   
                        "client": "4",
                        # "_session": "1620481668741351564456991249",
                        "author_id": "0",
                        # "_vs": "11103",
                        # 这整个是一个字符串,必须使用 +
                        "_ext": '{"query":{"kw":'+str(data['keyword'])+',"src":"11103","idx":"2","type":"13","id":'+str(i['r']['id'])+'}}',    # 这个需要用decode工具解码一下
                        "is_new_user": "1",
                        "sign_ran": "d0f92b8a4ba74357ebcbad077247f226",
                        "code": "5866305262e9a1f3",
                    }
                    detail_step = handle_request(detail_step_url, data_i)
                    detail_step_dict = json.loads(detail_step.text)
                    detail_info['tips'] = detail_step_dict['result']['recipe']['tips']
                    detail_info['cook_step'] = detail_step_dict['result']['recipe']['cookstep']
                    print(json.dumps(detail_info))
                else:
                    continue
        
        handle_index()
        print(q_list.qsize())   # 518个子子分类
        handle_detail(q_list.get())
        
        App数据抓取(抓包工具使用)_第29张图片
        App数据抓取(抓包工具使用)_第30张图片
      • 从队列中取出一个菜品列表list,获取每个菜品
        App数据抓取(抓包工具使用)_第31张图片
        App数据抓取(抓包工具使用)_第32张图片
    • 多线程抓取
      • 生产者(request)得到队列Queue,size即左侧分类中包含的所有子分类数;只需处理每个子分类下的所有item,其他交给循环
      • 线程池使用多线程抓取队列中的链接获取数据
    • 代理IP隐藏爬虫
      • 防止APP因你使用相同IP大量请求被禁止访问
      • 可以在阿布云上购买代理,在request中通过proxies=使用
      • 根据购买的情况会限制每秒请求数(每秒切换IP数),所以线程不能开的太多!
        App数据抓取(抓包工具使用)_第33张图片
      • 测试代码
        import requests
        
        # 测试使用阿布云代理IP
        url = "http://ip.hahado.cn/ip"
        proxy = {
                   'http':'http://H211EATS905745KC:[email protected]:9030'}
        response = requests.get(url=url, proxies=proxy) # 使用代理请求
        print(response.text)    # 访问的这个网站会返回当前使用的IP
        
      • 在我们的handle_request函数中加上这个proxy,加入到post的proxies中即可!
    • 保存数据到数据库
      • 安装pip install pymongo,这里使用MongoDB数据库
        • 它将数据存储在类似 JSON 的灵活文档集中
      • 这里将数据库安装子啊Ubuntu中:sudo apt install mongodb
        • 查看状态:sudo systemctl status mongodb或者查看其占用的端口netstat -an | grep 27017
      • 相关代码:
        # mongodb_conn.py
        import pymongo
        from pymongo.collection import Collection
        
        class Handle_mongodb(object):
            def __init__(self):
                self.client = pymongo.MongoClient(host='192.168.154.130', port=27017) # 虚拟机IP,固定端口号
                self.db = self.client['douguo_meishi']    # 如果没有该数据库会自动创建
        
            def insert(self, item):
                db_collection = Collection(self.db,  'douguo_item') # 传入数据库和json文件名
                db_collection.insert(item)
        
        mongo = Handle_mongodb()
        
        App数据抓取(抓包工具使用)_第34张图片
    • 以上便是爬取豆果美食APP的大致流程!

小结

  • 我们通过模拟器和fiddler分析出请求数据的URL和header之后,以requests包发起请求,编写python脚本抓取并保存数据
  • requests在这里的使用有三要素:URL、Header、data,理清页面之间的逻辑关系,准确定位后即可抓取
  • fiddler软件和模拟器的使用无需刻意记忆,工具的目的是简化操作,熟能生巧

你可能感兴趣的:(一起爬,爬虫,fiddler,android模拟器,mongodb)