正方教务系统成绩爬虫的实现

2023-11-19

简介

这是利用python爬虫对正方教务系统成绩进行爬取,将爬取到的成绩放入excel中的程序。

一、设计思路以及工具

正方教务系统主要使用了ASP.NET技术,是一个比较好的爬虫练手程序。

PythonIDE:Anaconda
使用库:requests_html,bs4,requests,xlwt,os

二、实现步骤

1.登陆流程

1.1抓取登陆链接

首先爬虫的登陆不同于常规的网页登陆,需要找到登陆请求的post连接,所以需要使用检查-NetWork抓包,建议开启Preserve log,这样会显示所有登陆时产生的网络交互信息。
1.1设置检查
首先在登陆界面输入一次错误信息,可以捕获到请求发送的目标地址
1.2抓取登陆请求
可以看到在登陆请求中有四个重要字段包含在data中
txtUserName:登陆用户名
TextBox2:密码
txtSecretCode:验证码
__VIEWSTATE:

ViewState是
http://ASP.NET中用来保存WEB控件回传时状态值一种机制。在WEB窗体(FORM)的设置为runat=”server”,这个窗体(FORM)会被附加一个隐藏的属性_VIEWSTATE。_VIEWSTATE中存放了所有控件在ViewState中的状态值。

ViewState是类Control中的一个域,其他所有控件通过继承Control来获得了ViewState功能。它的类型是system.Web.UI.StateBag,一个名称/值的对象集合。

当请求某个页面时, http://ASP.NET把所有控件的状态序列化成一个字符串,然后做为窗体的隐藏属性送到客户端。当客户端把页面回传时,
http://ASP.NET分析回传的窗体属性,并赋给控件对应的值。

这个东西是不可或缺的,而我们又怎么找到它呢?
实际上,在页面的结构中我们就能找到它,它存在与input的value中,这个input是个隐藏状态的输入框。
1.3ViewState获取
通过以下代码可以将它提取出来

    #   使用BeautifulSoup进行页面处理,提取出__VIEWSTATE
    soup = BeautifulSoup(res.text,'lxml')     
    viewState = soup.find('input', attrs={'name': '__VIEWSTATE'})['value'] 

由此我们可知我们将要将用户名,密码,验证码,VIEWSTATE以数据的形式发送到"http://XXXXXXX/default2.aspx"

1.2 验证码获取

通过观察页面的Element,可知验证码图片是从CheckCode.aspx文件下获取到的
1.4验证码获取来源分析
由此我们可以知道需要通过get方法去得到验证码,在观察了请求头后,发现只有cookie比较特殊,我们可以知道,验证码匹配就是用的Cookie中的ASP.NET_SessionId作为依据。
1.5 验证码获取
所以我们需要从登陆请求页面获取到Cookie

    #   cookie进行处理
    Cookie=str(res.cookies)[27:69]

并且将它放到请求头里

    headeri = {
    "Accept": "image/avif,image/webp,image/apng,image/*,*/*;q=0.8",
    "Accept-Encoding": "gzip, deflate",
    "Accept-Language": "zh-CN,zh;q=0.9",
    "Cookie": Cookie,
    "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36",
    "Referer": "http://替换成你的jwxt网址/default2.aspx",
    "Host": "替换成你的jwxt网址",
    "Cache-Control": "max-age=0"
    }

发送请求保存图片,并要求用户进行验证(此处可以尝试用图像分析解决)

    #发送请求
    resi = session.get("http://XXXXX/CheckCode.aspx",headers=headeri,stream=True)

    #如果验证码文件已经存在则删除它,如果不存在就直接创建一个将验证码放入其中
    if os.path.exists(r'F://FzscoreGet//yanzheng.jpg'):
        os.remove(r'F://FzscoreGet//yanzheng.jpg')
    with open(r'F://FzscoreGet//yanzheng.jpg','wb')as f:
        f.write(resi.content)
        
    #打开验证码文件要求用户进行验证(待完善,目标自动识别)
    os.startfile(r'F://FzscoreGet//yanzheng.jpg')
    checkCode = input("请输入弹出的验证码:")

1.3 发送登陆请求

要求用户输入账号密码
初始化登陆数据

    login_info = {
            "__VIEWSTATE": viewState,
            "txtUserName": user, 
            "TextBox2": pwd,
            "txtSecretCode": checkCode,
            "RadioButtonList1": "%D1%A7%C9%FA",#学生选项
            "Button1": "",
            "lbLanguage": ""
        }

