python获取职教云信息

news2024/11/27 21:07:00

⭐作者介绍:大二本科网络工程专业在读,持续学习Java,努力输出优质文章
⭐作者主页:@逐梦苍穹
⭐所属专栏:项目。

目录

  • 1、需求
    • 1.1、推荐课程
    • 1.2、课程详情
  • 2、思路分析
    • 2.1、推荐课程
      • 完整代码
    • 2.2、课程详情
      • 找到JSON数据
      • 最终思路
      • 新的问题
      • 完整代码

1、需求

1.1、推荐课程

在这里插入图片描述

1.2、课程详情

对于上面每个课程,点击图片能跳转到对应课程页面,爬取中上面每个课程页面中的如下红色线划出的信息并保存为“课程详细列表.csv”。各栏目依次为:课程名称,所属专业,学时安排,课程进度,学时数,累计选课人数,本期选课人数,学员所属单位数,本期学员所属单位数,累计互动次数,本期互动次数,累计日志总数。
在这里插入图片描述

2、思路分析

爬虫代码通用的部分,也是最重要的部分:请求头和资源访问路径。
请求头是模拟普通用户通过浏览器访问的行为,以此初步跳过爬虫检测机制
在这里插入图片描述
这里的推荐课程和课程详情的爬取,均使用解析JSON格式的数据的方式!

2.1、推荐课程

首先我们知道,对于前后端交互数据,需要使用Ajax。所以对浏览器请求抓包可以得到:
在这里插入图片描述
在这里可以看到,这个JSON响应的数据里面包含了一整页的数据,这些数据在portalMooc_selectCourseList.action里面,所以先定位这个资源的路径是什么
在这里插入图片描述
可以看到,资源路径是:https://mooc.icve.com.cn/patch/zhzj/portalMooc_selectCourseList.action
但是如果去访问这个路径,会发现无法请求。
查看请求头,发现是POST:
在这里插入图片描述
使用python代码去访问,也是失败。
在这里插入图片描述
POST请求的资源,需要传入请求数据data,如下便是可以成功访问资源的代码:
在这里插入图片描述
那么这个data是怎么来的呢?
查看负载:
在这里插入图片描述
至此,关于推荐课程单页的获取,就结束了。接下来爬取多页,无非就是加循环。解析数据无非就是把json的键值对进行提取,不再赘述了。请看全部的代码,代码即注释:

完整代码

import csv
import json

import requests
from datetime import datetime

headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/53\
    7.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
}
url = 'https://mooc.icve.com.cn/patch/zhzj/portalMooc_selectCourseList.action'
file = open('919页推荐课程.csv', mode='a', newline="",encoding='utf-8')
writer = csv.writer(file)
writer.writerow(['课程名称', '开课学校', '开始时间', '结束时间', '授课教师', '学习人数', '开课次数', '开课周次'])
for i in range(801, 920):
    data = {
        'newOrHot': 'hot',
        'curPage': i,
        'pageSize': '10',
        'state': '',
        'certificate': '',
        'majorType': 'all',
        'majorStatus': '全部',
        'searchValue': '',
        'projectCourseType': 'all',
        'publishCourseType': 'all',
        'cacheConditions': 'all'
    }
    response = requests.post(url, headers=headers, data=data)

    # 检查响应状态码是否为200,表示请求成功
    if response.status_code == 200:
        try:
            current_time = datetime.now()
            classesStartWeekly = ""
            dct = json.loads(response.text)['data']['items']  ##json字符串转换成python对象
            # print(dct)
            for data in dct:
                className = data[1]
                schoolName = data[2]
                startTime = data[3]
                endTime = data[4]
                teacher = data[5]
                learningNum = data[11]
                numberOfClass = data[9]

                time_diff = datetime.strptime(endTime + ' 23:59', '%Y-%m-%d %H:%M') - datetime.strptime(startTime + ' 0:00', '%Y-%m-%d %H:%M')
                if current_time > datetime.strptime(endTime, '%Y-%m-%d'):
                    current_weeks = (time_diff.seconds // 3600) // 7
                    classesStartWeekly = "课程已结束"
                elif(current_time < datetime.strptime(startTime, '%Y-%m-%d')):
                    classesStartWeekly = "课程未开始"
                else:
                    current_time_diff = current_time - datetime.strptime(startTime + ' 0:00', '%Y-%m-%d %H:%M')
                    current_weeks = current_time_diff.days // 7
                    if current_time_diff.days % 7 > 0:
                        current_weeks += 1
                    classesStartWeekly = current_weeks

                writer.writerow([className, schoolName, startTime, endTime, teacher,learningNum, numberOfClass, classesStartWeekly])
                print(className, schoolName, startTime, endTime, teacher, learningNum, numberOfClass, classesStartWeekly)
        except:
            pass
    else:
        print('请求失败')

2.2、课程详情

课程详情和推荐课程类似,即是抓包获取JSON数据。但是发现获取到的数据只包含部分。所以通过抓包,看到了前端的JS文件,通过对JS文件的分析,找到了所有的数据。

找到JSON数据

第一步我们需要先找到JSON的数据,这个地方和上面的推荐课程略有不同,这里需要间接获取。
可以看到我们"需要的所有数据"都在这里,这是第三个请求路径:(实际上数据不全,但是我们能获取到的只有这部分,后面再细说)
在这里插入图片描述
那么我们通过上面的推荐课程爬取,可以知道这部分的数据请求都是POST,需要传入data去请求,此时我们查看负载:
在这里插入图片描述
发现如果想要请求这个JSON资源,还需要传入两个Id:classId和courseId。
很明显这个Id加密过,没有办法得出规律,那么就需要利用其他渠道获取。
此时猜想,这个Id的数据肯定也是利用Ajax响应到前端的JSON资源。尝试寻找:
在这里插入图片描述
可以发现两个Id在这里,这是第二个请求路径。此时查看负载,发现还缺少classCode:
在这里插入图片描述
继续寻找,发现找不到了。这个地方的"类代码"应该是某一个唯一标识,一门课程对应一门。所以这个数据,应该是在一开始的"推荐课程"那里传递的:
在这里插入图片描述
此处,这是第一个请求路径

最终思路

那么此时思路就明确了:
  先通过第一个请求路径,得到classCode,
  再把这个classCode作为data传递请求第二个请求路径的资源,获取到classId和courseId,
  再把这两个Id作为data传递请求第三个请求路径的资源,获取到我们需要的课程详情的JSON数据。

URL
第一个:https://mooc.icve.com.cn/patch/zhzj/portalMooc_selectCourseList.action
第二个:https://mooc.icve.com.cn/patch/zhzj/portalMooc_getClassAndCourseIdByCode.action
第三个:https://mooc.icve.com.cn/patch/zhzj/portalMooc_selectCourseDetails.action

新的问题

此时获取到数据之后,发现了新的问题:有些数据是没有通过JSON格式传递的!
那么这些数据是怎么展现到前端页面的呢?毫无疑问是后端传递上来的JSON数据里面的某些数据,拿上来放在前端计算了。所以我们应该尝试找到前端计算的代码,我们可以去寻找JS文件:
在这里插入图片描述

可以看到,应该就是它了,但是我们不是要把它抓下来,而是要看它是如何计算的,然后照葫芦画瓢在python代码中计算。下面举个简单例子说明:(本期互动)
①定位前端元素
在这里插入图片描述

②JSON查找disCount

传的是0
③那就只能去JS里面查找
在这里插入图片描述
找到了这段逻辑,可以看到里面的num1和num2就是我们要的数据。剩下的数据以此类推,然后转为python代码,如:
在这里插入图片描述

完整代码

# -*- coding: utf-8 -*-
# @Author:︶ㄣ释然
# @Time: 2023/6/20 14:29
import csv
import json

import requests
from datetime import datetime
from termcolor import colored

file1 = open('919页课程详细列表.csv', mode='a', newline="", encoding='utf-8')
writer1 = csv.writer(file1)
writer1.writerow(['课程名称', '所属专业', '学时安排', '课程进度', '学时数', '累计选课人数', '本期选课人数', '学员所属单位数', '本期学员所属单位数', '累计互动次数', '本期互动次数', '累计日志总数','开课学校','学校类别','开始时间','结束时间'])

headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/53\
    7.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
}
# list_classCode = []
# list_classId_and_courseId = []

'''每一次循环爬一页热门推荐页,一页十条'''
def getClassCode(url_getClassCode):
    file2 = open('919页热门推荐页ClassCode.csv', mode='a', newline="", encoding='utf-8')
    writer2 = csv.writer(file2)
    for i in range(801, 920):
        data_getClassCode = {
            'newOrHot': 'hot',
            'curPage': i,
            'pageSize': '10',
            'state': '',
            'certificate': '',
            'majorType': 'all',
            'majorStatus': '全部',
            'searchValue': '',
            'projectCourseType': 'all',
            'publishCourseType': 'all',
            'cacheConditions': 'all'
        }
        try:
            response = requests.post(url_getClassCode, headers=headers, data=data_getClassCode)
            # 检查响应状态码是否为200,表示请求成功
            if response.status_code == 200:
                dct = json.loads(response.text)['data']['items']
                for data_getClassCode in dct:
                    # list_classCode.append(data_getClassCode[12])
                    writer2.writerow([data_getClassCode[12]])
                print("成功获取热门推荐页第%s页" % i)

            else:
                print('请求失败')
        except:
            pass


