一个小demo
旧文
原文发在**这里 **
假期闲来无事,看书之余突然想起来,总是登录教务网、点击、点击、查询成绩很是麻烦,于是想动手写一个小脚本。遂有此文。
简介
- 自动登录 ,使用python requests 包来实现;
- 页面指定内容抓取, 使用BeautifulSoup 包实现;
- 邮件发送 ,使用SMTP 和 email 包实现
具体内容
涉及到的import
import requests,re
from bs4 import BeautifulSoup
import types
import smtplib
from email.mime.text import MIMEText
import os
auto login
session = requests.session()
user=input("input your userName:") # get user name from console input
password=input("input your password:") # get user password from console input
p=session.post("http://jwas3.nju.edu.cn:8080/jiaowu/login.do", {'userName':user,"password": password}) # This is the login website
request_url = session.get("http://jwas3.nju.edu.cn:8080/jiaowu/student/studentinfo/achievementinfo.do?method=searchTermList") # the web site we want to get the contents from
request_text=request_url.text # get web text
- 上面的代码很容易理解,建立一个session会话,首先进行post登录,之后get想要抓取内容的网页来获取内容,requests的使用可以查询其文档
两个网址是如何得到的呢?
- 首先我们打开要抓取的网页,发现它会自动转到一个需要登录的页面
- 使用chrome NetWork进行跟踪,此时我们人工登录一次,会发现NetWork中会出现类似login命名的一个post方式请求的项,点击查询,会找到对应请求的Header和Form内容
- 模拟上述进行requests即可
get content from HTML document
抓到对应的表格
soup=BeautifulSoup(request_text)
content_1=soup.body.find_all("tr","TABLE_TR_01")
content_2=soup.body.find_all("tr","TABLE_TR_02")
- BeautifulSoup 的文档介绍十分简单易懂,可以直接照搬取到想要的内容,文档
一个obj类
class obj:
def __init__(self,n_z,n_e,t,c,g):
self.name_zh=n_z
self.name_en=n_e
self.type=t
self.credit=c
self.grade=g
def display(self):
print("name: "+self.name_zh)
print("name_en: "+self.name_en)
print("type: "+self.type)
print("credit: "+self.credit)
print("grade: "+self.grade)
print("")
def getStr(self):
temp="name: "+self.name_zh+"\nname_en: "+self.name_en+"\ntype: "+self.type+"\ncredit: "+self.credit+"\ngrade: "+self.grade
temp+="\n"
return temp
建立了一个obj类方便数据的传递和输出等,obj类的成员就是每个科目的中文名、英文名、科目类型、学分、成绩,display用于调试中展示其内容,getStr返回一个输出字符串,用于我们发送邮件时使用
解析内容建立obj对象列表
obj_list=[]
for i in content_1:
obj_list.append(parse(i))
for i in content_2:
obj_list.append(parse(i))
result=""
for i in obj_list:
#print("order: "+str(j))
#i.display()
result+=i.getStr()
result+="\n"
接着我们从 content_1 和 content_2 的HTML文件中解析出obj对象的成员并加入到list当中
下面是上面用到的parse函数
def parse(content):
name_zh="";name_en="";types="";study_grade=grade=0;
pre_list=content.contents # 取content的contents成员列表
# print(pre_list)
name_zh=pre_list[5].contents[0] # 这里的下标5、7、9、11、13是从pre_list的内容中人工获得或者find来获得
name_en=pre_list[7].contents[0]
type_temp=pre_list[9].contents[0]
types="".join(type_temp.split())
study_grade=pre_list[11].contents[0]
grade=pre_list[13].contents # 这里没有直接取contents[0]的原因是有无成绩的情况下,这里的项数会有区分,所以单独在下面进行处理
if len(grade)==1:
grade="无成绩"
else:
grade=grade[1]
pattern=re.compile(r'\d+\.\d')
grade=pattern.findall(str(grade))[0]
temp=obj(name_zh,name_en,types,study_grade,grade) # 返回处理好的obj对象
#temp.display()
return temp
发送邮件
如何发送?
def sendEmail():
From="xxxxxx@xx.com"
To=" xxxxxxxx@xx.com"
msg=MIMEText(result.encode('utf-8'),_charset='UTF-8')
msg['Subject']="成绩有改动"
msg['From']=From
msg['To'] = To
s=smtplib.SMTP('smtp.xx.com',25)
s.login(From,"From password")
s.sendmail(From,To,msg.as_string())
s.quit()
发送邮件的方法也见其文档说明,需要注意的是,计算机名需要是英文的,否则对应的库中的编码会出现问题
何时发送?
file=open("copy_local.txt","r+")
result_old=""
for eachline in file:
result_old+=eachline
print("old: "+result_old)
if result_old!=result:
sendEmail()
file.seek(0)
file.truncate()
file.write(result)
else:
print("nothing has changed.")
file.close()
- 本地建立一个拷贝,每次抓取后和本地副本进行对比,如果相同则不发送,如果不同,则发送邮件并更新本地副本。
- 这里有很多可以优化的地方,比如并不需要比对全文,可以存储一个项数,这样只需要比较科目数即可,也不需要文件IO的时候将全文均读取
最后
- 如果没有服务器,这个只是一个demo,没有太大的使用价值,不过如果有服务器或者PC一直开机的话,可以设置上述抓取内容的部分定时进行运行,比如每1h运行一次,这样就可以很好的实现预期的功能了;(阿里云可以申请6个月的服务器,不过没有外网ip,只能从内网ip登陆)
- 文档和google是我们的好伙伴,上述100行的python代码写的过程中遇到不少细节问题都通过搜索得到了解决;
- 看书不容易专注的时候给自己定一些小任务来做,也是一种提高啦,而且做完之后更容易看的进去了。