爬成绩的想法由来已久,寒假在家就动手操作了一下。我们学校的教务处登录是不需要验证码的,所以爬这个也相对简单些。写好后又用面向对象方法重新调整了代码,更美观也更符合python简洁优美的风格。
本文最后附上源代码和所用库的官方文档,方便大家学习。
所用到的库有requests, beautifulsoup和csv三个。
首先我们看登录页面:只需要用户名和密码两个信息。
在网页端登录后查看页面信息:我们选中post方法那个页面,那就是提交登录信息后的页面,状态显示为302,意思是重定向,要跳转到另一个页面。
在参数一栏里看见所需的表单信息名字分别为username和password。
所以在使用requests的post方法时,注意要提交一个字典格式的data
#用户登录
login_url = 'http://newjw.lixin.edu.cn/sso/login'
header = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0;) Gecko/20100101 Firefox/64.0'}
payload = {'username':username,'password':password}
login1 = s.post(login_url,data=payload,headers = header)
细心的话可以注意到,我这里post前面不是requests,而是s
因为教务系统需要使用cookies,而cookies到后面的页面中还需要用到,所以普通的requests.post()用法在这里不适用。需要用到requests库的高级用法:session会话
在session会话中可以保存并一直携带相同的cookies,我这里s的定义如下:
s = requests.Session()
在登录时还需注意的一点是请求头hearders的设置。
不知道有没有小伙伴的电脑跟我一样,我用的是火狐Quantum64,在页面信息中可以看到我完整地user-agent是这样:
括号里面有个…,在设置进requests的headers参数时,这个三个小点点要删掉,不然会出现什么latin -1的报错,具体我忘了。
第一步的登录到这里就完成了,接下来我们来到成绩页面,获取成绩页面的相关信息。
我成绩不太好就不献丑了,老师们也打了码,避免泄露个人信息。
拿到score_url ,结合上面我说过的,访问成绩页面也需要携带cookies,所以还是一样,用session。这里不需要重新定义一个session,因为这样相当于重新开了一个会话,我们需要和前面的登录在同一个会话里,所以看下面:
#成绩页面
my_score_url = 'http://newjw.lixin.edu.cn/webapp/std/edu/grade/course.action'
#下载成绩页面源代码
my_score_url_text = s.get(my_score_url).text
直接使用s.get()就可以了。
到这里requests库的使用基本放下了,页面源代码已经下载下来,接下来该对源代码进行结构化处理。请出我们的beautifulsoup打法,漂亮的肥皂???为啥叫这名儿我也不知道,可能这样清洗数据比较干净?我猜的哈。
beautifulsoup容易上手,学起来比正则表达式简单。当然,爬虫进阶是少不了正则的了,毕竟它更加精确
my_score_soup = BeautifulSoup(my_score_url_text,'html.parser')
首先看看我们所需要的数据在哪里,这里我需要爬取的是第一个表格2018-2019年 1 学期成绩列表。页面元素审查,找到数据位置。
观察可知该学期我的十四门课都在这个id下的tbody标签下。
这个地方有个问题我到现在也没解决,我用beautifulsoup.find_all(‘tbody’,id = xxx)或select(’.xxx’)这种比较的精确查找方法是找不到所需数据的。
没关系,条条大路通罗马,我们find_all(‘tbody’),查找出了已完成的所有学期的列表。
观察可知,所需要爬取的数据是第一个列表,那么可以切片
my_score_detail = my_score_soup.find_all('tbody')[0]
my_score_detail = list(my_score_detail.find_all('td'))
这里第二行转化为列表必不可少,不然它就是beautifulsoup内置的tag格式
不确定的可以检查下,通过len(my_score_detail)看长度是否和该学期课程数相吻合。我的输出结果为14,和课程数一致。
里面一堆td和/td标签没用的我们把它清洗掉,这里用到beautifulsoup内置的tag.string的方法取得标签内的文本。用一个简单的小循环就可以实现啦,但更简单的连循环都不用,一个简单的列表解析式就可以了。
my_score_list = [i.string for i in my_score_detail]
再来看看结果
这样就洗掉了大聊的html的标签符号,里面绩点和成绩的部分还有\t的制表符,后面我们输出为csv格式用excel打开时不影响正常显示的。如果自己看不顺眼的话可以用strip()函数去掉这些特殊符号的,这里我就不演示了。
接下来就是最后一步,将列表数据写入csv文件。
稍微分析下可知,总计十四门课,一门课有9列数据,分别是:序号,课程代码,课程名称,类别,情况,教师,学分,总评,绩点
先将首行写入文件:
f = open('my_score.csv','w',newline='')
csv_write = csv.writer(f)
csv_write.writerow(['序号','课程代码','课程名称','类别','情况','教师','学分','总评','绩点'])
这里的newline = ''是为了避免出现空行的情况。
接下来对课程信息做整理:
for i in range(0,140,10):
course_list = []
global my_score_list
for a in range(i,i+9):
course_list.append(my_score_list[a])
csv_write.writerow(course_list)
来看看情况吧
是不是挺好的
最后
f.close()
就可以了。
运行起来,目录下出现了my_score.csv文件,打开完工!
最后将代码进行优化,用面向对象的方法写出来,完整代码如下:
# -*- coding:UTF-8 -*-
import requests
from bs4 import BeautifulSoup
import csv
class score_spider():
def __init__(self,target_url,header):
self.target_url = target_url
self.header = header
self.s = requests.Session()
def login_data(self): #输入登录所需信息
username = input('请输入学号:')
password = input('请输入登录密码:')
global payload
payload = {'username':username,'password':password}
return payload
def login(self): #登录
url_login = self.s.post(self.target_url,headers = header,data = payload)
print('登录状态为:',url_login.status_code)
def get_score_text(self,score_url): #获取成绩页面源代码
global score_text
score_text = self.s.get(score_url).text
#print(score_text)
return score_text
def score_text_cleanning(self,score_text): #数据清洗
score_soup = BeautifulSoup(score_text,'html.parser')
my_score_detail = score_soup.find_all('tbody')[1]
my_score_detail = list(my_score_detail.find_all('td'))
global my_score_list
my_score_list = [i.string for i in my_score_detail]
print(my_score_list)
return my_score_list
def output_csv(self,filename): #写入csv文件
f = open(filename,'w',newline='')
csv_write = csv.writer(f)
csv_write.writerow(['序号','课程代码','课程名称','类别','情况','教师','学分','总评','绩点'])
for i in range(0,140,10):
course_list = []
global my_score_list
for a in range(i,i+9):
course_list.append(my_score_list[a])
csv_write.writerow(course_list)
f.close()
if __name__ == "__main__":
target_url = 'http://newjw.lixin.edu.cn/sso/login'
header = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0;) Gecko/20100101 Firefox/64.0'}
jw_crawler = score_spider(target_url,header)
jw_crawler.login_data()
jw_crawler.login()
score_url = 'http://newjw.lixin.edu.cn/webapp/std/edu/grade/course.action'
score_text = jw_crawler.get_score_text(score_url)
jw_crawler.score_text_cleanning(score_text)
jw_crawler.output_csv('my_score_update.csv')
三个库的官方文档如下:
链接: https://pan.baidu.com/s/1Pv83koEtXzBuhD9u8uRfCA 提取码: kxvg
链接如失效请私信,祝大家学习顺利!