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

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

前期准备

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

页面分析

登录前

Python爬虫实战:登录教务系统查成绩_第1张图片

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

Python爬虫实战:登录教务系统查成绩_第2张图片

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

Post表单

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

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

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

__VIEWSTATE的解决

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


Python爬虫实战:登录教务系统查成绩_第3张图片

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

验证码的解决

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

Python爬虫实战:登录教务系统查成绩_第4张图片

登录后

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

Python爬虫实战:登录教务系统查成绩_第5张图片

分析页面元素,可以知道成绩查询的链接: xscjcx.aspx?xh=账号&xm=名字&gnmkdm=N121605(不同学校的可能不同,需要你自己分析)
Python爬虫实战:登录教务系统查成绩_第6张图片

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

Python爬虫实战:登录教务系统查成绩_第7张图片

我想通过历年成绩来查询,这样可以查询所有的成绩,也不用选择学年学期
于是再再再度分析页面
Python爬虫实战:登录教务系统查成绩_第8张图片

可以看出这次是提交表单,于是我们打开chrome开发者工具,然后点击 历年成绩,看看它提交了什么东西
Python爬虫实战:登录教务系统查成绩_第9张图片

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

Python爬虫实战:登录教务系统查成绩_第10张图片

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

页面分析总结

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

  • 先登录:
  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) #该页面包含了成绩

实际效果

在桌面运行



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


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


Python爬虫实战:登录教务系统查成绩_第11张图片

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

美化结果显示

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

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

'转换成二维数组'

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库实现比较好看的表格输出,为了不暴露成绩,还是只截一半的图好了哈哈

Python爬虫实战:登录教务系统查成绩_第12张图片

你可能感兴趣的:(Python爬虫实战:登录教务系统查成绩)