吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 18291|回复: 57
收起左侧

[Python 原创] 超星(学习通)网络协议分析

  [复制链接]
liuyunfz 发表于 2020-11-21 12:14

前言

开头声明,本教程仅供学习,请勿将其用于商业或非法用途。
个人感觉本文难度为爬虫入门小进阶,请大佬轻喷。

正文

登陆

网页分析

超星平台的旧版登陆是需要验证码的old_sign_in
然而通过新版进去却不需要直接输入验证码(尚未测试过多次密码错误后是否会出现验证码)
new_sign_in
本着能简化就简化的理念(主要是懒),我们直接抓取这个新版的登陆页面。https://passport2.chaoxing.com/login?fid=&newversion=true&refer=http%3A%2F%2Fi.chaoxing.com
尝试登陆,老套路了,避免登陆成功后跳转出现很多包,我们直接输入错误的登陆信息。结果很符合我们的预期,只多出了一个包
post_sign_in
查看预览,正好是提示我们的“用户名或密码错误”,我们查看提交的表单,一个个来分析。
post_form_sign_in

fid: -1
uname: 11011110010
password: bGl1eXVuZno=
refer: http%3A%2F%2Fi.chaoxing.com
t: true

不管fid,-1应该是一个常量。uname正好是我们输入的手机号。password结尾有“=”,我们大胆猜测是base64编码
post_form_password
正好是我们输入的密码,那么password也搞定了。t是一个布尔值,也作为常量,refer我就不多解释了,也把它作为常量吧。

代码撰写

照着我们的分析,我们尝试利用python代码模拟我们的登陆操作。

import requests,base64
def sign_in(username:str,password:str):
    url="https://passport2.chaoxing.com/fanyalogin"
    headers={
        'Accept':'application/json, text/javascript, */*; q=0.01',
        'Accept-Encoding':'gzip, deflate, br',
        'Accept-Language':'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
        'Connection':'keep-alive',
        'Content-Length':'95',
        'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8',
        'Cookie':'',
        'Host':'passport2.chaoxing.com',
        'Origin':'https://passport2.chaoxing.com',
        'Referer':'https://passport2.chaoxing.com/login?fid=&newversion=true&refer=http%3A%2F%2Fi.chaoxing.com',
        'Sec-Fetch-Dest':'empty',
        'Sec-Fetch-Mode':'cors',
        'Sec-Fetch-Site':'same-origin',
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.193 Safari/537.36 Edg/86.0.622.68',
        'X-Requested-With':'XMLHttpRequest'
    }
    data="fid=-1&uname={0}&password={1}&refer=http%253A%252F%252Fi.chaoxing.com&t=true".format(username,base64.b64encode(password.encode()).decode())
    print(base64.b64encode(password.encode()).decode())
    rsp=requests.post(url=url,headers=headers,data=data)
    print(rsp.text,rsp.status_code)

调试一下,我这里把私密信息打了马赛克,但不影响看出成功。
function_sign_in
为了后续内容的顺利进行,这边我们再把登陆成功的cookie记录下来

cookieStr = '' 
    for item in rsp.cookies:
        cookieStr = cookieStr + item.name + '=' + item.value + ';'
    print(cookieStr)

cookie_sign_in

获取课程

网页分析

抓取课程信息

因为不能确定个人空间里面的课程内容是通过json获取数据在本地渲染的还是服务器就渲染好后传给用户的。所以我们还是抓包看看。
post_zone
查找我们的课程,发现在源代码里并没有找到,所以大概率是第一种可能,所以我们尝试往下找传递课程数据的json包。
post_zone_true
可以看到,成功找到了我们要的数据包,可惜并不是我们想要的json包,但问题也不大,只是增加了我们代码解析时的复杂度。然后具体看我们抓到的这个课程数据包,请求方式为get,唯一麻烦的是参数s
get_course
这个s参数肯定不会无故出现,所以我们往前寻找有没有前面就给出了这个地址的,我们从最前面找,看看最初的那个空间地址包里面有没有这个地址。
然而正当我打算尝试时,我发现把s参数去掉了一样可以正常访问我们的课程数据http://mooc1-2.chaoxing.com/visit/courses
get_course_withNoS
那么为了尽可能简化,我们还是能偷懒就偷懒。
然后我们跳到代码阶段,先试试前面的cookie能否正常使用,并且读取到这个课程网页的信息。

