python爬虫模拟登陆教务处并且保存数据到本地

刚开始接触#wiki/1514.html” target=”_blank”>python,看很多人玩爬虫我也想玩,找来找去发现很多人用网络爬虫干的第一件事就是模拟登陆,增加点难度就是模拟登陆后在获取数据,但是网上好少有python 3.x的模拟登陆demo可以参考,加上自己也不怎么懂html,所以这第一个python爬虫写的异常艰难,不过最终结果还是尽如人意的,下面把这次学习的过程整理一下。

工具

系统:win7 64位系统

浏览器:chrome

python版本:python 3.5 64-bit

ide:jetbrains pycharm (貌似很多人都用这个)

我把目标瞄准了我们的教务处,这次爬虫的目的是从教务处获取成绩并且把成绩输入excel表格中保存起来,我们学校教务处的地址是:http://jwc.ecjtu.jx.cn/ ,往常每次我们获取成绩都需要先进入教务处,然后点击成绩查询,输入公共的账号密码进入,最后输入相关信息获取成绩表格,这里登陆不需要验证码省了我一番功夫,这样我们先进入成绩查询系统登陆界面,先看看怎么模拟登陆这个过程,在chrome浏览器下按f12打开开发者面板:

查看表单数据

这里看到我们需要传递三个参数,分别是:user、pass、submit,可以很容易的理解这几个单词的字面意思,这样有了思路,我们就可以写出这次代码的第一步:模拟登陆教务处直接上代码:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import requests
url = ‘http://jwc.ecjtu.jx.cn/mis_o/login.php’
datas = {‘user’: ‘jwc’,
‘pass’: ‘jwc’,
‘submit’: ‘%cc%e1%bd%bb’
}
headers = {‘referer’: ‘http://jwc.ecjtu.jx.cn/mis_o/login.htm’,
‘user-agent’: ‘mozilla/5.0 (windows nt 6.1; wow64) applewebkit/537.36 ‘
‘(khtml, like gecko) chrome/52.0.2743.82 safari/537.36’,
‘accept’: ‘text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8’,
‘accept-language’: ‘zh-cn,zh;q=0.8’,
}
sessions = requests.session()
response = sessions.post(url, headers=headers, data=datas)
print(response.status_code)

代码输出:

200

说明我们模拟登陆成功了,这里用到了requests模块,还不会使用的可以查看中文文档 ,它给自己的定义是:http for humans,因为简单易用易上手,我们只需要传入url地址,构造请求头,传入post方法需要的数据,就可以模拟浏览器登陆了,这里因为有进一步获取成绩的操作所以使用了session来保持连接,这里单看最后的返回码的话我们是成功了的,具体如何还要看下一步操作,接下来:

查看post数据

因为这里就分析输入学号的情况所以其他都为空,这样我们就可以写出查询成绩的代码:

score_healders = {‘connection’: ‘keep-alive’,
‘user – agent’: ‘mozilla/5.0 (windows nt 6.1; wow64) ‘
‘applewebkit/537.36 (khtml, like gecko) chrome/52.0.2743.82 safari/537.36’,
‘content – type’: ‘application / x – www – form – urlencoded’,
‘accept’: ‘text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8’,
‘content – length’: ’69’,
‘host’: ‘jwc.ecjtu.jx.cn’,
‘referer’: ‘http: // jwc.ecjtu.jx.cn / mis_o / main.php’,
‘upgrade – insecure – requests’: ‘1’,
‘accept – language’: ‘zh – cn, zh;q = 0.8’
}
score_url = ‘http://jwc.ecjtu.jx.cn/mis_o/query.php?start=’ + str(
pagenum) + ‘&job=see&=&name=&course=&class + num
score_data = {‘name’: ”,
‘stuid’: num,
‘course’: ”,
‘term’: ”,
‘classid’: ”,
‘submit’: ‘%b2%e9%d1%af’
}
score_response = sessions.post(score_url, data=score_data, headers=score_healders)
content = score_response.content

这里解释一下上面的代码,上面的score_url 并不是浏览器上显示的地址,我们要获取真正的地址,在chrome下右键–查看网页源代码,找到这么一行:

a href=query.php?start=1&job=see&=&name=&course=&classhttp://jwc.ecjtu.jx.cn/mis_o/query.php?start=’ + str(pagenum) + ‘&job=see&=&name=&course=&class + num

同样使用post方法传递数据并获取响应的内容:

score_response = sessions.post(score_url, data=score_data,headers=score_healders)
content = score_response.content

这里采用beautiful soup 4.2.0来解析返回的响应内容,因为我们要获取的是成绩,这里到教务处成绩查询界面,查看获取到的成绩在网页中是以表格的形式存在:

观察表格的网页源代码:

