千峰教辅平台爬取

 1.起因 

   大二小学期就这样结束了,这次小学期内容是python实训,经过十天的不眠不休的战斗,也算是把一个python小白,变成了一个董一丢丢的小白。每次学习过后都需要一个实战项目来练手。这次当然不会意外!所以我选择了我们实训的直播网站,锋云智慧教辅平台!想去上面搞点好玩的下来!

锋云教辅平台爬取(图1)


2.登录

   首先这个网址是需要登录的,就如上图一样。登录这个事当然是最普遍的啦,但是登录最难的应该莫属于验证码了。很多现在网站都是 滑块严重,拼图验证,选词验证,像这种简单的验证码,很少有大公司用的。但没办法,他不用难的,那就是给我机会去爬取咯!开干!

   2.1-首先找到登录表单提交的地址,这个很简单,f12->network  查看哪个文件是提交登录的文件

       很容易发现,里面有一个login的文件,下面带着我们的账户密码,验证码,很显然他就是我们需要提交的表单,找到他的请求地址  https://api-xy.1000phone.com/api/login/login

锋云教辅平台爬取(图2)

      2.2-分析表单数据

         我们可以看到,表单中一共有五个数据,其中账户密码是通过加密得来的!验证码,还有一个通过加密的验证码token,最后一个data不知道是什么东西!

         我尝试过去分析他账户密码加密过程,但是应该是将多种加密方式组合在一起,形成的加密。所以我就放弃了研究他,直接拿着加密好的字符串进行请求,那我们现在的难点就只在验证码上了!

         验证码:

                通过再次分析数据,我找到了验证码的请求地址  https://api-xy.1000phone.com/api/common/getCaptcha

       对于验证码来说,请求头只要稍微的伪装一下就能拿到验证码,如果担心不行,就直接带上请求头里面的所有东西,百分百不出错!废话不多说,我们分析验证码数据,可以看见返回两个有用的参数,authCodeTokenimg

    ,img是一张base64转码的图片!也就是验证码图片,还返回的一个和我们登录时参数一致的authCodeToken,可以知道,每一个验证码对应一个  authCodeToken ,猜测 后台对验证码做了哈希,key是value经过某种算法得到,每一次请求生成一对 code和authCodeToken,

      很显然,验证码提交验证,只要提交对应的code和authCodeToken,验证码就会通过。那现在我们验证码也拿到了(base64),authCodeToken也拿到了,那我们就可以模拟登录了

锋云教辅平台爬取(图3)

       2.3-模拟登录

           python代码如下,可以看到代码中,我写了一个info类来存储请求头信息,因为这个信息我会长期用到,且会在里面去添加新的数据

base64
os
time

requests
urllib3
json
csv
PIL Image

hashlib

urllib3.disable_warnings()
request = requests.Session()  info:
    url_code = url_login = url_student_ranking = url_class_list = url_chatting_records = headers = {
        : : : : : : : : : : : : : }
    data_login = {
        : : : : : }

    my_login_info = {}


():
    reponse = request.post(=info.url_code=info.headers=)
    data = json.loads(reponse.text)[][]  (=) f:
        image = base64.b64decode((data).replace())
        f.write(image)
    authCodeToken = json.loads(reponse.text)[][]  Image.open().show()
    authCodeToken


(info):
    (* + + * )
    info.data_login[] = getCaptcha()
    info.data_login[] = ()
    info.headers[] = (((info.data_login)) - )
    info.headers[] = login_reponsts = request.post(=info.url_login=info.headers=json.dumps(
        info.data_login))  info.my_login_info = json.loads(login_reponsts.text)
    info.headers[] = (info.my_login_info[][])
    (login_reponsts.text)
    login_reponsts
__name__ == :
    info = info()
    login(info)

    运行结果:

锋云教辅平台爬取(图4)

其中验证码,我们将他转码保存成一个jpg文件,再用PIL自动打开,以便输入时方便查看,我们很轻松就进去了


2.4-验证码思考

     虽然成功进入了,但是这一路确实不像写的这么容易,都是经过了大量的验证和思考,那么就说说对验证码有哪些坎坷把,经过我的猜测和验证,得到了他的删除条件和储存


    验证码删除条件(猜测):

           1.提交,只要是提交,不管是正确还是错误,都会删掉对应用户的验证码哈希列表的对应值

           2.超过一段时间未验证会删除

     

       验证码存储:(验证,真实)

           1.所有用户共用一个哈希表  (经过验证)

           2.哈希列表会存储,用户所有已请求,未验证且未过期的验证码  (验证)


3.爬取首页相关信息

我们可以看见首页有很多信息,但是我们这里只演示爬取学霸排行榜信息,因为其他信息也是一模一样的方式!

锋云教辅平台爬取(图5)

我们观察刚刚login返回数据可以看见,服务器返回了很多我们自生相关的信息,其中有一条比较重要的信息,token,这是用户每一次登录的唯一标识,在后面的首页数据请求中,他也是请求到数据的重要凭证!

锋云教辅平台爬取(图6)

通过数据可发现,首页的数据基本都是通过ajax请求然后js本地渲染的,所以我们只需要直接模仿ajax请求就可以拿到我们想要的数据,如我们找到学霸排行榜的ajax请求,在network下,XHR是专门过滤ajax请求的,那么我们直接找到

锋云教辅平台爬取(图7)

可以看见queryStudyRanking这个请求就是我们想要的数据,然后去分析他的请求头:‘

锋云教辅平台爬取(图8)

可以看见里面有两个很重要的参数,其中Authorization:token就是我们登录时,服务端返给我们的token,我们只要在请求头上带上他和必要的参数,就能拿到正确的学霸信息handRefresh:0猜测是刷新次数