点我跳转

解析课程信息

因为课程数据已经在http://mooc1-2.chaoxing.com/visit/courses 这个包里给出了,但它又不是一个json,所以我们把它作为一个单独的网页来进行结构分析。
post_deal_data
可以看到课程是一个ul结构下的多个li构成的。我们需要的里面每个课程的courseId classId与对应跳转的url(因为url里包含了courseid与classid,所以我们可以选择一是通过xpath先储存并关联url与对应的课程id;亦或是只获取url,后面再从url里读取参数courseid,classid等)。选中我们要的元素,右键复制xpath。
html_xpath
这里我选择了第二种方案,只获取url,courseid从url中读取。我们继续跳到代码步骤。

点我跳转

代码撰写

尝试cookie与读取课程信息

将登陆获取到的cookie合并成cookiestr后传入获取课程信息的函数

def get_course(cookie:str):
    course_headers={
        'Cookie':cookie,
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36 Edg/85.0.564.51'
    }
    course_rsp=requests.get(url="http://mooc1-2.chaoxing.com/visit/courses",headers=course_headers)
    print(course_rsp.text)

funtion_get_course
可以看到,成功获取到了课程信息,同时也印证了我们前面登录函数的可行性。
之后回到网页,我们继续分析网页结构,使python可以解析我们的所有课程及相关信息。  

点我跳转  

课程xpath书写与重定向问题
def get_course(cookie:str):
    course_headers={
        'Cookie':cookie,
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36 Edg/85.0.564.51'
    }
    course_rsp=requests.get(url="http://mooc1-2.chaoxing.com/visit/courses",headers=course_headers)
    if course_rsp.status_code==200:
        from lxml import etree
        class_HTML=etree.HTML(course_rsp.text)
        print("处理成功,您当前已开启的课程如下:\n")
        i=0
        global course_dict
        course_dict={}
        for class_item in class_HTML.xpath("/html/body/div/div[2]/div[3]/ul/li[@class='courseItem curFile']"):
            #courseid=class_item.xpath("./input[@name='courseId']/@value")[0]
            #classid=class_item.xpath("./input[@name='classId']/@value")[0]
            try:              
                class_item_name=class_item.xpath("./div[2]/h3/a/@title")[0]
                #等待开课的课程由于尚未对应链接,所有缺少a标签。
                i+=1
                print(class_item_name)
                course_dict[i]=[class_item_name,"https://mooc1-2.chaoxing.com{}".format(class_item.xpath("./div[1]/a[1]/@href")[0])]
            except:
                pass
        print("———————————————————————————————————")
    else:
        print("error:课程处理失败")

code_xpath
课程读取成功了,课程的url与name也作为list被我们关联储存到了course_dict这个dict中(为了使序号与课程进行关联,并且保持有序性)
课程获取完后我们就可以开始针对单个课程,读取里面的待完成视频任务添加到程序任务里。然而,仔细观察我们从html里获取到的url
before_url
与我们手动访问课程获取的url
after_url
发现两者并不相同,我们尝试访问html里的url,然后抓包看发生了什么
status_302
不出所料,果然url发生了302重定向。
本来requests库是会识别302并自动跟随跳转的,但不知道为什么我这里出现了错误,并未跟随跳转,甚至让我一度怀疑问题出在了cookie上。

def deal_course(url:str):
    course_302_url=url
    course_headers={
        'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
        'Accept-Encoding':'gzip, deflate, br',
        'Accept-Language':'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
        'Connection':'keep-alive',
        'Cookie':cookieStr,
        'Host':'mooc1-2.chaoxing.com',
        'Sec-Fetch-Dest':'document',
        'Sec-Fetch-Mode':'navigate',
        'Sec-Fetch-Site':'none',
        'Upgrade-Insecure-Requests':'1',
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36 Edg/85.0.564.51'
    }
    #302跳转,requests库默认追踪headers里的location进行跳转,使用allow_redirects=False
    course_302_rsp=requests.get(url=course_302_url,headers=course_headers,allow_redirects=False)
    new_url=course_302_rsp.headers['Location']