初始化请求头,此处cookie一定要和获取验证码的相同

    #处理登陆请求的请求头
    header = {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Accept-Encoding": "gzip, deflate",
    "Cookie": Cookie,
    "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
    "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36",
    "Referer": "http://替换成你的jwxt网址/default2.aspx",
    "Host": "替换成你的jwxt网址",
    "Origin":"替换成你的jwxt网址",
    "Cache-Control": "max-age=0"
}

发送登陆请求

requests.session().post(url='http://xxxx/default2.aspx', data=login_info, headers=header)

2.读入数据

2.1 获取历年成绩对应的__VIEWSTATE

如果没有出现报错说明你已经成功登陆,在登陆后我们需要找到历年成绩的入口

2.1历年成绩页面
访问历年成绩的页面的地址是这个

> 'http://jwxt.yxnu.edu.cn/xscjcx.aspx?xh={}&xm={}&gnmkdm=N121605'

其中的xh对应学号,xm对应姓名(需要从页面提取,或者从用户输入)
但是如果直接访问是会出现以下界面的
在这里插入图片描述
这说明我们无法直接使用原来的请求头获取页面
在观察这个请求的结构时,发现__VIEWSTATE已经不是原来那个了
在这里插入图片描述
于是为了获取这个最新的页面我们需要先GET方法获取页面源代码,从中取得__VIEWSTATE的值,然后再次POST过去。

	    #使用get请求先get到__VIEWSTATE
	    rln = session.get('http://xxxxxxx/xscjcx.aspx?xh={}&xm={}&gnmkdm=N121605'.format(xh,name),headers=header)
	    #错误处理,如果报错则说明验证码或账户密码错误,不细化分析错误,学有余力可以自己搞下  
        soup=BeautifulSoup(rln.text,'lxml')
        value3=soup.find('input', attrs={'name': '__VIEWSTATE'})['value']

把这个最新的__VIEWSTATE放入data中

    data={
        'btn_zcj':'%C0%FA%C4%EA%B3%C9%BC%A8',#学年成绩:btn_xn 历年成绩:btn_zcj
        'ddlXN':'',
        'ddlXQ':'',
        '__EVENTVALIDATION': '',
        '__EVENTTARGET':'',   
        '__EVENTARGUMENT' :'',
        '__VIEWSTATE':'',
        'hidLanguage':'',
        'ddl_kcxz':'',
    }

	data['__VIEWSTATE']=value3

并将其放入链接post出去

        #post请求获取到显示界面
        lncj = session.post('http://xxxx/xscjcx.aspx?xh={}&xm={}&gnmkdm=N121605'.format(xh,name),data=data,headers=header)
    
        soup=BeautifulSoup(lncj.text,'lxml')

此时soup中就已经是历年成绩了

3.数据处理

通过对soup的观察我们可以知道成绩存放在form表格中,通过form的分析我们可以知道一个<tr>标签下有若干个<td>,由此我们可以使用循环获取。

3.1 存放数据

    #初始化工作环境使用Xlwt
    workbook = xlwt.Workbook(encoding = 'utf-8')
    #表名
    worksheet = workbook.add_sheet('Score')
    #行数,列数初始化
    row = 0
    column = 0
    for tr in soup.find_all('tr'):
        row = row+ 1
        column = 0
        for td in tr.find_all('td'):
             worksheet.write(row,column, label = str(td.get_text()))
             column=column+1
    
     #保存excel    
    workbook.save('Score.xls')

总结

以上,就是一次爬取正方教务系统历年成绩的实现过程,总体来说是不难的,只是在验证码获取方面我卡了很久。爬取过程还可以继续完善,这只是实现的思路而不是完整的程序。实现过程中其实发现有一个地方可以挖坑,就是验证码,验证码功能可以尝试直接识别而不用用户输入。

最后感谢学校和正方教务系统提供的练手机会。

参考文章:http://ddrv.cn/a/288264ython 爬虫案例——正方教务学生成绩获取
参考文档:BeautifulSoup文档

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

