简介

因为本人十分爱忘记做作业,因此,想通过爬虫,爬取超星的学习通作业时间,并且进行定时提醒。

环境

阿里云轻量级服务器

Centos7

Anaconda3

先看效果展示

爬取过程

发送邮件

过程

需要了解的知识

http的访问机制

cookies,session是用来干嘛的

验证码登录的流程

页面的机制

python几个包的使用

requetst(网页请求包)

lxml,etree(网页界面处理包)

email,smtplib(邮件处理包)

muggle-ocr(验证码识别)

datetime和time(时间包)

Centos下的anaconda的使用

stmp邮件协议讲解

Centos如何进行定时任务

Centos关于邮件发送的端口

开始

(对于部分模块,有些博客写的非常好,我就不进行详述,但是会提供链接)

1.http的访问机制

一文搞懂HTTP协议(带图文)

2.cookies,session是用来干嘛的

cookie和session的区别

session和cookies的区别

3. 验证码登录的流程

验证码的原理及作用

简单来说(以超星学习为例子):

每次进入登录页面,它会先请求一个验证码的网址(不用管它code?***是什么。,它只是一个通过js的datetime函数得到的一个时间戳(不明白的同学可以去搜一搜)。不要太在意这部分,一开始,我就走入歧途,想通过这个来获取验证码,其实思路就错了,引以为戒)

进入该链接之后,得到一张图片

此时,表面上得到的是一张图片,实际上,在服务器端,它还生成了与之匹配的cookies信息,并且返回给了登录页面。而之后我们客户端就必须携带这个cookies以及账号密码信息进行访问。

4. 页面的机制

使用的chrome浏览器,打开F12就可以了解整个页面的转换过程。主要分为:

html静态网页(容易获取)

js后台操作函数(通过相关函数来进行动态加载显示页)

就比如这里的验证码图片,并不是静态加载的,因此直接获取到的html中并没有该图片的链接,反而是通过一个函数进行动态加载。

打开F12后主要页面如下:

在最后的图中的formdata就是本次登录的信息(账号,密码(base64加密),验证码等)

代码

#coding=UTF-8

#File name :爬虫超星

#Author:龙文汉

#Data:2020.10.16

#Description:使用爬虫爬取超星的作业详情,获取作业的截至时间

import time

import json

import requests

from lxml import etree

import matplotlib.pyplot as plt

import matplotlib.image as mpimg

import os

import base64

import datetime

import smtplib

from email.header import Header

from email.mime.text import MIMEText

from email.mime.image import MIMEImage

from email.mime.multipart import MIMEMultipart

import threading

import sys

import muggle_ocr

class PaChaongxin():

#main_function

def __init__(self,username,password,emai):

self.username = username

self.password = password

self.to = emai

self.code = 0

self.code_status = None #验证码正确还是失败

self.user_pas_status = None #账户名和密码

self.sender_mail = 'xxxx@xxx'#发送者邮件

self.sender_pass = 'xxxxxxx'  # 邮箱的stm密码

self.session = requests.session()

self.header = {

'User-Agent': 'xxxxxxxx'#自己的user_agent

}

def User_Pas(self):

#输入账号,密码

# self.username = input("请输入学号:")

# self.password = input("请输入密码:")

# self.to = input("请输入邮箱:")

#self.username = xxxxxxx

#self.password = 'xxxxx'

#self.to = 'xxxxxx'

return

def Get_code(self):

#获取验证码,以及携带的cookies

code_url = 'https://passport2.chaoxing.com/num/code'#超星验证码网址

path_path = 'vari_code.png'

code_response = self.session.get(code_url)

#保存验证码

img = open(path_path,'wb')

img.write(code_response.content)

img.close()

#显示验证码,并且初始化,人为输入,不适用识别程序

# img_open = Image.open('vari_code.png')

# img = mpimg.imread('vari_code.png',0)

# plt.imshow(img)  # 显示图片

# plt.axis('off')  # 不显示坐标轴

# plt.show()

# self.code = input("请输入验证码:")

print("befor:",self.code)

self.code = self.Code_Verifed()

print("after:", self.code)

os.remove(path_path)

def Load_Page(self):

#使用session进入登录界面验证

