Python爬虫实战:登录教务系统查成绩

本文记录我用Python登录教务系统查询成绩的过程。手动输入验证码,简单获取成绩页面。后续将可能更新自动识别验证码登录查询

前期准备

本爬虫用到了Python的Requests库BeautifulSoup库
参考文章:从零开始写Python爬虫 --- 爬虫实践:登录正方教务系统

页面分析

登录前


打开chrome的开发者工具,提交一次表单


我们可以分析出请求的Headers,以及Post的表单
请求的Headers整个复制就好,因此我们需要解决的问题就在Post的表单这里。

Post表单

可以看到上图中,需要注意的项有:__VIEWSTATE、txtUserName、TextBox2、txtSecretCode、RadioButtonList1
其中

  • txtUserName:用户名
  • TextBox2:密码
  • txtSecretCode:验证码
  • RadioButtonList1:三个单选项,我们选学生
  • __VIEWSTATE

关于RadioButtonList1,我们只要点view source 就可以看到

__VIEWSTATE的解决

而__VIEWSTATE通过页面分析,我们知道,其实就是图中input元素的value值


因此我们只需获取到登录页面,通过BeautifulSoup分析出这个值即可

验证码的解决

对于教务的验证码,我们可以用机器识别,也可以手动输入,在这里我们采用手动输入验证码的方式。
在chrome开发者工具中可以看到,CheckCode.aspx就是验证码,它在default2.aspx之后访问,而每次访问CheckCode.aspx,验证码都会改变,也就是说以最后一次访问的为准。因此我们可以先访问default2.aspx,分析页面获取到viewstate,再访问CheckCode.aspx来获取验证码
而验证码的链接就是http://jw.******.com/(m4kwrs3n51rwlebcum0adrq5)/CheckCode.aspx

登录后

再次查看chrome开发者工具,可知登录后跳转到xs_main.aspx?xh=账号页面


分析页面元素,可以知道成绩查询的链接:xscjcx.aspx?xh=账号&xm=名字&gnmkdm=N121605(不同学校的可能不同,需要你自己分析)

我们再进入成绩查询的页面(在这里我们要再次记录请求的Headers)


我想通过历年成绩来查询,这样可以查询所有的成绩,也不用选择学年学期
于是再再再度分析页面

可以看出这次是提交表单,于是我们打开chrome开发者工具,然后点击历年成绩,看看它提交了什么东西

又是__VIEWSTATE,这次学聪明了,一看就知道在页面元素里面,不过一定是在提交前的页面,所以我们要回到成绩查询的页面再分析,用chrome的元素搜索一下viewstate,这一长串的就是我们想要的东西


至此,分析结束,一切就绪

页面分析总结

我们要查询成绩,总共需要以下步骤:

  • 先登录:
  1. 获取登录界面的headers
  2. 获取__VIEWSTATE
  3. 获取验证码
  4. Post 表单登录
  • 然后获取查询成绩页面:
  1. 获取成绩查询Headers
  2. 获取成绩查询链接
  3. 获取__VIEWSTATE
  4. Post表单提交
  5. 获得成绩页面

代码的实现

import requests
import bs4
from bs4 import BeautifulSoup

#获取请求头部
#可以通过chrome浏览器查看
#此函数用于把复制下来的Headers分成字典
def getHeaders(raw_head):
    headers={}
    for raw in raw_head.split('\n'):
        headerKey,headerValue = raw.split(':',1)
        headers[headerKey] = headerValue
    return headers

#登录Header 
login_head='''Host:xsweb.scuteo.com
Connection:keep-alive
Content-Length:178
Cache-Control:max-age=0
Origin:http://xsweb.scuteo.com
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36
Content-Type:application/x-www-form-urlencoded
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer:http://xsweb.scuteo.com/(5mn1wwnfzv1qepq1z5pnpgak)/default2.aspx
Accept-Encoding:gzip, deflate
Accept-Language:zh-CN,zh;q=0.8,en;q=0.6'''

#成绩查询请求头部
mark_head='''Host:xsweb.scuteo.com
Connection:keep-alive
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer:http://xsweb.scuteo.com/(5mn1wwnfzv1qepq1z5pnpgak)/xs_main.aspx?xh=201630021407
Accept-Encoding:gzip, deflate
Accept-Language:zh-CN,zh;q=0.8,en;q=0.6'''

#构造session
s=requests.session()

#请求url
url='http://xsweb.scuteo.com/(5mn1wwnfzv1qepq1z5pnpgak)/'

#登录页面
#获取登录headers
headers=getHeaders(login_head)