这样,只需要将html里获取到的url传入到这个函数里,new_url就是我们要的真正的课程地址了。
真正的课程地址获取完成了,接下来我们继续回到网络分析,分析单个课程中我们需要完成的任务。

课程任务

分析网页

任意进入到一个课程中,通过观察可以发现:白色无内容的——没有任务;橙色有数字的——数字代表该章节的任务数;绿色勾勾的——有任务但已完成了的。
misson_html
刷新一下,发现该课程地址就已经给出了相关的章节信息,即在服务端已经渲染好了再传输到用户端,并没有额外的json数据包。
post_course_html
所以我们直接右键“检查”,查看网页元素。
course_html_element
找规律可以发现,正常的没有课程任务的,em元素的class是“blank”,如果有任务但已完成则是“openlock”,如果有任务但未完成则class为“orange”,且其text为任务数。
知道了这个,我们就好写了,只需要判断em元素class属性是orange的,并把它对应的url写入我们的任务列表里。所以接下来回到代码环节  

代码编写

章节信息判断与未完成任务读取
通过上面的分析,以及课程信息url的获取,将其传入的requests,获取源代码后即可进行判断处理。

def add_misson(url:str):
    course_headers={
        'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
        'Accept-Encoding':'gzip, deflate, br',
        'Accept-Language':'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
        'Connection':'keep-alive',
        'Cookie':cookieStr,
        'Host':'mooc1-2.chaoxing.com',
        'Sec-Fetch-Dest':'document',
        'Sec-Fetch-Mode':'navigate',
        'Sec-Fetch-Site':'none',
        'Upgrade-Insecure-Requests':'1',
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36 Edg/85.0.564.51'
    }
    course_rsp=requests.get(url=url,headers=course_headers)
    course_HTML=etree.HTML(course_rsp.text)
    #为防止账号没有课程或没有班级,需要后期在xpath获取加入try,以防报错
    chapter_mission=[]
    try:
        for course_unit in course_HTML.xpath("/html/body/div[5]/div[1]/div[2]/div[3]/div"):
            print(course_unit.xpath("./h2/a/@title")[0])
            for chapter_item in course_unit.xpath("./div"):
                chapter_status=chapter_item.xpath("./h3/span[@class='icon']/em/@class")[0]
                if chapter_status == "orange":
                    print("----",chapter_item.xpath("./h3/span[@class='articlename']/a/@title")[0],"      ",chapter_item.xpath("./h3/span[@class='icon']/em/text()")[0])
                    chapter_mission.append("https://mooc1-2.chaoxing.com{}".format(chapter_item.xpath("./h3/span[@class='articlename']/a/@href")[0]))
                else:    
                    print("----",chapter_item.xpath("./h3/span[@class='articlename']/a/@title")[0],"      ",chapter_item.xpath("./h3/span[@class='icon']/em/@class")[0])
    except:
        pass
    print("课程读取完成,共有%d个章节可一键完成"%len(chapter_mission))

function_add_misson

视频任务点

因为要实现的是一键刷课程视频,所以接下来我们要读取的是章节任务里面的视频任务点。

网页分析

还是老样子,先抓一下包。
post_video_misson
可以从预览里看出,章节网页似乎并没有完全渲染完成后再传输给我们。通过查看响应里面的代码,我们也验证了猜想,并未找到有关的视频数据。所以我们尝试往下寻找包含视频信息的json包或者html网页。
post_video
最后,在这个“/knowledge/cards”里的包里找到了我们要的数据,可惜它也不是json,而是一个html,里面有js代码。所以我们还得自己提取代码,并转化成python可识别的内容。这边的分析还是比较麻烦的,但并不算难,只能说不熟练的话需要多试试。这个就请自己尝试看看吧,这边就不给出具体分析过程了(因为自己也想不起来了,也不想再分析一遍,懒)。不过如果实在有问题的话也可以联系我,和我交流交流。那我们就直接进入代码环节然后给出代码。
我们顺便分析一下这些视频数据代表的内容。
"isPassed"是任务点是否已完成,true为已完成,false为未完成,所以我们可以通过判断它的属性来直接判断是否需要对该视频进行处理。
"type": "video"是类型为视频,也是我们判断是否要添加到任务列表的一个条件(因为我们要实现的只有刷超星视频),其他还有可能出现ppt这种。
其他其实通过名字也都能知道是什么,主要还是要看后面我们需要用到什么属性。