3.2模拟请求

(info):
    (* + + * )
    data = {: }
    student_ranking = request.post(=info.url_student_ranking=info.headers=json.dumps(data))
    (====) fw:
        writer = csv.DictWriter(fw=[])
        writer.writeheader()  writer.writerows(json.loads(student_ranking.text)[][])  ()

这个函数直接加上段代码运行中就可以,然后调用!


4.爬取聊天区记录

  进入直播室后,我发现只有聊天区内容有爬取的必要,那么我就爬取聊天区内容把!

锋云教辅平台爬取(图9)

可以看见这个文件正是我们要找的文件,分析他的请求头!

锋云教辅平台爬取(图10)

可以看见,他请求头中,并没有发现什么特殊的请求头,但是在下面参数发现了一个不认识的sign,我起初以为是服务器在前面请求中返回的一个sign,但是我找了很久都没有找到,其他几个参数很好识别,timestamp是当前全球时间,那我们着重分析他的sign参数把,既然明眼上看不懂,并且确认了他不是服务器返回的,那么他一定是js本地生成的,那好,那我们进行js调试,

打开Initiator我们可以看见,里面都是此文件在调用时所影响的函数,那我们就找到参数生成的地方,

锋云教辅平台爬取(图11)

我们看见,代码格式如此难读,我们点击左下角{}就可以格式化代码!便于我们阅读,通过一个个函数定位查找,我最终发现其中有一段代码

锋云教辅平台爬取(图12)

可以看见,里面的参数和我们的请求参数一模一样,并且,timestamp也是当前时间戳,既然找到了,那我们就进行调试,看看每一步他打印的是啥,对于如何调试js,我们在前面行号可以打上断点,当页面中某个动作触发这个js时,他就会在这个断点处停下,那我们就可以一步步调试这个js,并且查看每一步的值了,可以看见右上角就是调试的按钮,单步调试和多步调试,只要你启用debug模式,并且此断点经过,他就会停下来。

锋云教辅平台爬取(图13)

调试到最后一步可以发现,原来n是将前面几个参数key和value拼接在一起,然后首尾加上一个特殊的单词,形成的,在进行MD5加密。

 组合形式: polyvChatSign+roomId+弹幕条数结束+fullMessage1+roomId+房间号+start+弹幕条数开始+timestamp+请求时时间毫秒+polyvChatSign

                      例:polyvChatSignend10fullMessage1roomId1820076start0timestamp1594397786321polyvChatSign

那我们知道了,那就轻松了 ,只需要带着正确的参数去请求,就能得到正确的返回,代码如下:

(info):
    = {
        : : : : : }
    start = end = timestamp = time.time() // sign_str = + (end) + + (start) + + (
        (timestamp)) + sign = md5vale(sign_str).upper()
    url = info.url_chatting_records + + (start) + + (
        end) + + ((timestamp)) + + sign
    reponst = request.get(==url)  obj_reponse = json.loads(reponst.text)
    i = obj_reponse[]:
        :
            i += :
            list_chatting = []  list_cookie = []  (obj_reponse[] == ):
        list = obj_reponse[]

        l list:
            l[] = (l[])
            (l[]) == :
                list_cookie.append(l)
                :
                list_chatting.append(l)
                day = (====) fw:
        writer = csv.DictWriter(fw=[])
        chatting list_chatting:
            :
                time.localtime((chatting[]) / ).tm_mday == day:
                    writer.writerow(chatting)  :
                (====) fw:
        writer = csv.DictWriter(fw=[])
        cookie list_cookie:
            :
                time.localtime((cookie[]) / ).tm_mday == day:
                    writer.writerow(cookie)  :
                (end)


(key):
    input_name = hashlib.md5()
    input_name.update(key.encode())
    input_name.hexdigest()

同样的,这两个函数都是基于第一份代码,且独立于第二份代码,因为请求下来的数据既有发言,也有送礼,所以我将他们分类存储在了本地csv文件中,以便数据分析,和长期的数据存储 ,


服务器验证,猜测:

           服务器会拿到前五个参数,会再次进行组合,再次进行MD5加密,得到sign与sign比对,如果通过,则返回

           因为MD5是不可逆转的加密方式,所以爬虫想要解密是不太现实的,只能从头分析,

另外,我发现,对于聊天记录的请求,是不需要经过登录过程的,单独就可以请求到聊天。


5.数据分析

如果爬虫不是为了数据分析,那将毫无意义,那我们就对已请求的数据进行数据分析,我们使用的是pyecharts绘图,可以看如下效果图(部分):

锋云教辅平台爬取(图14)

锋云教辅平台爬取(图15)

锋云教辅平台爬取(图16)

这个绘图好看而且简单,基本上去官网copy代码就能做出好看的数据分析图:

pyecharts

接下来分享部分绘图代码

(list_cookie):
    show_list_cookies = [[][][][][]]
    = []
    cookie list_cookie:
        cookie_ID = re.findall(cookie[])
        cookie_time = (re.findall((cookie))[])
        date = time.localtime(cookie_time / )
        :
            show_list_cookies[date.tm_mday - ][(cookie_ID[]) - ] += :
            x_data = []
    y_data = show_list_cookies
    tl = Timeline()
    i ():
        data = y_data[i - ]
        pie = (
            Pie()
                .add(
                [(z) z (x_datadata)]==[]).set_global_opts(=opts.TitleOpts(.format(i)))
        )
        tl.add(pie.format(i))
    ()
    tl


提供绘上述数据图网址:传送门