'''查每一条的classId和courseId,上面for一次,这里就可以for10次'''
def getClassId_and_CourseId(url_getClassId_and_CourseId):
    file3 = open('919页classId_and_courseId.csv', mode='a', newline="", encoding='utf-8')
    writer3 = csv.writer(file3)

    csvfile = open('919页热门推荐页ClassCode.csv', newline='')
    reader = csv.reader(csvfile)
    listReader = list(reader)
    for i in range(6001, 9167):
        data_getClassId_and_CourseId = {
            'classCode': listReader[i][0]
        }
        try:
            response = requests.post(url_getClassId_and_CourseId, headers=headers, data=data_getClassId_and_CourseId)
            if response.status_code == 200:
                dct = json.loads(response.text)['data']
                # list_classId_and_courseId.append(dct)
                writer3.writerow([dct])
                print("成功获取第%s门课程的classId和courseId" % i)
            else:
                print('请求失败')
        except:
            pass


'''根据上面查到的两个Id,开始获取详情页JSON数据'''
def details(url_details):
    csvfile = open('919页classId_and_courseId.csv', newline='')
    reader = csv.reader(csvfile)
    listReader = list(reader)

    for i in range(1, 9154):
        current_time = datetime.now()

        json_str = str(listReader[i - 1][0])
        json_str = json_str.replace("'", "\"")
        dictionary = json.loads(json_str)

        data_details = {
            'classId': dictionary['classId'],
            'courseId': dictionary['courseId']
        }
        response = requests.post(url_details, headers=headers, data=data_details)
        if response.status_code == 200:
            try:
                dct = json.loads(response.text)
                className = dct['data']['className']
                majorName = dct['data']['majorName']
                learningPlan = round(float(dct['data']['learningTime']) / int(dct['data']['weekNum']), 2)
                weekDate = 0
                learningTime = dct['data']['learningTime']
                allClassPeople = int(dct['courseMoocData'][0][3]) + int(dct['classSpocList'][0][0])
                classPeople = dct['data']['classPeople']
                allSchoolCount = dct['courseMoocData'][0][0]
                schoolCount = dct['data']['schoolCount']
                allDisCount = int(dct['courseMoocData'][0][1]) + int(dct['classSpocList'][0][1])
                disCount = 0
                allLogCount = int(dct['courseMoocData'][0][2]) + int(dct['classSpocList'][0][3])
                schoolName = dct['data']['schoolName']
                schoolCategory = dct['data']['category']
                startTime = dct['data']['startTime']
                endTime = dct['data']['endTime']

                time = (current_time - datetime.strptime(dct['data']['startTime'], '%Y-%m-%d %H:%M:%S.%f'))
                time = time.days // 7
                weekDate = time
                if current_time < datetime.strptime(dct['data']['endTime'], '%Y-%m-%d %H:%M:%S.%f'):
                    if time % 7 > 0:
                        weekDate += 1
                else:
                    weekDate = int(dct['data']['weekNum'])

                questionNum = dct['data']['questionNum']
                noteNum = dct['data']['noteNum']
                allQuestionNum = dct['courseMoocData'][0][4]
                allNoteNum = dct['courseMoocData'][0][5]
                disCount = dct['data']['disCount']
                # print(questionNum,noteNum,allNoteNum,allQuestionNum)
                if disCount == '' or disCount is None:
                    disCount = 0
                if questionNum == '' or questionNum is None:
                    questionNum = 0
                if noteNum == '' or noteNum is None:
                    noteNum = 0
                if allQuestionNum == '' or allQuestionNum is None:
                    allQuestionNum = 0
                if allNoteNum == '' or allNoteNum is None:
                    allNoteNum = 0

                # 累计互动
                if (dct['postData']['data']['totalCount'] == None or 0 == dct['postData']['data']['totalCount'] or "0" == dct['postData']['data']['totalCount']):
                    allDisCount = int(allDisCount) + int(allQuestionNum) + int(allNoteNum)

                else:
                    allDisCount = int(allDisCount) + int(dct['postData']['data']['totalCount']) + int(allQuestionNum) + int(
                        allNoteNum)

                # 本期互动
                if (dct['postData']['data']['nowCount'] == None or 0 == dct['postData']['data']['nowCount'] or "0" == dct['postData']['data'][
                    'nowCount']):
                    disCount = int(disCount) + int(questionNum) + int(noteNum)

                else:
                    disCount = int(disCount) + int(dct['postData']['data']['nowCount']) + int(questionNum) + int(noteNum)
                writer1.writerow([className, majorName, learningPlan, weekDate, learningTime, allClassPeople, classPeople, allSchoolCount, schoolCount, allDisCount,
                                  disCount, allLogCount,schoolName,schoolCategory,startTime,endTime])
                print(className, majorName, learningPlan, weekDate, learningTime, allClassPeople, classPeople, allSchoolCount, schoolCount, allDisCount,
                      disCount, allLogCount,schoolName,schoolCategory,startTime,endTime)
            except Exception as e:
                print(colored(str(e), 'red'))
        else:
            print('请求失败')