代码编写

为了把js代码提取出来并变成python中的dict或list,我这里用到了正则表达式,取出我们要的内容。

def deal_misson(missons:list,class_cpi:str):  
    for chapter_mission_item in missons:
        result = parse.urlparse(chapter_mission_item)
        chapter_data=parse.parse_qs(result.query)
        print(chapter_data)
        medias_url="https://mooc1-2.chaoxing.com/knowledge/cards?clazzid={0}&courseid={1}&knowledgeid={2}&num=0&ut=s&cpi={3}&v=20160407-1".format(chapter_data.get('clazzid')[0],chapter_data.get('courseId')[0],chapter_data.get('chapterId')[0],class_cpi)
        class_headers={
        'Cookie':cookieStr,
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36 Edg/85.0.564.51'
        }
        medias_rsp=requests.get(url=medias_url,headers=class_headers)
        medias_HTML=etree.HTML(medias_rsp.text)
        medias_text=medias_HTML.xpath("//script[1]/text()")[0]
        import re,json
        pattern = re.compile(r'attachments":([\s\S]*),"defaults"') 
        re_result=re.findall(pattern,medias_text)[0]
        reportUrl=re.findall(r'reportUrl":([\s\S]*),"chapterCapture"',medias_text)[0]
        reportUrl=reportUrl.replace("\"","")
        result_json=json.loads(re_result)

这里传入的missons是每个章节的链接构成的一个list,至于class_cpi是课程的cpi信息,每个课程中的所有章节中的所有视频都共用这个cpi属性,而这个是后面完成视频要用到的,针对我们现在判断视频任务并用不到。result_json即是我们从网页代码中提取出的json。
misson_debug
可以看到,调试成功了,而且是很完美的json格式,loads我们可以用list的操作方式轻松的调用其中的数据信息。
章节中的视频信息我们也处理完成了,到此我们的准备工作已经都完成了。账号登陆——判断课程信息——判断课程中的章节任务——判断章节里面的视频任务,最后一步就是完成章节里的视频任务了,也是我们最后的大头。

任务完成解析

抓包分析

打开“开发者选项”,进行抓包,然后播放视频。出现了两个包
post_video_status
第一个是视频的包,用于向服务器请求视频数据,从其中的type,以及一些其他的headers都能看出来。
post_video_first
第二个包,就是我们要的用来完成任务的包了。它传输的是你观看的数据,返回的isPassed来说明你的任务是否完成了。因为我们只是播放了一下,所以任务当然没能完成,所以返回的数据是{"isPassed":false}。
然后我们等视频放完,即将任务点完成,看它会发送什么包,与前面这个包有什么区别。
post_video_finish

可以看出主要区别就在这个playingTime,翻译下来也很好理解,应该就是我们播放了的时间。只要这个时间等于视频的总长度,那么就算我们就完成了。接下来我们分析下提交的整个表单。

