IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> python模拟登录中国海洋大学教务系统(青果)- 爬取学期所有专业课至excel - 并进行课表排课(一) -> 正文阅读

[Python知识库]python模拟登录中国海洋大学教务系统(青果)- 爬取学期所有专业课至excel - 并进行课表排课(一)

本章内容包含python模拟登录中国海洋大学教务系统(青果)- 百度智能云识别验证码 - 进入选课页面。

系列文章目录

第一章 python模拟登录中国海洋大学教务系统(青果)
第二章 爬取学期所有专业课至excel
第三章 课表排课



前言

提示:本人是个菜鸟,没系统学过python、html等,对以下所有知识点只会应用,不懂原理,大家可以去看看这两位大佬的技术文章。

python爬虫模拟登录学校教务系统(青果教务系统)并查询个人成绩——王森ouc
python-scrapy模拟登陆网站–登陆青果教务管理系统(一)——耿子666

本文是借鉴了以上两位大佬的文章,能够跑出结果,但我不是很懂原理,会在网上整理总结,仅供参考。


一、模拟登录

这个前两个大佬都做的很优秀了,我一开始只会f12,查看一下要提交的表单,然后发现提交表单的key(不知道专业术语是啥)会变,我就歇菜了。
就下面这个? , _uydvv,_pydvv,它们是由_u,_p和你输入的验证码拼接而成的

之后查资料的时候,发现竟然有大佬已经写了我想写的前半部分内容,整挺好,然后踩在大佬的肩膀上,修修改改也写了一天,才基本实现了所有内容。
现在我就带着大家再挨个走一遍所有的路程,出发!

1.分析提交表单

(海大的教务系统需要连校园网或VPN)
1.使用Google Chrome打开教务网站http://jwgl.ouc.edu.cn/cas/login.action

2.按F12 - 进入开发者工具
点击Network - 网络

chrome开发者工具最常用的四个功能模块:元素(ELements)、控制台(Console)、源代码(Sources),网络(Network)。

勾选Preserve log - 保留请求日志

谷歌开发者工具里面这个preserve log :保留请求日志,跳转页面的时候勾选上,可以看到跳转前的请求,也可适用于chrome开发者工具抓包的问。

3.随便输入一些账户密码验证码
可以看到点击验证码框或验证码的时候,会有名为genValidateCode?..的请求来获取新的验证码图片。

点击该请求(查看请求的详细内容)- 点击preview(预览)-可以看到验证码图片
再点击Headers
在这里插入图片描述
Request Method是Get

Request URL: http://jwgl.ouc.edu.cn/cas/genValidateCode?dateTime=Fri%20Jul%2023%202021%2010:35:41%20GMT+0800%20(%D6%D0%B9%FA%B1%EA%D7%BC%CA%B1%BC%E4)

复制该url进行打开,可以看到并不是原来的验证码图片,每次刷新后验证码都不一样

经过验证,该验证码与dateTime并没有关系,目前我还不知道原理,请大佬指教

通过该url可以获得验证码,暂且不管该验证码是否是我们需要的正确的验证码


4.点击提交,出现名为logon.action的请求,点击观察其Headers内容

在写请求的时候,一般形式应该是(url,表单数据,请求头):

response = request.post(url, data, headers)

这些在Headers内容里都能找到,然后就是用代码构造这些内容:

import requests
url = 'http://jwgl.ouc.edu.cn/cas/logon.action'
#表单信息不能直接复制,这里就演示一下,并不是实际实现的
data = {
	'_u'+randnumber: 'MTgwOTAwMTIwMjg7OzkyODY2QTM4NUI4NDBCMkQzQjFGNEI5MzgwMDI2Qzg3',
	'_p'+randnumber: 'e045dced42fea99c1434f62903a9e564',
	'randnumber': 'y7jz',
	'isPasswordPolicy': '1',
	'txt_mm_expression': '12',
	'txt_mm_length': '11',
	'txt_mm_userzh': '0',
}
#headers里的内容并没有完全复制,我也不清楚哪些是必须的,哪些是非必须的,建议全部粘贴,以下是拷贝大佬的,我就不做修改了
headers = {
    'Host': 'jwgl.ouc.edu.cn',
    'Origin': 'http://jwgl.ouc.edu.cn',
    'Referer': 'http://jwgl.ouc.edu.cn/cas/login.action',
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36',
    'X-Requested-With': 'XMLHttpRequest',
    'Upgrade-Insecure-Requests': '1',
}
#这里用session,而不是request
r = session.post(url, data=data, headers=headers)