if __name__ == '__main__':
    url_getClassCode = 'https://mooc.icve.com.cn/patch/zhzj/portalMooc_selectCourseList.action'
    url_getClassId_and_CourseId = 'https://mooc.icve.com.cn/patch/zhzj/portalMooc_getClassAndCourseIdByCode.action'
    url_details = 'https://mooc.icve.com.cn/patch/zhzj/portalMooc_selectCourseDetails.action'
    getClassCode(url_getClassCode)
    getClassId_and_CourseId(url_getClassId_and_CourseId)
    details(url_details)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/759661.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

4.2.tensorRT基础(1)-第一个trt程序,实现模型编译的过程

目录 前言1. hello案例2. 补充知识总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程&#xff0c;之前有看过一遍&#xff0c;但是没有做笔记&#xff0c;很多东西也忘了。这次重新撸一遍&#xff0c;顺便记记笔记。 本次课程学习 tensorRT 基础-第一个 trt 程序&#x…

SlickGrid学习

options&#xff1a; 选项 设置 enableCellNavigation 启用单元格导航&#xff0c;可以点单元格 enableColumnReorder 启动拖拽列 example-colspan.html 跨列实例 AutoTooltips plugin 隐藏列文字时自动显现列标题全文 Checkbox row select column 增加选择列来选择行…

STM32入门学习之core_cm3问题

1.安装了keil之后&#xff0c;新建工程出现几百个关于core_cm3的问题&#xff0c;百思不得其解。后在网上查阅资料后&#xff0c;了解到可能是keil版本的问题&#xff0c;是因为我下载的keill版本太高了&#xff0c;内部不支持ARM5.06的编译器。出现很多关于core_cm3的问题是因…

使用java语言制作一个窗体(弹窗),用来收集用户输入的内容

前言 最近做的一个需求&#xff0c;有个逻辑环节是&#xff1a;需要从本地保存的xml文件中取出一个值&#xff0c;这个值是会变化的。然后项目经理就给我说&#xff0c;你能不能做一个小工具&#xff0c;让用户可以直接通过界面化操作将这个变化的值写入文件&#xff0c;不用再…

rv1126交叉编译

目录 一、解压sdk二、交叉编译出动态库sqlite3交叉编译opencv交叉编译一、解压sdk tar xzvf rv1126_rv1109_linux_sdk_v1.8.0_PureVersion.tar.gz 查看交叉编译工具链 pwd查看绝对路径/home

正斜杠“/” 和反斜杠 “\” 傻傻分不清?

Note, on your keyboard, the location of two different punctuation marks—/, or forward slash, and \, or backward slash (also known as backslash). As you read from left to right, the forward slash falls forward while the backslash falls backward; 引用自 《…

js逆向思路-区分各个瑞数版本vmp/3/4/5/6代

目录 一、如何区分是最新瑞数vmp反爬二、3/4/5/6代/vmp版本的瑞数网站特征举例三、瑞数反爬的解决思路四、推荐相关瑞数文章五、一些心得一、如何区分是最新瑞数vmp反爬 前言:本篇文章不会介绍详细的解决反爬的算法扣代码过程,只是一些经验闲谈,文章的末尾有相关的好的质量的…

油猴脚本-Bilibili剧场模式仿Youtube

对比某个不存在的视频网站&#xff08;YouTube&#xff09;&#xff0c;以及B站的播放模式&#xff0c;普通模式以及网页全屏之间都有一个“中间档”&#xff0c;油管的叫 剧场模式&#xff0c;B站的叫 宽屏模式。 剧场模式 宽屏模式 相比之下&#xff0c;还是更喜欢油管的剧…

10.带你入门matlab频率表、盒图(matlab程序)