self.password = base64.b64encode(self.password.encode("utf-8"))  # 被编码的参数必须是二进制数据

param = {

'fid': 'xxxx',

'uname': self.username,

'numcode': self.code,

'password': self.password,

'refer': 'http%3A%2F%2Fi.chaoxing.com',

't': 'true'

}

header = {

'Accept': 'application/json, text/javascript, */*; q=0.01',

'Accept-Encoding': 'gzip, deflate, br',

'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,und;q=0.7',

'Connection': 'keep-alive',

'Content-Length': '109',

'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',

'Host': 'passport2.chaoxing.com',

'Origin': 'https://passport2.chaoxing.com',

'Referer': 'https://passport2.chaoxing.com/login?loginType=3&newversion=true&fid=-1&refer=http%3A%2F%2Fi.chaoxing.com',

'Sec-Fetch-Dest': 'empty',

'Sec-Fetch-Mode': 'cors',

'Sec-Fetch-Site': 'same-origin',

'User-Agent': 'xxxxxxxx',#注意自己更改

'X-Requested-With': 'XMLHttpRequest',

}

load_url = 'https://passport2.chaoxing.com/unitlogin?'

load_response = self.session.post(load_url,headers=header,data=param)

load_response_msg = json.loads(load_response.text)

print(load_response_msg,load_response_msg.keys())

if 'mes' in load_response_msg.keys():

if load_response_msg['mes'] == '验证码错误':

print('验证码错误')

self.code_status = True

while(self.code_status):

self.Get_code()

param['numcode'] = self.code

load_response = self.session.post(load_url, headers=header, data=param)

load_response_msg = json.loads(load_response.text)

if 'mes' not in load_response_msg.keys() or load_response_msg['mes'] != '验证码错误':

self.code_status = False

if 'mes' in load_response_msg.keys() and load_response_msg['mes'] == '用户名或密码错误':

print('用户名或密码错误')

self.user_pas_status = True

while(self.user_pas_status):

self.User_Pas()

self.Get_code()

param['numcode'] = self.code

param['uname'] = self.username

param['password'] = self.password

load_response = self.session.post(load_url, headers=header, data=param)

load_response_msg = json.loads(load_response.text)

if 'mes' not in load_response_msg.keys() and load_response_msg['mes'] != '用户名或密码错误':

self.code_status = False

#上面返回的应该是登录成功,接下来,带着新的cookies访问主页

self_page_url = 'http://i.mooc.chaoxing.com'

self_page_response = self.session.get(url=self_page_url,headers = self.header)

#个人空间的主页面,这里直接提取课程的部分,要在左侧的按钮里找到相对应的连接,

#该课程的页面直接镶嵌在本页面,所以带着session直接访问也可以

# self_page_html = etree.HTML(self_page.text)

# return self_page_html

def Get_Class_View(self):

#进入所有课程的界面

class_view_url = 'http://mooc1-2.chaoxing.com/visit/courses'

class_view = self.session.get(class_view_url,headers = self.header)

class_view_html = etree.HTML(class_view.text)

#直接返回整个页面的编码,方便后续的查找

return class_view_html

def Go_to_work(self,Singel_Class_Url_after):

#通过外面直接传来的课程网址,直接跳转

#进入单个课程的界面的作业模块

Singel_Class_Url = 'https://mooc1-2.chaoxing.com'+Singel_Class_Url_after

single_class_response = self.session.get(Singel_Class_Url,headers=self.header)

single_class_page = etree.HTML(single_class_response.text)

url_after = single_class_page.xpath("/html/body/div[4]/div/div/div[2]/ul/li[6]/a/@data")

if len(url_after) == 0:

open_zuoye_url = 'https://mooc1-2.chaoxing.com' + \

single_class_page.xpath("/html/body/div[2]/div/div/div[2]/ul/li[6]/a/@data")[0]

else:

open_zuoye_url = 'https://mooc1-2.chaoxing.com' + \

single_class_page.xpath("/html/body/div[4]/div/div/div[2]/ul/li[6]/a/@data")[0]

work_xml_response = self.session.get(open_zuoye_url,headers=self.header)

work_xml = etree.HTML(work_xml_response.text)

#这里的open_zuoye直接转到了作业的界面

#调用每个作业的函数,方便多线程