request对象的生命周期是针对一个客户端(说确切点就是一个浏览器应用程序)的一次请求,当请求完毕之后,request里边的内容也将被释放掉 。
而session的生命周期也是针对一个客户端,但是却是在别人设置的会话周期内(一般是20-30分钟),session里边的内容将一直存在,即便关闭了这个客户端浏览器 session也不一定会马上释放掉的。
——参考request和session的区别

在这里因为在登录后,还要跳转到选课页面,需要保存登录信息,所以用session

另外好像可以#创建cookie,利用cookie实现持久化登录,感兴趣的可以瞅瞅

会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。
——参考Cookie和Session详解


表单信息不能直接复制,需要先观察一下
_uy7jz: MTgwOTAwMTIwMjg7OzkyODY2QTM4NUI4NDBCMkQzQjFGNEI5MzgwMDI2Qzg3
_py7jz: e045dced42fea99c1434f62903a9e564
randnumber: y7jz #验证码
isPasswordPolicy: 1#密码合法
txt_mm_expression: 12#密码规则
txt_mm_length: 11#输入的密码长度
txt_mm_userzh: 0

观察得到:
1.前两项的key是_u,_p+输入的验证码,value是加密过的,看不出内容
2.根据变量名猜测其含义,见注释
3.账户密码没有直接显示出来

所以要去源代码里找相关信息:
1.定位账号密码代码的位置
2.在控制台里按CTRL+shift+F打开全局搜索,搜索yhmm
在这里插入图片描述
3.点击第二个
在这里插入图片描述
4.观察代码:前几行都是获取输入,后几行就是对这些信息的加密,我们需要在代码里对信息进行同样的加密

	password = hex_md5(hex_md5(password)+hex_md5(randnumber.toLowerCase()));

	var p_username = "_u"+randnumber;
	var p_password = "_p"+randnumber;
	username = base64encode(username+";;"+_sessionid);
	var params = p_username+"="+username+"&"+p_password+"="+password+"&randnumber="+randnumber+"&isPasswordPolicy="+passwordPolicy+
				"&txt_mm_expression="+txt_mm_expression+"&txt_mm_length="+txt_mm_length+"&txt_mm_userzh="+txt_mm_userzh;

加密语句??对应

#具体加密原理见王森ouc大佬的文章
    password = md5(password.encode('utf-8')).hexdigest()
    randnumber = md5(randnumber.lower().encode('utf-8')).hexdigest()
    password = md5((password + randnumber).encode('utf-8')).hexdigest()
#randnumber会在之前通过百度智能云文字识别获得
    data['randnumber'] = randnumber
    p_username = '_u' + randnumber
    p_password = '_p' + randnumber
    data[p_username] = username
    data[p_password] = password
#sessionid是会话的id,一般是存放在cookie中
    _sessionid = session.cookies.get_dict()['JSESSIONID']
    username = base64.b64encode(username.encode('utf-8') + b';;' + str(_sessionid).encode('utf-8'))
#params不知道是干啥的,这里没用

???以上,账号、密码、验证码、url、data、headers都已分析过

2.百度智能云识别验证码

没有用过百度智能云的可以看看:百度智能云-文字识别SDK-识别验证码Demo

import requests
from aip import AipOcr
from bs4 import BeautifulSoup