1.简述 相关概念介绍 以信号为例&#xff0c;信号在时域下的图形可以显示信号如何随着时间变化&#xff0c;而信号在频域下的图形&#xff08;一般称为频谱&#xff09;可以显示信号分布在哪些频率及其比例。频域的表示法除了有各个频率下的大小外&#xff0c;也会有各个频率的…

《遗留系统现代化》读书笔记(基础篇)

你现在所写的每一行代码&#xff0c;都是未来的遗留系统 为什么要对遗留系统进行现代化&#xff1f; 什么是遗留系统&#xff1f; 判断遗留系统的几个维度&#xff1a;代码、架构、测试、DevOps 以及技术和工具。时间长短并不是衡量遗留系统的标准。代码质量差、架构混乱、没…

【C++】多线程编程系列总纲

本系列文章主要介绍C11并发编程&#xff0c;主要包括如下篇目&#xff1a; 1、多线程编程一&#xff08;初识并发和多线程&#xff09; https://blog.csdn.net/Jacky_Feng/article/details/131751373?csdn_share_tail{"type"%3A"blog"%2C"rType&qu…

idea快捷方式,(主要是自己用的,持续收集)

编辑器 代码光标扩大选中 快捷键名字 使用方式 此时光标在这这里,按一下发现选中面积扩大, 如果再按快捷键,选中面积还扩大

带你【玩转Linux命令】➾ find cut 每天2个day06

带你【玩转Linux命令】➾ find & cut 每天2个day06 &#x1f53b; 一、文件管理命令1.1 find-查找文件或目录1.2 cut-指定欲显示的文件内容&#xff0c;输出到标准输出设备 &#x1f53b; 总结—温故知新 &#x1f53b; 一、文件管理命令 1.1 find-查找文件或目录 &#x…

【Nacos】基于k8s容器化部署Nacos集群

近期&#xff0c;在机器上部署了三个节点的nacos集群服务用于几个小型微服务的注册配置中心&#xff0c;并使用了Nginx简单代理了一下&#xff0c;随即简单研究了下集群部署分布式部署稍微提高可用性。部署完后能够正常使用&#xff0c;但是发现一个问题&#xff0c;刷新Nacos集…

制作文件间链接

制作文件间链接 管理文件间链接 硬/软链接 创建指向同一个文件的多个名称。 创建硬链接 从初始名称到文件系统的数据&#xff0c;每个文件都以一个硬链接开始。当创建指向文件的新硬链接时,也会创建另一个指向同一数据的名称。新硬链接与原始文件名作用相同。一经创建&…

Maya适合哪个工作站?

Autodesk Maya 提供多种功能&#xff0c;可以适应电影、游戏和建筑等不同行业的需求。定制的 Autodesk Maya 工作站可以帮助您提高行业领先的 3D 计算机动画、建模、模拟和渲染软件的工作效率和用户体验。 根据您的特定需求定制的快速、强大的工作站可以帮助您充分利用 Maya 工…

高薪Offer收割机之Redis分布式锁

锁在应用开发中使用非常广泛,哪些场景需要使用锁呢? 我们先来看抢购优惠卷的场景,代码如下: public void rushToPurchase() throws InterruptedException {//获取优惠券数量Integer num = (Integer) redisTemplate.opsForValue().get(“num”);//判断是否抢完if (null == n…

[Ipsc2009]Let there be rainbows!

Description HY Star是一个处处充满和谐&#xff0c;人民安居乐业的星球&#xff0c;但是HY Star却没有被评上宇宙文明星球&#xff0c;很大程度上是因为 星球的形象问题。HY Star由N个国家组成,并且在一些国家之间修建了道路以方便交流。由于HY Star是一个和谐的 星球&#x…

【运维】第04课:入口网关服务注册发现-Openrety 动态 uptream

本课时,我将带你一起了解入口网关服务的注册发现,并使用 OpenResty 实现一套动态 Upstream。 课前学习提示 基于本课时我们将要学习的内容,我建议你课前先了解一下 Nginx 的基础,同时熟悉基础的 Lua 语言语法,另外再回顾一下 HTTP 的请求过程,对于 Nginx 的负载均衡基本…

按键控制流水灯方向——FPGA

文章目录 前言一、按键二、系统设计1、模块框图2、RTL视图 三、源码四、效果五、总结六、参考资料 前言 环境&#xff1a; 1、Quartus18.0 2、vscode 3、板子型号&#xff1a;EP4CE6F17C8 要求&#xff1a; 按键1按下&#xff0c;流水灯从右开始向左开始流动&#xff0c;按键2按…