#构造登录Post表单
account='账号'
password='密码'
formtable={
'txtUserName':'',
'TextBox2':'',
'txtSecretCode':'',
'__VIEWSTATE':'',
'RadioButtonList1':'%D1%A7%C9%FA',
'Button1':'',
'lbLanguage':'',
'hidPdrs':'',
'hidsc':''
}
formtable['txtUserName']=account
formtable['TextBox2']=password

#获取viewstate
#获取登录界面
login_page=s.get(url+'default2.aspx')
soup=BeautifulSoup(login_page.text,'lxml')
__VIEWSTATE = soup.find('input',attrs={'name':'__VIEWSTATE'}).get('value')
#将Viewstate存入登录Post表单
formtable['__VIEWSTATE']=__VIEWSTATE

#获取验证码
pic = requests.get(url+'CheckCode.aspx').content
with open('ver_pic.png','wb') as f:
    f.write(pic)

vercode=input('请输入验证码:')
formtable['txtSecretCode']=vercode

#Post表单,登录
res = s.post(url+'/default2.aspx',formtable,headers=headers)
print(res)

#主页面
#获取成绩查询链接
page=s.get(url+'/xs_main.aspx?xh='+account)
soup2=BeautifulSoup(page.text,'lxml')
markurl = soup2.find('a',attrs={'onclick':"GetMc('成绩查询');"}).get('href')

#成绩查询界面

#获取成绩查询Headers
headers=getHeaders(mark_head)

#Post表单
formtable={
    '__EVENTTARGET':'',
    '__EVENTARGUMENT':'',
    '__VIEWSTATE':'',
    'hidLanguage':'',
    'ddlXN':'',
    'ddlXQ':'',
    'ddl_kcxz':'',
    'btn_zcj':'%C0%FA%C4%EA%B3%C9%BC%A8'
}

#获取__VIEWSTATE
markpage = s.get(url+markurl,headers=headers)
soup=BeautifulSoup(markpage.text,'lxml')
__VIEWSTATE = soup.find('input',attrs={'name':'__VIEWSTATE'}).get('value')
formtable['__VIEWSTATE']=__VIEWSTATE

#提交表单,获取成绩查询界面
markpage=s.post(url+markurl,formtable,headers=headers)
print(markpage.text) #该页面包含了成绩

实际效果

在桌面运行



提示输入验证码,验证码会自动保存到桌面


输入后若顺利就能够看到成绩的页面了


我们还可以进一步通过BeautifulSoup筛选出成绩,美化我们的查询结果。

美化结果显示

这里我用BeautifulSoup进行节点的筛选,将成绩表格数据存入一个二维数组,再输出。
我单独写了一个table2List.py文件,和源文件同一个目录

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 文件名:table2List.py

'<table>转换成二维数组'

import bs4
from bs4 import BeautifulSoup

def tableToList(document):
    document=str(document)
    soup = BeautifulSoup(document,'lxml')

    #得到table的每行每项,存到items中
    lines=soup.table.find_all('tr')
    items=[]
    for line in lines:
        lineList=[]
        for item in line.find_all('td'):
            lineList.append(item.string)
        items.append(lineList)

    return items

最后只需要在原来的文件引入这段代码
from table2List import tableToList
然后在源代码加上这段代码

soup3=BeautifulSoup(markpage.text,'lxml')

marktable = str(soup3.find_all(id="Datagrid1")[0])
marklist = tableToList(marktable)

for line in marklist:
    for column in [3,7,8]: #这里的数字表示你想显示的列,不同教务不同
        print("%-20s" % line[column] ,end='')
    print("")

这样能得到相对整洁的输出,当然我们还可以用prettytable库实现比较好看的表格输出,为了不暴露成绩,还是只截一半的图好了哈哈

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,784评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,745评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,702评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,229评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,245评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,376评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,798评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,471评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,655评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,485评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,535评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,235评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,793评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,863评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,096评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,654评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,233评论 2 341

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,571评论 18 139
  • 简述 所在的组织 华工小灯神 需要为用户提供方便地获取成绩单的服务,于是需要爬取本科生的教务系统,识别验证码时使用...
    treelake阅读 9,454评论 7 66
  • 1.爬取baidu baidudemo.py#!/usr/bin/python#-- coding:gb18030...
    fatfatEddy阅读 3,567评论 0 7
  • 回忆像个说书的人用充满乡音的口吻跳过水坑 绕过小村等相遇的缘分你用泥巴捏一座城说将来要娶我进门转多少身 过几次门 ...
    舒小妮儿阅读 348评论 0 0
  • 刚刚才参加完一次与朋友的聚会! 说起朋友来,从小到大我们认为的、我们自认为的朋友可真不少!有十几年的发小,有大学同...
    赵扬帆阅读 336评论 0 0