headers = {
    'Host': 'jwgl.ouc.edu.cn',
    'Origin': 'http://jwgl.ouc.edu.cn',
    'Referer': 'http://jwgl.ouc.edu.cn/cas/login.action',
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36',
    'X-Requested-With': 'XMLHttpRequest',
    'Upgrade-Insecure-Requests': '1',
}
#百度智能云-文字识别
def randnumber_ocr(image):
    APP_ID = ''  # 在百度官网的应用列表中查看APP_ID
    API_KEY = ''  # 在百度官网的应用列表中查看API_KEY
    SECRET_KEY = ''  # 在百度官网的应用列表中查看SECRET_KEY
    client = AipOcr(APP_ID, API_KEY, SECRET_KEY)
    text = client.basicAccurate(image)
    if text['words_result_num'] == 1:
        return text['words_result'][0]['words'].strip()
    else:
        return ''

def logon():
    url = 'http://jwgl.ouc.edu.cn/cas/logon.action'
    session = requests.Session()
#百度智能云-文字识别-获取验证码    
    randnumber = ''
    while len(randnumber) != 4:
        r = session.get('http://jwgl.ouc.edu.cn/cas/genValidateCode', headers=headers)      
        randnumber = randnumber_ocr(r.content).replace(' ','')#去除空格
  
if __name__ == '__main__':
    logon()

3.代码

import requests
from aip import AipOcr
import base64
from hashlib import md5
import json
import re
from bs4 import BeautifulSoup

headers = {
    'Host': 'jwgl.ouc.edu.cn',
    'Origin': 'http://jwgl.ouc.edu.cn',
    'Referer': 'http://jwgl.ouc.edu.cn/cas/login.action',
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36',
    'X-Requested-With': 'XMLHttpRequest',
    'Upgrade-Insecure-Requests': '1',
}
#data和headers一样,并没有传全部的项
data = {
    'randnumber': 'gqzd'
}
#百度智能云-文字识别
def randnumber_ocr(image):
    APP_ID = ''  # 在百度官网的应用列表中查看APP_ID
    API_KEY = ''  # 在百度官网的应用列表中查看API_KEY
    SECRET_KEY = ''  # 在百度官网的应用列表中查看SECRET_KEY
    client = AipOcr(APP_ID, API_KEY, SECRET_KEY)
    text = client.basicAccurate(image)
    if text['words_result_num'] == 1:
        return text['words_result'][0]['words'].strip()
    else:
        return ''

def logon():
    url = 'http://jwgl.ouc.edu.cn/cas/logon.action'
    username = ''  # 输入你的用户名,也就是学号
    password = ''  # 输入你的密码
    session = requests.Session()
#百度智能云-文字识别-获取验证码    
    randnumber = ''
    while len(randnumber) != 4:
        r = session.get('http://jwgl.ouc.edu.cn/cas/genValidateCode', headers=headers)      
        randnumber = randnumber_ocr(r.content).replace(' ','')#去除空格
#构造data-信息加密        
    password = md5(password.encode('utf-8')).hexdigest()
    randnumber = md5(randnumber.lower().encode('utf-8')).hexdigest()
    password = md5((password + randnumber).encode('utf-8')).hexdigest()
#randnumber会在之前通过百度智能云文字识别获得
    data['randnumber'] = randnumber
    p_username = '_u' + randnumber
    p_password = '_p' + randnumber
    data[p_username] = username
    data[p_password] = password
#sessionid是会话的id,一般是存放在cookie中
    _sessionid = session.cookies.get_dict()['JSESSIONID']
    username = base64.b64encode(username.encode('utf-8') + b';;' + str(_sessionid).encode('utf-8'))
#post    
    r = session.post(url, data=data, headers=headers)
#response    
    info = json.loads(r.text)
    status = info['status']
    if status == '401':
        print('验证码错误')
        return
    elif status == '200':
        pass
    else:
        print(info['message'])
        return

    print('登录成功')
    
if __name__ == '__main__':
    logon()

二、进入选课页面

登录成功后,就要进入选课页面,此部分与一内容基本相同且更简单,可以自己尝试分析一下