single_class_text = self.Get_work_time(work_xml)

return single_class_text

def Get_work_time(self,work_xml):

# 以上就是关于页面跳转的函数,接下来就是作业的截取

#作业的信息提取

work_num = len(work_xml.xpath('//*[@id="RightCon"]/div/div/div[2]/ul/li'))

class_name = work_xml.xpath('/html/body/div[2]/div/h1/span[1]/@title')[0]

#邮件的内容:[[课程],[作业名称,截至时间,剩余时间]*n]的列表

#无作业的课程直接返回

if work_num == 0:

return None

work_text_info = []#有作业的课程

work_text_info.append(class_name)#添加课程名

#对每个项目进行整理

for i in range(1,work_num+1):

work_name = work_xml.xpath('//*[@id="RightCon"]/div/div/div[2]/ul/li['+str(i)+']/div[1]/p/a/text()')[0].strip()

work_status = work_xml.xpath('/html/body/div[3]/div[1]/div/div/div/div[2]/ul/li['+str(i)+']/div[1]/span[3]/strong/text()')[0].strip()

work_end_time = work_xml.xpath('//*[@id="RightCon"]/div/div/div[2]/ul/li['+str(i)+']/div[1]/span[2]/text()')

# print(work_end_time)

if len(work_end_time) == 0:#无截至日期的课程

break

else:

work_end_time = work_end_time[0]

print(class_name,work_name,work_end_time)

if work_status != '已完成' and work_status != "待批阅":#存在未完成的课程

work_text_info.append([])

#增加作业名

work_text_info[-1].append(work_name)

#使用datatime,计算时间差

current_time = time.strftime("%Y-%m-%d %H:%M", time.localtime())

#转化位datatime的格式

current_time_tran = datetime.datetime.strptime(current_time ,"%Y-%m-%d %H:%M")

work_end_time_tran = datetime.datetime.strptime(work_end_time ,"%Y-%m-%d %H:%M")

time_mul = work_end_time_tran - current_time_tran

time_mul_day = time_mul.days

if time_mul_day <= 1:

time_m, time_s = divmod(time_mul.seconds, 60)

time_h, time_m = divmod(time_m, 60)

lea_time = '0天:'+str(time_h)+'小时:'+str(time_m)+'分钟'

#增加剩余时间

work_text_info[-1].append(lea_time)

else:

lea_time = str(time_mul_day)+'天'

work_text_info[-1].append(lea_time)

print(class_name,"已经结束")

#返回单课程的作业信息

return work_text_info

def Code_Verifed(self):

sdk = muggle_ocr.SDK(model_type=muggle_ocr.ModelType.Captcha)

with open(r'vari_code.png', 'rb') as f:

captcha_bytes = f.read()

code = sdk.predict(image_bytes=captcha_bytes)

return code

def send_email_by_qq(self,text):

# 利用邮箱发邮件提醒

# 设置总的邮件体对象,对象类型为mixed

msg_root = MIMEMultipart('mixed')

# 邮件添加的头尾信息等

msg_root['From'] = 'xxxx@xxxxx'

msg_root['To'] = self.to

# 邮件的主题,显示在接收邮件的预览页面

subject = '快到作业截止时间了!'

msg_root['subject'] = Header(subject, 'utf-8')

# 构造文本内容

text_inf = "未完成作业总览:\n"

for i in range(0,len(text)):

text_inf += text[i][0]+"\n"#添加标题

for x in range(1,len(text[i])):

text_inf += '\t\t'

text_inf += str(text[i][x])

text_inf += "\n\n"

text_sub = MIMEText(text_inf, 'plain', 'utf-8')

print(text_inf)

msg_root.attach(text_sub)

# # 构造超文本

# url = "https://blog.csdn.net/chinesepython"

# html_info = """

#

点击以下链接,你会去向一个更大的世界

#

click me

#

i am very galsses for you

# """% url

# html_sub = MIMEText(html_info, 'html', 'utf-8')

# # 如果不加下边这行代码的话,上边的文本是不会正常显示的,会把超文本的内容当做文本显示

# html_sub["Content-Disposition"] = 'attachment; filename="csdn.html"'

# # 把构造的内容写到邮件体中

# msg_root.attach(html_sub)