key 含义 来源
clazzId 章节ID 单个章节的url里
playingTime 视频播放的时间 自己修改,通过读取视频的长度信息
duration 时间 与playingtime同步,都为视频的总时长
clipTime 起止时间? “0”加上总时长,即与前面两个参数一样,但在前方加上"0"
objectId 视频的“objectId”属性 视频任务点中的所有视频信息“result_json” 里面的单个视频数据中给出。
otherInfo 视频的“otherInfo”属性 视频任务点中的所有视频信息“result_json” 里面的单个视频数据中给出。
jobid 视频的“jobid”属性 视频任务点中的所有视频信息“result_json” 里面的单个视频数据中给出。
userid 用户id信息 由登陆时获取的cookie里获取
isdrag 视频状态信息,0:默认;3:播放;2:暂停;4:视频结束。 我们要用的是状态4,将其作为常量
view 终端情况? 值为"pc",我们将其作为常量不修改它
enc 是其他表单所有数据经过算法计算后得出的数据校验码 全篇的最难点,见后面分析
rt 不清楚 将值0.9作为常量
dtype 任务类型 因为我们要实现的功能仅有视频,所以将值Video作为常量
_t 时间戳 可以添加实时时间戳,也可以沿用,服务端不会进行校验

以下我将给出以上部分参数含义及来源的代码依据。

clipTime

post_clipTime

clipTime = (params.startTime || "0") + "_" + (params.endTime || params.duration);

以上是源代码,不难看懂,clipTime的值是 起始时间或"0" + "_"  + 终止时间或duration 。

isdrag

post_isdrag

var isdrag = 0;
switch (evt) {
case "play":
    isdrag = 3;
    break;
case "pause":
    isdrag = 2;
    break;
case "ended":
    isdrag = 4;
    break
}

因为我们要完成任务,自然是视频播放完结束后发送的包,所以为ended,所以我们用“4”作为常量值。

rt

post_rt

var rt = data ? (data.rt ? data.rt : 0.9) : 0.9;