1.选择选课学年学期-年级-专业——点击检索
2.
出现新请求,一般我们需要的都是document类型的,点击查看其详细信息
在这里插入图片描述
3.
Request URL: http://jwgl.ouc.edu.cn/taglib/DataTable.jsp?tableId=6146
Request Method: POST

Referer: http://jwgl.ouc.edu.cn/student/wsxk.kcbcx.html?menucode=JW130414

FormData
initQry: 0
xktype: 2 #选课类型
xh: 1*********8 #你的学号
xn: 2021 #学年
xq: 1 #学期-夏0-秋1-春2
nj: 20** #你的年级
zydm: 0**0 #你的专业代码
items:
xnxq: 2021-1 #学年-学期
kcfw: Specialty #课程范围
sel_nj: 2019 #选择的年级2018-2020
sel_zydm: 0011 #选择的专业代码
sel_schoolarea:
sel_cddwdm:
sel_kc:
kcmc:

经过观察,表单信息都很简单,构造data,headers需要有Referer

Referer 是 HTTP 请求header 的一部分,当浏览器(或者模拟浏览器行为)向web 服务器发送请求的时候,头信息里有包含 Referer 。它就是表示该请求的来源。
——参考什么是HTTP Referer?

def get_score(session, sel_nj):
    url = 'http://jwgl.ouc.edu.cn/taglib/DataTable.jsp?tableId=6146'
#这个data包含了所有项,但有些其实的非必须的
    formdata={
        'initQry': '0',
        'xktype': '2',#选课类型
        'xh': '1**********',#修改为你的学号
        'xn': '2021',#学年
        'xq': '1',#学期-夏0-秋1-春2
        'nj': '20**',#修改为你的年级
        'zydm': '0**0',#修改为你的专业代码
        'items': '',
        'xnxq': '2021-1',#学年-学期
        'kcfw': 'Specialty',#课程范围
        'sel_nj': sel_nj,#修改为你想选择的年级2018-2020
        'sel_zydm': '0011',#修改为你想选择的专业代码
        'sel_schoolarea': '',
        'sel_cddwdm': '',
        'sel_kc': '',
        'kcmc': ''
    }
#这个headers必须包含Referer
    headers['Referer'] = 'http://jwgl.ouc.edu.cn/student/wsxk.kcbcx.html?menucode=JW130414'
#使用session
    r = session.post(url,data=formdata, headers=headers)

#解析一下网页,打印一下表格内容看看
    soup = BeautifulSoup(r.text, 'html.parser')
    table_node = soup.find_all('td')
    for table in table_node:
		print (table.text)
		
    session.close()

三、完整代码及代码效果

import requests
from aip import AipOcr
import base64
from hashlib import md5
import json
import re
from bs4 import BeautifulSoup

headers = {
    'Host': 'jwgl.ouc.edu.cn',
    'Origin': 'http://jwgl.ouc.edu.cn',
    'Referer': 'http://jwgl.ouc.edu.cn/cas/login.action',
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36',
    'X-Requested-With': 'XMLHttpRequest',
    'Upgrade-Insecure-Requests': '1',
}
#data和headers一样,并没有传全部的项
data = {
    'randnumber': 'gqzd'
}
#百度智能云-文字识别
def randnumber_ocr(image):
    APP_ID = ''  # 在百度官网的应用列表中查看APP_ID
    API_KEY = ''  # 在百度官网的应用列表中查看API_KEY
    SECRET_KEY = ''  # 在百度官网的应用列表中查看SECRET_KEY
    client = AipOcr(APP_ID, API_KEY, SECRET_KEY)
    text = client.basicAccurate(image)
    if text['words_result_num'] == 1:
        return text['words_result'][0]['words'].strip()
    else:
        return ''
        
def get_score(session):
    url = 'http://jwgl.ouc.edu.cn/taglib/DataTable.jsp?tableId=6146'