# # 构造图片

# image_file = open(r'D:\python_files\images\test.png', 'rb').read()

# image = MIMEImage(image_file)

# image.add_header('Content-ID', '')

# # 如果不加下边这行代码的话,会在收件方方面显示乱码的bin文件,下载之后也不能正常打开

# image["Content-Disposition"] = 'attachment; filename="red_people.png"'

# msg_root.attach(image)

# # 构造附件

# txt_file = open(r'D:\python_files\files\hello_world.txt', 'rb').read()

# txt = MIMEText(txt_file, 'base64', 'utf-8')

# txt["Content-Type"] = 'application/octet-stream'

# #以下代码可以重命名附件为hello_world.txt

# txt.add_header('Content-Disposition', 'attachment', filename='hello_world.txt')

# msg_root.attach(txt)

try:

sftp_obj = smtplib.SMTP_SSL('smtp.qq.com', 465)

sftp_obj.login(self.sender_mail, self.sender_pass)

sftp_obj.sendmail(self.sender_mail, self.to, msg_root.as_string())

sftp_obj.quit()

print('sendemail successful!')

except Exception as e:

print('sendemail failed next is the reason')

print(e)

def Begin(self):

self.User_Pas()#获取信息

self.Get_code()#获取验证码以及cookies信息,用session进行保存

self.Load_Page()#进入个人中心,更新cookies

class_view_html = self.Get_Class_View()#进入所有作业的单页面,并且返该界面

len_class = len(class_view_html.xpath('/html/body/div/div[2]/div[3]/ul/li'))#计算一共有多少个课程

all_text = []#总的邮件信息

for i in range(1,len_class):#开始遍历每门课

print('开始第',i)

Singel_Class_Url_after = class_view_html.xpath('/html/body/div/div[2]/div[3]/ul/li['+str(i)+']/div[2]/h3/a/@href')[0]

# print(Singel_Class_Url_after)

# all_text.append([threading.Thread(target=self.Go_to_work,args=(Singel_Class_Url_after)).start()])

work_text = self.Go_to_work(Singel_Class_Url_after)

if work_text == None or len(work_text) <= 1:

continue

else:

all_text.append(work_text)

#将没有作业的课程删除

return all_text

if __name__ == '__main__':

#while 1:

#time_list = time.strftime("%H:%M:%S", time.localtime()).split(":")

# if time_list[0] == "08" and time_list[1] == "00" and time_list[2] == "00":

#    pachong = PaChaongxin(201821094053,"Mmgh774109","1766446371@qq.com")

#    work_email = pachong.Begin()

#    pachong.send_email_by_qq(work_email)

#    pachong = PaChaongxin(201821094049,"wcsba8102","2651447403@qq.com")

#    work_email = pachong.Begin()

#    pachong.send_email_by_qq(work_email)

pachong = PaChaongxin(学号,密码,邮箱)

work_email = pachong.Begin()

pachong.send_email_by_qq(work_email)

pachong = PaChaongxin(xxxxxxx,"xxxxxxx","xxxxx@xxxxx")

work_email = pachong.Begin()

pachong.send_email_by_qq(work_email)

总结

果然任务驱动加上兴趣的学习,能更加扩展知识面,本次实例,都了解了:爬虫知识,网络知识,密码学,云服务器的使用,邮件知识,python的使用。总体来说,受益匪浅。希望大家能共同进步

PS:如有需要Python学习资料的小伙伴可以加点击下方链接自行获取

