zoukankan      html  css  js  c++  java
  • 8 爬取EOS whales网站出现的ssl验证问题以及无法建立websocket握手问题(北京大学出版社的《python 爬虫与反爬虫开发》书中错误)

    案例来源于北京大学出版社的《python 爬虫与反爬虫开发》

    1.对于ssl验证问题的解决

    采取firefox打开eos whales,点击小锁标志,查看证书,下载pem证书,然后利用openssl命令将pem证书转变为cer证书,并安装到本机中,之后使用AIOwebsocket的方法,就不会报出ssl验证失败的问题。

    因为在Aiowebsocket的查询证书过程中,通过调试,发现其去windows的ca和root下寻找证书。

    对于其他可能用到ssl验证的api,上述方法可能无法解决,需要关闭ssl验证,或者调试程序,看看去哪里寻找证书。

    2.对于无法建立websocket握手问题

    这个问题,暂时无法解决。

    发现EOS whales网站的当前操作与书中案例执行时的操作不一致。

    在模拟握手过程中,执行send(2probe)操作后,可以收到3probe的回复。但是后续send(5)以及send一个42开头的字符串,都无法继续获取服务器端的推送数据。

    其中42开头的数据中:

    42["message","{"_url":"/admin/subscribe_event","_method":"POST","_headers":{"content-type":"application/json"},"cid":"9f280170-2d9c-11ec-9d3f-f5367634451a","event":"recent_performance_history","lang":"zh-CN"}"]

    其中,包括一个cid代码。

    后来查看common.js代码文件,发现有一个生成uuid的js代码片段。

    var CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
    function uuid(len, radix){
        var chars = CHARS, uuid = [], i;
        radix = radix || chars.length;
     
        if (len) {
          for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random()*radix];
        } else {
          var r;
          uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
          uuid[14] = '4';
          for (i = 0; i < 36; i++) {
            if (!uuid[i]) {
              r = 0 | Math.random()*16;
              uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
            }
          }
        }
        return uuid.join('');             
    }

    通过在python中仿真出这段代码:

        CHARS = split_str('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz');
        radix = len(CHARS)
        uuid={}
        uuid[8]=uuid[13] = uuid[18] = uuid[23] = '-'
        uuid[14] = '4';
        print(uuid)
        
        for i in range(0,36):
            # print (i)
            if not (i in uuid.keys()):
                # print (i)
                r = 0 | int(random.random()*16)
                
                # print (r)
                if(i==19):
                    uuid[i]=CHARS[(r&0x3)|0x8]
                else:
                    uuid[i]=CHARS[r]
                    
        #     uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
        #   }
        # }
        # print (CHARS)
        # print (radix)
        # print(uuid)
        CID=''
        for i in range(0,36):
            CID=CID+uuid[i]
        # print (CID)
        return CID

    后来实际运行时,发现我写的python生成的CID和Js中生成的UUID在风格上很不一致。

    所以,这个CID的生成,目前较难通过分析JS去破解。

    除了CID以外,WS链接中有一个sid的参数,wss://api-v1.eosflare.io/socket.io/?EIO=3&transport=websocket&sid=

    K1ngCFhbUMrnQ-jMCevK,

     我们通过分析XHR,发现是在第一个执行中,获取SID,后来通过python生成了SID。

    同时,注意在cookie中要设置session id。

    后续发现,每次刷新网页,里面都会有个t的参数存在变化。 

    同时,观察发现,这些XHR交互过程中,第一个post将cid发送到服务器

     

    并获取到返回状态ok

    继续执行get,会获得一个成功的返回状态

    总结:

    个人利用websocket发送握手数据,失败的两种可能原因是:

    1.生成的CID不符合要求。

    2.在建立ws链接之前,通过post和get,将cid发送到服务器,并获取返回状态。这一步使用requests的时候,无论如何设置cookie(主要是设置session id号)还是如何模拟xhr的操作,都无法返回成功状态。

    报错的结果就是:500。可能是一种反爬措施,由于前述t这个参数造成。而t一直在变化。所以,你用requests去访问,去试图建立成功状态,都会由于t的变化,导致无法成功模拟。

    可能是因为这一步不成功,就相当于cid未在服务端注册,因此导致握手无法成功。

    对于js导致的爬虫失败和反爬措施,只能采取selenium,是绝杀了。虽然不太好,但是只能这样。

    关于本书的看法:

     花了钱买了书,也进了交流群,但是连续两天,对我提出的问题,作者不回应(作者看到了问题),这就让人很无语了。

    书中第七章勘误代码:

    案例1,乐鱼体育的正确调试代码

    import asyncio
    from aiowebsocket.converses import AioWebSocket
    import websocket
    import requests
    import json
    import math
    import time
    
    def get_token():
        """
        获取加密字符串,将其拼接到websocket协议的url上
        :return: token
        """
        url = "https://live.611.com/Live/GetToken"
        #设置header属性
        header={
            "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3877.400 QQBrowser/10.8.4506.400"
            ,"Referer":"http://m.611.com/Match/Index"
            ,"Connection":"keep-alive"
            ,"Cookie":"UM_distinctid=17c7d64b47c3c2-09f412d2404148-3354427f-144000-17c7d64b47d549; CNZZDATA1279044329=299416022-1634189736-%7C1634189736; ASP.NET_SessionId=kkym2rc4wqj05jrrwwjrogzu"
            ,"Host":"m.611.com"
            }
        #response = requests.get("http://m.611.com/Match/Index",headers=header)
        response = requests.get(url,headers=header)
        if response.status_code == 200:
            data = json.loads(response.text)
            token = data["Data"]
            return token
        else:
            print("请求错误")
    
    
    async def startup(url):
        async with AioWebSocket(url) as aws:
            converse = aws.manipulator
            # # 客户端给服务端发送消息
            _time = math.floor(time.time()) * 1000
            info = {'chrome': 'true', 'version': '70.0.3538.25', 'webkit': 'true'}
            message1 = {
                "command": "RegisterInfo",
                "action": "Web",
                "ids": [],
                "UserInfo": {
                    "Version": str([_time]) + json.dumps(info),
                    "Url": "https://live.611.com/zq"
                }
            }
            message2 = {
                "command": "JoinGroup",
                "action": "SoccerLive",
                #"ids": [303499393],
                "ids":[]
            }
            message3 = {
                "command": "JoinGroup",
                "action": "BasketSoccerLive",
                #"ids": [303499393],
                "ids":[]
            }
            # # await (time.sleep(2))
            print("------------向服务的发起握手")
            # await asyncio.sleep(3)
            # await send(converse,json.dumps(message1))
            await converse.send(json.dumps(message1))
            await converse.send(json.dumps(message2))
            await converse.send(json.dumps(message3))
            while True:
                mes = await converse.receive()
                print("---------正在接收服务端主动推送的信息")
                print(mes)
    
    # async def send(converse,message):
    #     converse.send(message)
    if __name__ == '__main__':
        url = "wss://push.611.com:6119/{}".format(get_token())
        print(url)
        try:
    
            
            #使用websocket
            # ws = websocket.create_connection(url)
            
            # # 客户端给服务端发送消息
            # _time = math.floor(time.time()) * 1000
            # info = {'chrome': 'true', 'version': '70.0.3538.25', 'webkit': 'true'}
            # message1 = {
            #     "command": "RegisterInfo",
            #     "action": "Web",
            #     "ids": [],
            #     "UserInfo": {
            #         "Version": str([_time]) + json.dumps(info),
            #         "Url": "https://live.611.com/zq"
            #     }
            # }
            # message2 = {
            #     "command": "JoinGroup",
            #     "action": "SoccerLive",
            #     "ids": [303499393]
            # }
            # message3 = {
            #     "command": "JoinGroup",
            #     "action": "BasketSoccerLive",
            #     "ids": [303499393]
            # }
            # # await (time.sleep(2))
            # print("------------向服务的发起握手")
            # # converse.send(str(message1))
            # # converse.send(str(message2))
            # # converse.send(str(message3))
            # ws.send(json.dumps(message1))
            # ws.send(json.dumps(message2))
            # ws.send(json.dumps(message3))
            # while True:
            #     # mes = converse.receive()
            #     result=ws.recv()
            #     print(result)
            #     print("---------正在接收服务端主动推送的信息")
            
            # aws=AioWebSocket(url)
            # converse = aws.manipulator
            loop=asyncio.get_event_loop()
            task = loop.create_task(startup(url)) 
            loop.run_until_complete(task)
            # asyncio.run(startup(url))
        except Exception as ex:
            print(ex)
            pass
        finally:
            pass
            # loop.stop();
            # asyncio.set_event_loop(None)
            

    注意:下面三句话是正确的。原书案例中的已经无法正确跑通。同时我的代码中提供了websocket的方式,有区别于Aiowebsocket

            loop=asyncio.get_event_loop()
            task = loop.create_task(startup(url)) 
            loop.run_until_complete(task)

    案例2,无法爬取eos whales网站的无法跑通代码(希望有人能够给它调通)

    import asyncio
    from datetime import datetime
    from aiowebsocket.converses import AioWebSocket
    import ssl
    import websocket
    import requests
    import json
    import re
    import random 
    from requests.cookies import RequestsCookieJar
    # ssl._create_default_https_context = ssl._create_unverified_context()
    def split_str(s):
      return [ch for ch in s]
    def get_CID():
        
        # 下面是生成CID的JS代码。
        # var CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
        # function uuid(len, radix){
        #     var chars = CHARS, uuid = [], i;
        #     radix = radix || chars.length;
         
        #     if (len) {
        #       for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random()*radix];
        #     } else {
        #       var r;
        #       uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
        #       uuid[14] = '4';
        #       for (i = 0; i < 36; i++) {
        #         if (!uuid[i]) {
        #           r = 0 | Math.random()*16; 
        #           uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
        #         }
        #       }
        #     }
        #     return uuid.join('');             
        # }
        
        ###需要将上面的代码改为python代码。
        ###通过实际调试,发现len和radix都未传入值
        CHARS = split_str('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz');
        radix = len(CHARS)
        uuid={}
        uuid[8]=uuid[13] = uuid[18] = uuid[23] = '-'
        uuid[14] = '4';
        print(uuid)
        
        for i in range(0,36):
            # print (i)
            if not (i in uuid.keys()):
                # print (i)
                r = 0 | int(random.random()*16)
                
                # print (r)
                if(i==19):
                    uuid[i]=CHARS[(r&0x3)|0x8]
                else:
                    uuid[i]=CHARS[r]
                    
        #     uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
        #   }
        # }
        # print (CHARS)
        # print (radix)
        # print(uuid)
        CID=''
        for i in range(0,36):
            CID=CID+uuid[i]
        # print (CID)
        return CID
            
    def get_ready(SID):
        """
        建立会话的一些过程
        """
        url = "https://api-v1.eosflare.io/socket.io/?EIO=3&transport=polling&t=No3LAGI&sid="+SID
        #设置header属性
        header={
            "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3877.400 QQBrowser/10.8.4506.400"
            ,"Referer":"hhttps://eosflare.io/"
            ,"Connection":"keep-alive"
            ,"Cookie":"_ga=GA1.2.73969542.1634216434; _gid=GA1.2.678520107.1634216434;_gat=1; io="+SID
            # ,"content-type":"text/plain;charset=UTF-8"
            # ,"content-length":"218"
            }
        #response = requests.get("http://m.611.com/Match/Index",headers=header)
        cookie_jar = RequestsCookieJar()
        cookie_jar.set("_ga", "GA1.2.73969542.1634216434", domain="https://eosflare.io")
        cookie_jar.set("_gid", "GA1.2.678520107.1634216434", domain="https://eosflare.io")
        cookie_jar.set("io",SID, domain="https://eosflare.io")
        cookie_jar.set("_gat","1", domain="https://eosflare.io")
        payload='214:42["message","{"_url":"/admin/subscribe_event","_method":"POST","_headers":{"content-type":"application/json"},"cid":"de70974e-70be-4ee0-9668-1230f3a54490","event":"info","lang":"zh-CN"}"]'
        print (payload)
        # response = requests.get(url,headers=header,cookies=cookie_jar)
        response = requests.post(url,headers=header,data=payload)
        if response.status_code == 200:
            #post数据
            # url="https://api-v1.eosflare.io/socket.io/?EIO=3&transport=polling&t=No37Orm.0&sid=aHVo8UA0B0INjHtMCeQ4"
            print(response.text)
        else:
            print("请求错误")
    def get_SID():
        """
        获取加密字符串,将其拼接到websocket协议的url上
        :return: token
        """
        url = "https://api-v1.eosflare.io/socket.io/?EIO=3&transport=polling&t=No3LACi"
        #设置header属性
        header={
            "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3877.400 QQBrowser/10.8.4506.400"
            ,"Referer":"hhttps://eosflare.io/"
            ,"Connection":"keep-alive"
            ,"Cookie":"_ga=GA1.2.73969542.1634216434; _gid=GA1.2.678520107.1634216434; io=Y74WllNMsh0UAdQyCbZ5"
            }
        #response = requests.get("http://m.611.com/Match/Index",headers=header)
        
    
        response = requests.get(url,headers=header)
        if response.status_code == 200:
            p1 = re.compile(r'[{](.*?)[}]', re.S)  #最小匹配
            text=(re.findall(p1, response.text))
            text='{'+text[0]+'}'
            # print(text)
            data = json.loads(text)
            token = data["sid"]
            return token
        else:
            print("请求错误")
    
    async def startup(uri):
        async with AioWebSocket(uri) as aws:
            # aws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
            converse = aws.manipulator
            # 客户端给服务端发送消息
            await converse.send(2)
            while True:
                try:
                    mes = await converse.receive()   
                    print(mes)
                except Exception as ex:
                    print (ex)
                    continue
                # print(mes)
                # print('{time}-Client receive: {rec}'
                      # .format(time=datetime.now().strftime('%Y-%m-%d %H:%M:%S'), rec=mes))
    
    if __name__ == '__main__':
        remote = 'wss://api-v1.eosflare.io/socket.io/?EIO=3&transport=websocket&sid='
        # url="https://eosflare.io/"
        try:
            # xxx=ssl.get_server_certificate(("34.117.51.23",443),ca_certs="C:\Users\xiaojie\anaconda3\Lib\site-packages\certifi\cacert.pem")
            print(remote)
            
            
                    # 使用websocket
            # CID=get_CID()
            # CID='de70974e-70be-4ee0-9668-1230f3a54490'
            SID=get_SID()
            print(SID)
            get_ready(SID)
            # 客户端给服务端发送消息
            remote=remote+SID
            # remote='wss://api-v1.eosflare.io/socket.io/?EIO=3&transport=websocket&sid=ZJLHtYunzyNXMRxdCb4d'
            print(remote)
            # ws = websocket.create_connection(remote,sslopt={"cert_reqs": ssl.CERT_NONE})
            print("------------向服务的发起握手")
            ws.send("2probe")
            result=ws.recv()
            print(result)
            
            # message1 = '42["message","{"_url":"/admin/subscribe_event","_method":"POST","_headers":{"content-type":"application/json"},"cid":'+'"'+CID+'","event":"recent_performance_history","lang":"zh-CN"}"]'
            # print (message1)
            # ws.send(message1)
            # de70974e-70be-4ee0-a668-1230f3a54490
            # 56338e60-2d6c-11ec-99a8-93befb71502e
            while True: 
                ws.send("5")
                result=ws.recv()
                print(result)
                print("---------正在接收服务端主动推送的信息")
            
            
            # loop=asyncio.get_event_loop()
            # task = loop.create_task(startup(remote)) 
            # loop.run_until_complete(task)
        except Exception as ex:
            pass

    里面已经包括生成UUID操作,以及模拟ws前获取sid操作,和模拟ws前上传cid操作。但是如前所述,无法成功建立握手。只能收到“3probe”的推送,后续消息全无。

     心得

    为了这个问题,我花了两天时间。而实际上大可不必。

    对于一个人而言,他的经验有限,他看的书的经验也有限。当经验有限的时候,一定钻于一个问题,苦思而不得其解的时候,不如放下这个问题,继续学习经验,当眼光更高,视野更阔的时候,

    可能就迎刃而解。

    书的作者也是个小年轻,闻道有先后,术业有专攻,不回复就算了,拽老资格就没必要。

    在一个错误的方法中苦思不得其解,如同坐井观天,与青蛙论道,与夏虫言冬。

    以后要换一种学习思路,切不可浪费时间,切记切记。

    你永远不知道未来会有什么,做好当下。技术改变世界,欢迎交流。
  • 相关阅读:
    vue 组件之间的通讯方式
    vue 路由4种传参方式
    vue+axios封装已文件流的形式导出文件
    vue 开发环境正常打包之后背景图片无法访问或者element-ui的icon找不到
    vue 优化webpack引入CND
    microtime() — 返回当前 Unix 时间戳和微秒数
    将一个字符串分隔为组成它的字符
    Laravel 伪静态配置
    VSCode
    array_merge()&array_combine()合并数组函数
  • 原文地址:https://www.cnblogs.com/xiaojieshisilang/p/15412181.html
Copyright © 2011-2022 走看看