#这个data包含了所有项,但有些其实的非必须的
    formdata={
        'initQry': '0',
        'xktype': '2',#选课类型
        'xh': '1**********',#修改为你的学号
        'xn': '2021',#学年
        'xq': '1',#学期-夏0-秋1-春2
        'nj': '20**',#修改为你的年级
        'zydm': '0**0',#修改为你的专业代码
        'items': '',
        'xnxq': '2021-1',#学年-学期
        'kcfw': 'Specialty',#课程范围
        'sel_nj': '2019',#修改为你想选择的年级2018-2020
        'sel_zydm': '0011',#修改为你想选择的专业代码
        'sel_schoolarea': '',
        'sel_cddwdm': '',
        'sel_kc': '',
        'kcmc': ''
    }
#这个headers必须包含Referer
    headers['Referer'] = 'http://jwgl.ouc.edu.cn/student/wsxk.kcbcx.html?menucode=JW130414'
#使用session
    r = session.post(url,data=formdata, headers=headers)

#解析一下网页,打印一下表格内容看看
    soup = BeautifulSoup(r.text, 'html.parser')
    table_node = soup.find_all('td')
    for table in table_node:
    	#if table.style != 'none':#有隐藏的列表格,没必要输出
		print (table.text)
		
    session.close()
    
def logon():
    url = 'http://jwgl.ouc.edu.cn/cas/logon.action'
    username = ''  # 输入你的用户名,也就是学号
    password = ''  # 输入你的密码
    session = requests.Session()
#百度智能云-文字识别-获取验证码    
    randnumber = ''
    while len(randnumber) != 4:
        r = session.get('http://jwgl.ouc.edu.cn/cas/genValidateCode', headers=headers)      
        randnumber = randnumber_ocr(r.content).replace(' ','')#去除空格
#randnumber会在之前通过百度智能云文字识别获得
    data['randnumber'] = randnumber
    p_username = '_u' + randnumber
    p_password = '_p' + randnumber
#构造data-信息加密        
    password = md5(password.encode('utf-8')).hexdigest()
    randnumber = md5(randnumber.lower().encode('utf-8')).hexdigest()
    password = md5((password + randnumber).encode('utf-8')).hexdigest()
#sessionid是会话的id,一般是存放在cookie中
    _sessionid = session.cookies.get_dict()['JSESSIONID']
    username = base64.b64encode(username.encode('utf-8') + b';;' + str(_sessionid).encode('utf-8'))
#构造data    
    data[p_username] = username
    data[p_password] = password
#post    
    r = session.post(url, data=data, headers=headers)
#response    
    info = json.loads(r.text)
    status = info['status']
    if status == '401':
        print('验证码错误')
        return
    elif status == '200':
        pass
    else:
        print(info['message'])
        return

    print('登录成功')
    
    get_score(session)
    
if __name__ == '__main__':
    logon()

运行效果:
在这里插入图片描述


四、遇到的问题

KeyError: 'words_result_num'

没有填写APP_ID、API_KEY 、SECRET_KEY

    print(tds[1].string + ": " + tds[16].string)
TypeError: can only concatenate str (not "NoneType") to str

爬取错误,tds里没有内容

    if table.attrs['style'] != "display:none;":
KeyError: 'style'
TypeError: 'NoneType' object is not subscriptable
    table = soup.find('td')
    print (table)#<td id="head_curent_skbjdm" width="6%">选课号</td>
    print (table['id'])#head_curent_skbjdm
    print (table['style'])#KeyError: 'style'
    print (table.id)#None
    print (table.style)#None
    #print (table.style['display'])#TypeError: 'NoneType' object is not subscriptable
    #print (table.attrs['style'])#KeyError: 'style'

<td id=“tr2_cddw” name=“cddw” style=“display:none;”>计算机科学与技术系</td>
这样一个table,其style=“display:none;”,说明其是隐藏的,没有必要爬取,但是有的table有style,有的没有
所以要怎么添加判断呢 TT 感谢指教

总结

知识点十分多啊,整理了两天,收获也很多 \OoO/

码字不易,感谢点赞评论收藏关注~

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2021-07-24 11:23:48  更:2021-07-24 11:25:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/7 8:55:35-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码