centos7 python3 爬虫登陆邮箱_使用爬虫爬取超星学习通的作业时间并且通过邮件提醒!...相关推荐

  1. python爬虫小案例_从豆瓣爬取《哪吒之魔童降世》的剧照

    一.在豆瓣电影网爬取以下剧照保存到本地: 本次案例只爬取前 5 页的剧照,先获取前五页的链接: for i in range(5):url = 'https://movie.douban.com/su ...

  2. 爬取超星考试题目_如何利用搜题软件查找《超星慕课》考试题目答案?

    A.The woman is a kind-hearted boss.B.The woman is strict with her employees.C.The man [C6]A.illegalB ...

  3. 爬取超星考试题目_如何使用网页的源代码分析找到超星线上考试的答案

    [HTML] 纯文本查看 复制代码 .Cy_TItle p {display: block;} img {max-width: 100%;} try{ $(function(){ var imgLis ...

  4. python 通登录银行_带你了解Python|自动化实现学习通登录

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理 以下文章来源于腾讯云,作者:算法与编程之美. 前言 最近小编在学习python自动 ...

  5. [网络爬虫|smtp协议|python]东方财富网爬虫,python smtp协议发送爬取数据至QQ邮箱

    本文改自 [网络爬虫|smtp协议|python]东方财富网爬虫,python smtp协议发送爬取数据至QQ邮箱 之前写的爬虫单子,代码已经跑了快3个月了,后续又增加了一些需求,修改了一些小bug ...

  6. python 扒数据_不踩坑的Python爬虫:如何在一个月内学会爬取大规模数据

    Python爬虫为什么受欢迎 如果你仔细观察,就不难发现,懂爬虫.学习爬虫的人越来越多,一方面,互联网可以获取的数据越来越多,另一方面,像 Python这样的编程语言提供越来越多的优秀工具,让爬虫变得 ...

  7. python3爬虫实战:requests库+正则表达式爬取头像

    python3爬虫实战:requests库+正则表达式爬取头像 网站url:https://www.woyaogexing.com/touxiang/qinglv/new/ 浏览网页:可以发现每个图片 ...

  8. python爬虫怎么爬小说_小白的python爬虫,40代码教你爬取豆瓣小说

    这篇文章写了很久了,一直没有发布: 爬虫学的差不多了,觉得这篇文章对新手实践还是有些作用的.毕竟这也是我刚学爬虫的时候练习的,爬取了比较好爬的网站,也比较经典:多余的解释不说了,代码里每一行都有注释, ...

  9. python爬取豆瓣代码_小白的python爬虫,40代码教你爬取豆瓣小说

    这篇文章写了很久了,一直没有发布: 爬虫学的差不多了,觉得这篇文章对新手实践还是有些作用的.毕竟这也是我刚学爬虫的时候练习的,爬取了比较好爬的网站,也比较经典:多余的解释不说了,代码里每一行都有注释, ...

  10. mysql 去重con_python 爬虫 实现增量去重和定时爬取实例

    前言: 在爬虫过程中,我们可能需要重复的爬取同一个网站,为了避免重复的数据存入我们的数据库中 通过实现增量去重 去解决这一问题 本文还针对了那些需要实时更新的网站 增加了一个定时爬取的功能: 本文作者 ...

最新文章

  1. cv2.putText
  2. 附加没有日志文件的数据库方法
  3. 注册服务(addService)
  4. win10安装mysql无服务_win10装sql没有SQL Server 服务
  5. 企业过快移向MBaaS很危险
  6. Spring事务属性详解
  7. GNU make manual 翻译( 一百七十五)
  8. debug 标志位说明
  9. 在 .NET Core 中运行 JavaScript
  10. 前端学习(3302):createRef
  11. vv7无法启动显示发动机故障_点火系统故障引起发动机不能启动的排除方法
  12. 国民财商教育白皮书(2021年)
  13. c++ sort 从大到小排序_C语言必学的12个排序算法:堆排序(第7篇)
  14. 【python】Python的基本数据类型之数字类型与字符串类型
  15. 能搜python题的软件_中国大学MOOC的APP用Python玩转数据期末考试搜题公众号答案...
  16. 20145235李涛《网络对抗》Exp8 Web基础
  17. win10安装ubuntu虚拟机
  18. 机器学习之PCA算法的人脸图像识别-平均脸的计算(详细操作步骤)
  19. 数据结构课程设计-哈夫曼树及其应用
  20. Pytorch实现mnist手写数字识别

热门文章

  1. matlab版VLFeat安装
  2. 视频分辨率全表(101种)
  3. 认知无线电-频谱检测-匹配滤波
  4. Android之PreferenceActivity
  5. win7桌面图标消失的3种解决方案
  6. Excel数据透视表经典教程九《数据透视表计算》
  7. 个人信息安全管理条例解释
  8. 读 自己动手写操作系统
  9. matlab理论力学项目研究,基于MATLAB的机械力学问题的研究
  10. 如何配置微信公众号卡密回复?