朋友最近遇上选课困难,手速慢,总是抢不到心仪的课程,我目前正好找不到爬虫项目练手,于是写了个面向复旦选课系统的抢课小软件帮助朋友抢课

首先需要这些模块:

import requests
import re
import time
import schedule

第一步需要做的是通过身份认证并爬取csrf-token,在选课网站登陆后复制cookies到代码中,这是服务器识别你身份的方式,接着网站会通过生成csrf-token并将其包含在选课请求中来进一步保护信息安全;csrf-token这是网站的一道保护措施,网站根据你提交的cookie信息生成一段随机的csrf-token码,你需要将其爬取并应用到后续的请求中。

class csrfscrapy():
    def csrf_req(self):
        cookies = {
            '_WEU': '********',
            'route': '***********',
            'JSESSIONID': '***********',
            'XK_TOKEN': '****************',
        }#cookie需替换,根据自己登陆后找到的cookie进行替换

        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.7',
            'Accept-Language': 'en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7,en-GB;q=0.6',
            'Cache-Control': 'max-age=0',
            'Connection': 'keep-alive',
            'Referer': 'http://yjsxk.fudan.sh.cn/yjsxkapp/sys/xsxkappfudan/*default/index.do',
            'Upgrade-Insecure-Requests': '1',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.46',
        }

        res = requests.get(
            'http://yjsxk.fudan.sh.cn/yjsxkapp/sys/xsxkappfudan/xsxkHome/gotoChooseCourse.do',
            cookies=cookies,
            headers=headers,
            verify=False,
        )
        return res

    def pares_csrf(self, res):
        csrf=re.findall('csrfToken\W+value=\W+([a-z0-9]+)\W', res.text)
        csrfstr=csrf[0]
        print(csrfstr)
        return csrfstr#从html中找到csrftoken

    def run(self):
        responce=self.csrf_req()
        csrfstr=str(self.pares_csrf(responce))
        return csrfstr

之后要解决的就是提交选课请求,注意在request.post中有个参数param(dict类型),这是时间13位时间戳,需要整合到url里一起提交;另外一个参数是data(dict类型),里面包含csrf-token和选课信息bjdm,lx,bqmc和身份认证csrfToken。其中bjdm是课程代码,lx是课程类型(数字,专业外语对应7,其他选修课10,公共选修课9),bqmc是课程所在的分类(如专业外语,其他选修课,公共选修课等),lx和bqmc钥匙可以通过自己要选的课程所在的门类进行填写,比如要选的xxx课程在公共选修课,那么'lx': '9', 'bqmc': '公共选修课’。bjdm钥匙需要在浏览器开发者模式中寻找对应的代码,后续详细说明。csrfToken钥匙则是上个部分爬取的结果。

class courceburglar():
    def __init__(self, csrf):
        self.csrf=csrf

    def get_millisecond():

        millis = int(round(time.time() * 1000))
        return millis

    def cource_req(self):
        cookies = {
            '_WEU': '********',
            'route': '***********',
            'JSESSIONID': '***********',
            'XK_TOKEN': '****************',
        }#cookie需替换,根据自己登陆后找到的cookie进行替换

        headers = {
            'Accept': 'application/json, text/javascript, */*; q=0.01',
            'Accept-Language': 'en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7,en-GB;q=0.6',
            'Connection': 'keep-alive',
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
            'Origin': 'http://yjsxk.fudan.sh.cn',
            'Referer': 'http://yjsxk.fudan.sh.cn/yjsxkapp/sys/xsxkappfudan/xsxkHome/gotoChooseCourse.do',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.46',
            'X-Requested-With': 'XMLHttpRequest',
        }

        timestramp = courceburglar.get_millisecond()
        print(timestramp)
        params = {
            '_': timestramp,
        }

        data = {
            'bjdm': '************',
            'lx': '7',
            'bqmc': '专业外语',
            'csrfToken': self.csrf,
        }

        response = requests.post(
            'http://yjsxk.fudan.sh.cn/yjsxkapp/sys/xsxkappfudan/xsxkCourse/choiceCourse.do',
            params=params,
            cookies=cookies,
            headers=headers,
            data=data,
            verify=False,
        )
        print(response.status_code)

    def run(self):
        responce=self.cource_req()

如何寻找cookie:首先登陆自己选课账号,进入选课界面,f12打开开发者界面中的网络(或者network)选项卡,刷新你的浏览器,在你的开发者界面会有如下的状态,打开任意一个形如r'\w+do\?_=\d+'命名的文件的请求头(或者headers)选项卡,在request headers中包含了cookie信息,稍微加工改成dict类型,替换掉两部分代码中cookies部分.