学期
学号
姓名
课程
课程要求
学分
成绩
重考一
重考二

这里拿出第一行举例,虽然我不太懂html但是从这里可以看出来 代表的是一行,而应该是代表这一行中的每一列,这样就好办了,取出每一行然后分解出每一列,打印输出就可以得到我们要的结果:

from bs4 import beautifulsoup
soup = beautifulsoup(content, ‘html.parser’)
# 找到每一行
target = soup.findall(‘tr’)

这里分解每一列的时候要小心,因为这里表格分成了三页显示,每页最多显示30条数据,这里因为只是收集已经毕业的学生的成绩数据所以不对其他数据量不足的学生成绩的情况做统计,默认收集的都是大四毕业的学生成绩数据。这里采用两个变量i和j分别代表行和列:

# 注:这里的print单纯是我为了验证结果打印在pycharm的控制台上而已
i=0, j=0
for tag in target[1:]:
tds = tag.findall(‘td’)
# 每一次都是从列头开始获取
j = 0
# 学期
semester = str(tds[0].string)
if semester == ‘none’:
break
else:
print(semester.ljust(6) + ‘\t\t\t’, end=”)
# 学号
studentid = tds[1].string
print(studentid.ljust(14) + ‘\t\t\t’, end=”)
j += 1
# 姓名
name = tds[2].string
print(name.ljust(3) + ‘\t\t\t’, end=”)
j += 1
# 课程
course = tds[3].string
print(course.ljust(20, ‘ ‘) + ‘\t\t\t’, end=”)
j += 1
# 课程要求
requirments = tds[4].string
print(requirments.ljust(10, ‘ ‘) + ‘\t\t’, end=”)
j += 1
# 学分
scredit = tds[5].string
print(scredit.ljust(2, ‘ ‘) + ‘\t\t’, end=”)
j += 1
# 成绩
achievement = tds[6].string
print(achievement.ljust(2) + ‘\t\t’, end=”)
j += 1
# 重考一
reexaminef = tds[7].string
print(reexaminef.ljust(2) + ‘\t\t’, end=”)
j += 1
# 重考二
reexamines = tds[8].string
print(reexamines.ljust(2) + ‘\t\t’)
j += 1
i += 1

这里查了很多别人的博客都是用正则表达式来分解数据,表示自己的正则写的并不好也尝试了但是没成功,所以无奈选择这种方式,如果有人有测试成功的正则欢迎跟我说一声,我也学习学习。

把数据保存到excel

因为已经清楚了这个网页保存成绩的具体结构,所以顺着每次循环解析将数据不断加以保存就是了,这里使用xlwt写入数据到excel,因为xlwt模块打印输出到excel中的样式宽度偏小,影响观看,所以这里还加入了一个方法去控制打印到excel表格中的样式:

file = xlwt.workbook(encoding=’utf-8′)
table = file.add_sheet(‘achieve’)
# 设置excel样式
def set_style(name, height, bold=false):
style = xlwt.xfstyle() # 初始化样式
font = xlwt.font() # 为样式创建字体
font.name = name # ‘times new roman’
font.bold = bold
font.color_index = 4
font.height = height
style.font = font
return style

运用到代码中:

for tag in target[1:]:
tds = tag.findall(‘td’)
j = 0
# 学期
semester = str(tds[0].string)
if semester == ‘none’:
break
else:
print(semester.ljust(6) + ‘\t\t\t’, end=”)
table.write(i, j, semester, set_style(‘arial’, 220))
# 学号
studentid = tds[1].string
print(studentid.ljust(14) + ‘\t\t\t’, end=”)
j += 1
table.write(i, j, studentid, set_style(‘arial’, 220))
table.col(i).width = 256 * 16
# 姓名
name = tds[2].string
print(name.ljust(3) + ‘\t\t\t’, end=”)
j += 1
table.write(i, j, name, set_style(‘arial’, 220))
# 课程
course = tds[3].string
print(course.ljust(20, ‘ ‘) + ‘\t\t\t’, end=”)
j += 1
table.write(i, j, course, set_style(‘arial’, 220))
# 课程要求
requirments = tds[4].string
print(requirments.ljust(10, ‘ ‘) + ‘\t\t’, end=”)
j += 1
table.write(i, j, requirments, set_style(‘arial’, 220))
# 学分
scredit = tds[5].string
print(scredit.ljust(2, ‘ ‘) + ‘\t\t’, end=”)
j += 1
table.write(i, j, scredit, set_style(‘arial’, 220))
# 成绩
achievement = tds[6].string
print(achievement.ljust(2) + ‘\t\t’, end=”)
j += 1
table.write(i, j, achievement, set_style(‘arial’, 220))
# 重考一
reexaminef = tds[7].string
print(reexaminef.ljust(2) + ‘\t\t’, end=”)
j += 1
table.write(i, j, reexaminef, set_style(‘arial’, 220))
# 重考二
reexamines = tds[8].string
print(reexamines.ljust(2) + ‘\t\t’)
j += 1
table.write(i, j, reexamines, set_style(‘arial’, 220))
i += 1
file.save(‘demo.xls’)