没学过js的我也不太清楚这段话的具体作用,不过我们把“0.9”作为常量传输不会有任何异常。
这里有个小细节,就是当我们全站检索“rt”时因为字符数较少,会出现较多的结果(因为很多函数或变量里都会包含这两个字符,所以我们搜索 rt 的定义语句——“var rt”,即可减少结果。

enc

全文的最难点,以上所有数据的校验码。以下将给出我个人尝试破解的详细思路。
首先我选择全站检索enc,尝试找到它的由来。
post_enc_1
可以看到,出现了很多的数据(15个文件共77行匹配),所以为了缩小范围,我尝试跟上面的rt一样操作,查询enc的赋值语句。
post_enc_2
很有效果,结果只有4个文件5行匹配。而如图红色圈起来的代码,虽然都对enc进行了赋值,但都不是我们要的语句。我们一个个手动排除,最后我锁定了 https://mooc1-2.chaoxing.com/ananas/videojs-ext/videojs-ext.min.js 这个文件,细心的朋友可能已经发现了,就是我们刚才找clipTime的那个地方。

var sendLog_ = function(player, isdrag, currentTimeSec, callback) {
    if (!params.reportUrl) {
        return
    }
    if (params.isFiled == 1 || params.state == 1) {
        return
    }
    var format = "[{0}][{1}][{2}][{3}][{4}][{5}][{6}][{7}]"
      , clipTime = (params.startTime || "0") + "_" + (params.endTime || params.duration);
    var enc = Ext.String.format(format, params.clazzId, params.userid, params.jobid || "", params.objectId, currentTimeSec * 1000, "d_yHJ!$pdA~5", params.duration * 1000, clipTime);
    var rurl = [params.reportUrl, "/", params.dtoken, "?clazzId=", params.clazzId, "&playingTime=", currentTimeSec, "&duration=", params.duration, "&clipTime=", clipTime, "&objectId=", params.objectId, "&otherInfo=", params.otherInfo, "&jobid=", params.jobid, "&userid=", params.userid, "&isdrag=", isdrag, "&view=pc", "&enc=", md5(enc), "&rt=", params.rt, "&dtype=Video", "&_t=", new Date().getTime()].join("");
    logFunc(player, rurl, callback)
};

把里面关于enc的代码提取出来

var format = "[{0}][{1}][{2}][{3}][{4}][{5}][{6}][{7}]"

var enc = Ext.String.format(format, params.clazzId, params.userid, params.jobid || "", params.objectId, currentTimeSec * 1000, "d_yHJ!$pdA~5", params.duration * 1000, clipTime);

"&enc=", md5(enc)

大致意思就是令

enc=[clazzId][userid][jobid][objectId][currentTimeSec 1000]["d_yHJ!$pdA~5"][duration 1000][clipTime]  

然后再将取enc的md5,作为表单的校验码。这里面的参数多是前面已经分析过了的,只是中间多了一个字符串常量 \"d_yHJ!$pdA~5\" 。还有一个可能不太清楚的就是currentTimeSec了。
我们设置断点,一边看看这个currentTimeSec到底是啥,顺便验证下我们前面的是否正确。
html_pause
在这里设置断点,然后我们刷新后重新播放视频。将鼠标移到enc处,即可看到enc的值预览。
html_enc这里因为隐私问题做打码处理,可以自己尝试,和我们前面说的基本无二。而currentTimeSec我们也发现了,currentTimeSec 1000跟 duration 1000 的值一模一样。
截止目前,数据包里面的所有参数我们已经都弄明白了,接下来就是写代码就好了。

代码书写

以下是enc的返回函数

def encode_enc(clazzid:str,duration:int,objectId:str,otherinfo:str,jobid:str,userid:str):
    import hashlib
    data="[{0}][{1}][{2}][{3}][{4}][{5}][{6}][0_{7}]".format(clazzid,userid,jobid,objectId,duration*1000,"d_yHJ!$pdA~5",duration*1000,duration)
    print(data)
    return hashlib.md5(data.encode()).hexdigest()

然后是数据包的代码

if video_item.get("isPassed") == True:
    pass
else:
    if video_item.get("type") == "video":
        objectId=video_item.get("objectId")
        otherInfo=video_item.get("otherInfo")
        jobid=video_item.get("jobid")
        name=video_item.get('property').get('name')
        status_url="https://mooc1-1.chaoxing.com/ananas/status/{}?k=&flag=normal&_dc=1600850935908".format(objectId)
        status_rsp=requests.get(url=status_url,headers=class_headers)
        status_json=json.loads(status_rsp.text)
        duration=status_json.get('duration')
        dtoken=status_json.get('dtoken')
        print(objectId,otherInfo,jobid,uid,name,duration,reportUrl)
        multimedia_headers={
            'Accept':'*/*',
            'Accept-Encoding':'gzip, deflate, br',
            'Accept-Language':'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
            'Connection':'keep-alive',
            'Content-Type':'application/json',
            'Cookie':cookieStr,
            'Host':'mooc1-1.chaoxing.com',
            'Referer':'https://mooc1-1.chaoxing.com/ananas/modules/video/index.html?v=2020-0907-1546',
            'Sec-Fetch-Dest':'empty',
            'Sec-Fetch-Mode':'cors',
            'Sec-Fetch-Site':'same-origin',
            'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36 Edg/85.0.564.51'
        }
        import time
        elses="/{0}?clazzId={1}&playingTime={2}&duration={2}&clipTime=0_{2}&objectId={3}&otherInfo={4}&jobid={5}&userid={6}&isdrag=4&view=pc&enc={7}&rt=0.9&dtype=Video&_t={8}".format(dtoken,chapter_data.get('clazzid')[0],duration,objectId,otherInfo,jobid,uid,encode_enc(chapter_data.get('clazzid')[0],duration,objectId,otherInfo,jobid,uid),int(time.time()*1000))
        reportUrl_item=reportUrl+str(elses)
        print(reportUrl_item)
        multimedia_rsp=requests.get(url=reportUrl_item,headers=multimedia_headers)
        print(multimedia_rsp.text)

结语

这篇文章还是断断续续写了一个礼拜,里面我尽可能展示了我的思路。可以当作爬虫学习入门的小进阶,所以还是很值得记录与分享的。
最后再次强调,本文仅作为经验分享,请勿将其中内容二次修改用作商业用途。

免费评分

参与人数 35吾爱币 +50 热心值 +31 收起 理由
窥屏专用小马甲 + 1 + 1 谢谢@Thanks!
j2000yk + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Lthero + 2 + 1 好帖子!我今天就试试
SevenThRe + 1 用心讨论,共获提升!
GVWN + 1 + 1 鼓励转贴优秀软件安全工具和文档!
zjf123456 + 2 + 1 用心讨论,共获提升!
一方旅人 + 1 我很赞同!
zjh4473 + 1 + 1 对新手还是有帮助的。比较基础。
yunruifuzhu + 1 + 1 上次我研究过,由于过验证码太麻烦,就弄成js脚本了
bambooslip + 2 + 1 用心讨论,共获提升!
xhtdtk + 1 + 1 用心讨论,共获提升!
橘子好好吃 + 1 + 1 用心讨论,共获提升!
阝里 + 1 + 1 用心讨论,共获提升!
infernal + 1 用心讨论,共获提升!
whiteking + 1 + 1 用心讨论,共获提升!
bapi + 1 + 1 用心讨论,共获提升!
chhzll + 1 + 1 谢谢@Thanks!
Java_S + 2 + 1 牛逼
2586blj + 1 + 1 我很赞同!
爱游泳的冰激凌 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
lelea + 1 + 1 热心回复!
xingdongpai + 2 + 1 用心讨论,共获提升!
cuijianyu + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
CYSOSYC + 1 用心讨论,共获提升!
维常之华 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
asdkiyuas + 1 + 1 谢谢@Thanks!
zangguicheng + 1 + 1 用心讨论,共获提升!
tzy0830 + 1 + 1 谢谢@Thanks!
一杯白咖啡 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
时空之外 + 1 用心讨论,共获提升!
三滑稽甲苯 + 1 + 1 一个小瑕疵:网站用的是“登录”,而不是“登陆”
TinySoul + 1 + 1 感谢您的宝贵建议,我们会努力争取做得更好!
我是一个外星人 + 1 用心讨论,共获提升!
hi52 + 1 + 1 我很赞同!
苏紫方璇 + 15 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| liuyunfz 发表于 2021-3-10 13:16
ruigewoaini 发表于 2021-3-7 21:10
请问大佬,关于学习通提交作业能不能写个思路呢,我卡在这个地方了,请你解答一下

这个我最近也有在研究,不过不幸的是我也卡住了。测验的暂时保存我是已经基本实现了,但是作业提交里面的url有个pos参数,它似乎是由一段混淆的js代码产生的。所以我也不算大佬了,如果您有所突破的话也希望您能不吝赐教。
 楼主| liuyunfz 发表于 2021-3-11 19:44
山野村夫-陈墨 发表于 2021-3-11 00:04
网络协议分析这块为什么不交给 selenium去做呢?
感觉你对服务器开发很熟悉,一套流程下来很顺畅。这里面 ...

selenium的缺点很明显,就类似脚本,占用内存大,跨平台性不高,适合个人的单账号使用。
但是如果分析出了协议包,那么我可以用一些语言实现跨平台,可以在此基础上多线程多进程,多账号并发,可操作性很强。
这一套流程虽然看起来流畅,但其实是我在花了好几天钻研后实现的开发,然后写出来时又重新理了一遍逻辑。
然后就是请求头,区别肯定是有的
{'User-Agent': 'python-requests/2.24.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
这是requests库session的默认headers。最为不同的就是ua了吧,大多数网页也都会根据ua来判断你是否是用浏览器来访问。其他基本没太大区别和影响,只是要根据不同的网站再额外添加一些参数了
TinySoul 发表于 2020-11-21 15:14
skymilong 发表于 2020-11-21 15:15
        感谢发布原创作品,吾爱破解论坛因你更精彩!
雷欧库珀 发表于 2020-11-21 17:46
啊这,太长了。
chenxiang12159 发表于 2020-11-21 18:03
啊这 太牛了
egbert_tao 发表于 2020-11-22 14:43
啊,这也太牛了吧
ahang3421 发表于 2020-11-22 17:05
学习新思想,争做新青年
asdkiyuas 发表于 2020-11-23 17:29
嘿嘿,我也学习实践一下
ouyahayu 发表于 2020-11-24 15:03
太牛了8,网课人🐮🍺
Ly老Y 发表于 2020-11-24 19:28
写的真好
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-22 06:55

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表