如何寻找课程代码:清空你的开发者界面,将你的选课类别调整到你需要选的类型中,比如你要选公共选修课的课程,你将浏览器中的选项点击到公共选修课,与此同时,在开发者界面会出现响应的文件,打开形如'loadxxxxxcourceinfo.do?_=xxxxxx'文件,打开preview的选项卡,在众多检索出的课程中找到你要的课程的BJDM,并替换掉data参数里面的bjdm。

代码最后就是时间部分:通过schedule模块控制选课请求发送时间。选课系统一般是下午一点开放。

if __name__=='__main__':
    def job(cs):
        crsreq = courceburglar(cs)
        crsreq.run()
    csrf=csrfscrapy()
    csrftoken=csrf.run()
    #schedule.every(5).to(10).minutes.do(job, csrftoken) #5-10分钟随机做一次
    schedule.every().day.at("13:00").do(job, csrftoken)
    while True:
        schedule.run_pending()
        time.sleep(1)

具体参考这位大佬的内容:(10条消息) Python3学习(八):使用schedule模块定时执行任务_猪笨是念来过倒的博客-CSDN博客_schedule.run_pending()

整合起来全代码如下:

import requests
import re
import time
import schedule

class csrfscrapy():
    def csrf_req(self):
        cookies = {
            '_WEU': '********',
            'route': '***********',
            'JSESSIONID': '***********',
            'XK_TOKEN': '****************',
        }#cookie需替换,根据自己登陆后找到的cookie进行替换

        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.7',
            'Accept-Language': 'en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7,en-GB;q=0.6',
            'Cache-Control': 'max-age=0',
            'Connection': 'keep-alive',
            'Referer': 'http://yjsxk.fudan.sh.cn/yjsxkapp/sys/xsxkappfudan/*default/index.do',
            'Upgrade-Insecure-Requests': '1',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.46',
        }

        res = requests.get(
            'http://yjsxk.fudan.sh.cn/yjsxkapp/sys/xsxkappfudan/xsxkHome/gotoChooseCourse.do',
            cookies=cookies,
            headers=headers,
            verify=False,
        )
        return res

    def pares_csrf(self, res):
        csrf=re.findall('csrfToken\W+value=\W+([a-z0-9]+)\W', res.text)
        csrfstr=csrf[0]
        print(csrfstr)
        return csrfstr#从html中找到csrftoken

    def run(self):
        responce=self.csrf_req()
        csrfstr=str(self.pares_csrf(responce))
        return csrfstr

class courceburglar():
    def __init__(self, csrf):
        self.csrf=csrf

    def get_millisecond():

        millis = int(round(time.time() * 1000))
        return millis

    def cource_req(self):
        cookies = {
            '_WEU': '********',
            'route': '***********',
            'JSESSIONID': '***********',
            'XK_TOKEN': '****************',
        }#cookie需替换,根据自己登陆后找到的cookie进行替换

        headers = {
            'Accept': 'application/json, text/javascript, */*; q=0.01',
            'Accept-Language': 'en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7,en-GB;q=0.6',
            'Connection': 'keep-alive',
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
            'Origin': 'http://yjsxk.fudan.sh.cn',
            'Referer': 'http://yjsxk.fudan.sh.cn/yjsxkapp/sys/xsxkappfudan/xsxkHome/gotoChooseCourse.do',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.46',
            'X-Requested-With': 'XMLHttpRequest',
        }

        timestramp = courceburglar.get_millisecond()
        print(timestramp)
        params = {
            '_': timestramp,
        }

        data = {
            'bjdm': '************',
            'lx': '7',
            'bqmc': '专业外语',
            'csrfToken': self.csrf,
        }

        response = requests.post(
            'http://yjsxk.fudan.sh.cn/yjsxkapp/sys/xsxkappfudan/xsxkCourse/choiceCourse.do',
            params=params,
            cookies=cookies,
            headers=headers,
            data=data,
            verify=False,
        )
        print(response.status_code)

    def run(self):
        responce=self.cource_req()

if __name__=='__main__':
    def job(cs):
        crsreq = courceburglar(cs)
        crsreq.run()
    csrf=csrfscrapy()
    csrftoken=csrf.run()
    #schedule.every(5).to(10).minutes.do(job, csrftoken) #5-10分钟随机做一次
    schedule.every().day.at("13:00").do(job, csrftoken)
    while True:
        schedule.run_pending()
        time.sleep(1)

后续改进可以写一个可以自动生成需要的选课信息并将信息放进队列的模块,以及多线程同时发送选课请求的模块。

Logo

GitCode 天启AI是一款由 GitCode 团队打造的智能助手,基于先进的LLM(大语言模型)与多智能体 Agent 技术构建,致力于为用户提供高效、智能、多模态的创作与开发支持。它不仅支持自然语言对话,还具备处理文件、生成 PPT、撰写分析报告、开发 Web 应用等多项能力,真正做到“一句话,让 Al帮你完成复杂任务”。

更多推荐