最后稍加整合,写成一个方法:

# 获取成绩
# 这里num代表输入的学号,pagenum代表页数,总共76条数据,一页30条所以总共有三页
def getscore(num, pagenum, i, j):
score_healders = {‘connection’: ‘keep-alive’,
‘user – agent’: ‘mozilla/5.0 (windows nt 6.1; wow64) ‘
‘applewebkit/537.36 (khtml, like gecko) chrome/52.0.2743.82 safari/537.36’,
‘content – type’: ‘application / x – www – form – urlencoded’,
‘accept’: ‘text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8’,
‘content – length’: ’69’,
‘host’: ‘jwc.ecjtu.jx.cn’,
‘referer’: ‘http: // jwc.ecjtu.jx.cn / mis_o / main.php’,
‘upgrade – insecure – requests’: ‘1’,
‘accept – language’: ‘zh – cn, zh;q = 0.8’
}
score_url = ‘http://jwc.ecjtu.jx.cn/mis_o/query.php?start=’ + str(
pagenum) + ‘&job=see&=&name=&course=&class + num
score_data = {‘name’: ”,
‘stuid’: num,
‘course’: ”,
‘term’: ”,
‘classid’: ”,
‘submit’: ‘%b2%e9%d1%af’
}
score_response = sessions.post(score_url, data=score_data, headers=score_healders)
# 输出到文本
with open(‘text.txt’, ‘wb’) as f:
f.write(score_response.content)
content = score_response.content
soup = beautifulsoup(content, ‘html.parser’)
target = soup.findall(‘tr’)
try:
for tag in target[1:]:
tds = tag.findall(‘td’)
j = 0
# 学期
semester = str(tds[0].string)
if semester == ‘none’:
break
else:
print(semester.ljust(6) + ‘\t\t\t’, end=”)
table.write(i, j, semester, set_style(‘arial’, 220))
# 学号
studentid = tds[1].string
print(studentid.ljust(14) + ‘\t\t\t’, end=”)
j += 1
table.write(i, j, studentid, set_style(‘arial’, 220))
table.col(i).width = 256 * 16
# 姓名
name = tds[2].string
print(name.ljust(3) + ‘\t\t\t’, end=”)
j += 1
table.write(i, j, name, set_style(‘arial’, 220))
# 课程
course = tds[3].string
print(course.ljust(20, ‘ ‘) + ‘\t\t\t’, end=”)
j += 1
table.write(i, j, course, set_style(‘arial’, 220))
# 课程要求
requirments = tds[4].string
print(requirments.ljust(10, ‘ ‘) + ‘\t\t’, end=”)
j += 1
table.write(i, j, requirments, set_style(‘arial’, 220))
# 学分
scredit = tds[5].string
print(scredit.ljust(2, ‘ ‘) + ‘\t\t’, end=”)
j += 1
table.write(i, j, scredit, set_style(‘arial’, 220))
# 成绩
achievement = tds[6].string
print(achievement.ljust(2) + ‘\t\t’, end=”)
j += 1
table.write(i, j, achievement, set_style(‘arial’, 220))
# 重考一
reexaminef = tds[7].string
print(reexaminef.ljust(2) + ‘\t\t’, end=”)
j += 1
table.write(i, j, reexaminef, set_style(‘arial’, 220))
# 重考二
reexamines = tds[8].string
print(reexamines.ljust(2) + ‘\t\t’)
j += 1
table.write(i, j, reexamines, set_style(‘arial’, 220))
i += 1
except:
print(‘出了一点小bug’)
file.save(‘demo.xls’)

在模拟登陆操作后增加一个判断:

# 判断是否登陆
def islogin(num):
return_code = response.status_code
if return_code == 200:
if re.match(r”^\d{14}$”, num):
print(‘请稍等’)
else:
print(‘请输入正确的学号’)
return true
else:
return false

最后在main中这么调用:

if name == ‘main’:
num = input(‘请输入你的学号:’)
if islogin(num):
getscore(num, pagenum=0, i=0, j=0)
getscore(num, pagenum=1, i=31, j=0)
getscore(num, pagenum=2, i=62, j=0)

在pycharm下按alt+shift+x快捷键运行程序:

最终获取结果

至此,大功告成

以上就是python爬虫模拟登陆教务处并且保存数据到本地的详细内容,更多请关注 第一php社区 其它相关文章!

Posted in 未分类

发表评论