正方教务系统成绩爬虫的实现 的相关文章

  • 如何替换 Pandas Dataframe 中不在列表中的所有值? [复制]

    这个问题在这里已经有答案了 我有一个值列表 如何替换 Dataframe 列中不在给定值列表中的所有值 例如 gt gt gt df pd DataFrame D ND D garbage columns S gt gt gt df S 0
  • 在 Python 中解析 TCL 列表

    我需要在双括号上拆分以空格分隔的 TCL 列表 例如 OUTPUT 172 25 50 10 01 01 Ethernet 172 25 50 10 01 02 Ethernet Traffic Item 1 172 25 50 10 01
  • Python 类型提示 Dict 语法错误 可变默认值是不允许的。使用“默认工厂”

    我不知道为什么解释器会抱怨这个类型的字典 对于这两个实例 我得到一个 不允许可变默认值 使用默认工厂 语法错误 我使用的是 python 3 7 3 from dataclasses import dataclass from typing
  • Python - 比较同一字典中的值

    我有一本字典 d Trump MAGA FollowTheMoney Clinton dems Clinton Stein FollowTheMoney Atlanta 我想删除字符串列表中的重复字符串 该字符串是键的值 对于这个例子 期望
  • NLTK 2.0分类器批量分类器方法

    当我运行此代码时 它会抛出一个错误 我认为这是由于 NLTK 3 0 中不存在batch classify 方法 我很好奇如何解决旧版本中的某些内容在新版本中消失的此类问题 def accuracy classifier gold resu
  • VSCode Settings.json 丢失

    我正在遵循教程 并尝试将 vscode 指向我为 Scrapy 设置的虚拟工作区 但是当我在 VSCode 中打开设置时 工作区设置 选项卡不在 用户设置 选项卡旁边 我还尝试通过以下方式手动转到文件 APPDATA Code User s
  • 从Django中具有外键关系的两个表中检索数据? [复制]

    这个问题在这里已经有答案了 This is my models py file from django db import models class Author models Model first name models CharFie
  • Python 3:将字符串转换为变量[重复]

    这个问题在这里已经有答案了 我正在从 txt 文件读取文本 并且需要使用我读取的数据之一作为类实例的变量 class Sports def init self players 0 location name self players pla
  • Java 和 Python 可以在同一个应用程序中共存吗?

    我需要一个 Java 实例直接从 Python 实例数据存储中获取数据 我不知道这是否可能 数据存储是否透明 唯一 或者每个实例 如果它们确实可以共存 都有其单独的数据存储 总结一下 Java 应用程序如何从 Python 应用程序的数据存
  • 使用 python/numpy 重塑数组

    我想重塑以下数组 gt gt gt test array 11 12 13 14 21 22 23 24 31 32 33 34 41 42 43 44 为了得到 gt gt gt test2 array 11 12 21 22 13 14
  • 无法导入 langchain.agents.load_tools

    我正在尝试使用 LangChain Agents 但无法导入 load tools 版本 langchain 0 0 27 我尝试过这些 from langchain agents import initialize agent from
  • 嵌套作用域和 Lambda

    def funct x 4 action lambda n x n return action x funct print x 2 prints 16 我不太明白为什么2会自动分配给n n是返回的匿名函数的参数funct 完全等价的定义fu
  • python的shutil.move()在linux上是原子的吗?

    我想知道python的shutil move在linux上是否是原子的 如果源文件和目标文件位于两个不同的分区上 行为是否不同 或者与它们存在于同一分区上时的行为相同吗 我更关心的是如果源文件和目标文件位于同一分区上 shutil move
  • 通过Python连接到Bigquery:ProjectId和DatasetId必须非空

    我编写了以下脚本来通过 SDK 将 Big Query 连接到 Python 如下所示 from google cloud import bigquery client bigquery Client project My First Pr
  • 找到一个数字所属的一组范围

    我有一个 200k 行的数字范围列表 例如开始位置 停止位置 该列表包括除了非重叠的重叠之外的所有类型的重叠 列表看起来像这样 3 5 10 30 15 25 5 15 25 35 我需要找到给定数字所属的范围 并对 100k 个数字重复该
  • Python:Goslate 翻译请求返回“503:服务不可用”[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我们不允许提出寻求书籍 工具 软件库等推荐的问题 您可以编辑问题 以便用事实和引文来回答 这个问题似乎不是关于主要由程序员使用的特定编程问
  • 重新分配唯一值 - pandas DataFrame

    我在尝试着assign unique值在pandas df给特定的个人 For the df below Area and Place 会一起弥补unique不同的价值观jobs 这些值将分配给个人 总体目标是使用尽可能少的个人 诀窍在于这
  • 在virtualenv中下载sqlite3

    我正在尝试使用命令创建应用程序python3 manage py startapp webapp但我收到一条错误消息 django core exceptions ImproperlyConfigured 加载时出错 pysqlite2 或
  • JSON:TypeError:Decimal('34.3')不是JSON可序列化的[重复]

    这个问题在这里已经有答案了 我正在运行一个 SQL 查询 它返回一个小数列表 当我尝试将其转换为 JSON 时 出现类型错误 查询 res db execute SELECT CAST SUM r SalesVolume 1000 0 AS
  • NLTK:查找单词大小为 2k 的上下文

    我有一个语料库 我有一个词 对于语料库中该单词的每次出现 我想获取一个包含该单词之前的 k 个单词和该单词之后的 k 个单词的列表 我在算法上做得很好 见下文 但我想知道 NLTK 是否提供了一些我错过的功能来满足我的需求 def size

随机推荐