python 爬虫(小功能实现)

要想完成本次 小功能程序需要 的环境:

  • python 3.6 以上 (我用的是 3.6.7的) 此链接下载[提取码是: apge]
  • QQ邮箱 授权码(大家自行百度)

相关依赖 (如果报错的话 没有那个库 就去 pip install 一下就好)

  • requests_html (主要是这个 其它的【如果没记错的话】 都是python 自带的 不用去下载)
pip install requests_html -i https://pypi.douban.com/simple

需要注意的地方是 某某通里面的课程表需要自己 填补完成

废话不多说了 直接上代码吧

  1. 登陆模块
def moni_request():
    '''
    某某通 模拟登录 获取 cookie
    :return: None
    '''
    email = '' # 账号
    pwd = ''  # 密码
    url = 'http://passport2.chaoxing.com/login?newversion=true'
    response = session.get(url)
    fid = response.html.xpath('//input[@id="fid"]/@value')[0]
    refer = response.html.xpath('//input[@id="refer"]/@value')[0]
    forbidotherlogin = response.html.xpath('//input[@id="forbidotherlogin"]/@value')[0]
    t = response.html.xpath('//input[@id="t"]/@value')[0]
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
        'Host': 'passport2.chaoxing.com',
        'Referer': 'http://passport2.chaoxing.com/login?newversion=true',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36',
        'X-Requested-With': 'XMLHttpRequest'
    }
    pwd = str(base64.b64encode(pwd.encode('utf-8')), 'utf-8')
    data = {
        'fid': fid,
        'uname': email,
        'password': pwd,
        'refer': refer,
        't': t,
        'forbidotherlogin': forbidotherlogin
    }
    url = 'http://passport2.chaoxing.com/fanyalogin'
    session.post(url, data=data, headers=headers)
  1. 获取 课程表 信息
# 本周 课程表
def course_table():
    '''
    获取 本周的 课程表
    :return: time_ 一天课程表 的安排
    course 本周课程的 相关信息
    '''
    url = 'https://kb.chaoxing.com/pc/curriculum/getMyLessons'
    data = session.get(url).json()['data']
    time_ = data['curriculum']['lessonTimeConfigArray']
    course = []
    for i in data['lessonArray']:
        dayOfWeek = i['dayOfWeek']
        location = i['location']
        beginNumber = i['beginNumber']
        name = i['name'].split('-')[0] if '-' in i['name'] else i['name']
        teacherName = i['teacherName']
        course.append((name, teacherName, location, dayOfWeek, beginNumber))
    return time_, course
  1. 获取当天的 课程 信息
# 获取 当天的 课程
def to_day_course(time_, course):
    '''
    处理 获取到的 课程时间安排 与课程
    得到 当天的 课程与课程对应的时间
    :param time_:  课程 时间安排
    :param course: 本学期 的课程
    :return: 处理后当天的 课程相关信息
    '''
    dayOfWeek = datetime.now().isoweekday()
    data_time = []
    for i in course:
        if i[-2] == dayOfWeek:
            data = (time_[i[-1] - 1].split('-')[0], i)
            data_time.append(data)
    return data_time
  1. 通过的到的相关课程信息,匹配出 当前时间 可能需要 模拟签到的 课程
def getLastedCourseInfo(currentCourse, course_name):
    """
    :param currentCourse:  本学期的 课程详情
    :param course_name:    当天最新的 可能可以 模拟签到的 科目
    :return:  返回一个 url
    """
    for i in currentCourse:
        if i[1] == course_name:
            return i[0]
    return '没有找到相关课程'

  1. 这一点功能就是我 特别想实现的 功能(啊哈哈,大家都懂的…)------- 模拟签到
# 用于完成签到任务
def get_detail_course(url):
    response = session.get(url)
    courseid = response.html.xpath('//input[@id="courseid"]/@value')[0]
    clazzid = response.html.xpath('//input[@id="clazzid"]/@value')[0]
    fid = response.html.xpath('//input[@id="fid"]/@value')[0]
    param = f'fid={fid}&courseId={courseid}&classId={clazzid}&_={int(time.time() * 1000)}'
    url = 'https://mobilelearn.chaoxing.com/v2/apis/active/student/activelist'
    response = session.get(url, params=param).json()['data']['activeList']
    sign_id = []
    TenXunUrl_list = []
    for i in response:
        if i['endTime'] // 1000 > time.time():  # 判断当前时间 是否 超过 通知有效时间
            if i['type'] == 2: # 判断是不是 签到通知
                sign_id.append(i['id'])
            elif i['type'] == 64:  # 判断 是不是会议id
                url = json.loads(i['content'])['teaUrl'].decode()
                TenXunUrl_list.append(url)
    if not sign_id:
        return '该课程还没有签到通知'
    if TenXunUrl_list:
        TenXunId = session.get('https://meeting.tencent.com/dm/YeclwuIgRbhx').html.xpath('//span[@id="tm-meeting-code"]/text()')[0]
        send_message('会议地址地址', TenXunId)
    # 自动签到
    url = 'https://mobilelearn.chaoxing.com/v2/apis/sign/signIn'
    data = f'activeId={sign_id[0]}'
    response = session.get(url, params=data).json()
    if response['msg'] == 'success':
        logging.info(f'{response["data"]["name"]}\t签到 [成功] \n提交时间为{response["data"]["submittime"]}')
        return response["data"]["submittime"]
    else:
        logging.warning(f'{response["data"]["name"]}\t签到 [异常] \n提交时间为{response["data"]["submittime"]}')
  1. 发送邮件
# 发送邮件
def send_message(suject, data):
    '''
    用于发送 qq邮箱
    :param suject:  发送邮箱 的主题。一共有 三种 大类型 (当天课程提醒,签到成功,会议地址)
    :param data:  用于 传递 相关发送 信息
    :return:  返回 发送 成功与失败 (type=string)
    '''
    if suject == '当天课程提醒':
        key_word = data
    elif suject == '签到成功':
        course_name, operator_time = data
        key_word = f'<h1>{course_name}</h1><p>签到时间为:{operator_time}</p>'
    else:
        key_word = f'<p>这是会议的 会议id</p><p style="font-size:50px;color:green">{data}</p>'
    msg_from = 'qqnumber@qq.com' # 发件人
    msg_to = 'QQnumber@qq.com'  # 收件人
    msg = MIMEMultipart('related')
    html_ = f"""
                <html>
                <body>
                  <div style="width: 100%; font-size: 20px;background-color: #5ebc5c;margin-top:-180px">{key_word}</div>
                </body>
                </html>
            """
    content = MIMEText(html_, 'html', 'utf-8')
    msg.attach(content)
    msg['Subject'] = suject
    msg['From'] = msg_from
    msg['To'] = msg_to
    passwd = '' # 生成的 授权码
    try:
        s = smtplib.SMTP_SSL("smtp.qq.com", 465)
        s.login(msg_from, passwd)
        s.sendmail(msg_from, msg_to, msg.as_string())
        logging.info(f'邮件发送成功 [{suject}]')
        return '发送成功'
    except Exception as e:
        logging.error(f'邮件发送失败 [{suject}][{e}]')
        return '发送失败'

    finally:
        s.quit()
  1. 一个指定 线程的 函数 用于实现 发送 当天课程提醒信息 功能
# 指定的 线程函数
def func_Thead(Today_course):
    '''
    开启线程的 指定函数 也就是 完成 发送当天的 课程 安排 情况
    :param Today_course:  当天 课程的 相关信息
    :return:  None
    '''
    data = f'<h2>今天一共{len(Today_course)}课</h2><br>'
    count = 1
    for i in Today_course:
        data += f'<h2>第{count}节课:</h2><br>'
        time_ = i[0]
        course_name = i[1][0]
        teacher_name = i[1][1]
        address = i[1][2]
        data += f'''
        <p style="margin-left:30px;color:#619656">课程名称:&emsp;&emsp;{course_name}</p>
        <p style="margin-left:30px;color:#619656">上课老师:&emsp;&emsp;{teacher_name}</p>
        <p style="margin-left:30px;color:#619656">上课时间:&emsp;&emsp;{time_}</p>
        <p style="margin-left:30px;color:#619656">上课地点:&emsp;&emsp;{address}</p>
        '''
        count += 1
    while True:
        if datetime.now().hour == 7 and datetime.now().minute >= 0:
            if send_message('当天课程提醒', data) == '发送成功':
                logging.info(f'当天课程提醒 发送 [成功] 信息:[{data}]')
                break
        time.sleep(120)

全部代码奉上

# -*- encoding: utf-8 -*-
# @Time       :  9:24
# @Author     : yuxian
# @File       : demo.py
# @SoftWare   : PyCharm
import base64
import json
import time
from requests_html import HTMLSession
from datetime import datetime
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import smtplib
from threading import Thread
import logging

logging.basicConfig(level=logging.INFO, filename='utils.log',
                    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') # 指定日志 输出 格式
logger = logging.getLogger(__name__)  # 创建 日志器

session = HTMLSession()


def moni_request():
    '''
    模拟登录 获取 cookie
    :return: None
    '''
    email = ''
    pwd = ''
    url = 'http://passport2.chaoxing.com/login?newversion=true'
    response = session.get(url)
    fid = response.html.xpath('//input[@id="fid"]/@value')[0]
    refer = response.html.xpath('//input[@id="refer"]/@value')[0]
    forbidotherlogin = response.html.xpath('//input[@id="forbidotherlogin"]/@value')[0]
    t = response.html.xpath('//input[@id="t"]/@value')[0]
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
        'Host': 'passport2.chaoxing.com',
        'Referer': 'http://passport2.chaoxing.com/login?newversion=true',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36',
        'X-Requested-With': 'XMLHttpRequest'
    }
    pwd = str(base64.b64encode(pwd.encode('utf-8')), 'utf-8')
    data = {
        'fid': fid,
        'uname': email,
        'password': pwd,
        'refer': refer,
        't': t,
        'forbidotherlogin': forbidotherlogin
    }
    url = 'http://passport2.chaoxing.com/fanyalogin'
    session.post(url, data=data, headers=headers)


# 本周 课程表
def course_table():
    '''
    获取 本周的 课程表
    :return: time_ 一天课程表 的安排
    course 本周课程的 相关信息
    '''
    url = 'https://kb.chaoxing.com/pc/curriculum/getMyLessons'
    data = session.get(url).json()['data']
    time_ = data['curriculum']['lessonTimeConfigArray']
    course = []
    for i in data['lessonArray']:
        dayOfWeek = i['dayOfWeek']
        location = i['location']
        beginNumber = i['beginNumber']
        name = i['name'].split('-')[0] if '-' in i['name'] else i['name']
        teacherName = i['teacherName']
        course.append((name, teacherName, location, dayOfWeek, beginNumber))
    return time_, course


# 获取 今天的 课程
def to_day_course(time_, course):
    '''
    处理 获取到的 课程时间安排 与课程
    得到 当天的 课程与课程对应的时间
    :param time_:  课程 时间安排
    :param course: 本学期 的课程
    :return: 处理后当天的 课程相关信息
    '''
    dayOfWeek = datetime.now().isoweekday()
    data_time = []
    for i in course:
        if i[-2] == dayOfWeek:
            data = (time_[i[-1] - 1].split('-')[0], i)
            data_time.append(data)
    return data_time


# 当前时间 与 课程时间 比较
def get_nowTime(data):
    '''
    本函数 用于处理 当前时间 与 课程时间的 的关系
    :param data:  当天 每一门课程的 相关信息
    :return:  type = bool型  如果满足设定的 条件 就返回 True 否则反之
    '''
    start = data
    start_time = int(start.split(':')[0]) * 60 + int(start.split(':')[1])
    now_time = datetime.now().hour * 60 + datetime.now().minute
    if -30 <= start_time - now_time <= 3:
        return True
    return False


def getLastedCourseInfo(currentCourse, course_name):
    """
    :param currentCourse:  本学期的 课程详情
    :param course_name:    当天最新的 可能需要 模拟签到的 课程
    :return:  返回一个 url
    """
    for i in currentCourse:
        if i[1] == course_name:
            return i[0]
    return '没有找到相关课程'


# 课程详细 路由 与名称
def course_data():
    '''
    获取 当前时间 最近的 一门课程 的请求地址 与 课程名称
    :return:
    '''
    url = 'https://mooc1-2.chaoxing.com/visit/courselistdata'
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
        'X-Requested-With': 'XMLHttpRequest'
    }
    data = 'courseType=1&courseFolderId=0&courseFolderSize=0'
    response = session.post(url, headers=headers, data=data)
    url_name = response.html.xpath('//h3[@class="inlineBlock"]/a/@href | //h3[@class="inlineBlock"]/a/span/@title')
    return [(url_name[i], url_name[i + 1]) for i in range(0, len(url_name), 2)]


# 完成签到任务
def get_detail_course(url):
    response = session.get(url)
    courseid = response.html.xpath('//input[@id="courseid"]/@value')[0]
    clazzid = response.html.xpath('//input[@id="clazzid"]/@value')[0]
    fid = response.html.xpath('//input[@id="fid"]/@value')[0]
    param = f'fid={fid}&courseId={courseid}&classId={clazzid}&_={int(time.time() * 1000)}'
    url = 'https://mobilelearn.chaoxing.com/v2/apis/active/student/activelist'
    response = session.get(url, params=param).json()['data']['activeList']
    sign_id = []
    TenXunUrl_list = []
    for i in response:
        if i['endTime'] // 1000 > time.time():  # 判断当前时间 是否 超过 通知有效时间
            if i['type'] == 2: # 判断是不是 签到通知
                sign_id.append(i['id'])
            elif i['type'] == 64:  # 判断 是不是会议
                url = json.loads(i['content'])['teaUrl'].decode()
                TenXunUrl_list.append(url)
    if not sign_id:
        return '该课程还没有签到通知'
    if TenXunUrl_list:
        TenXunId = session.get('https://meeting.tencent.com/dm/YeclwuIgRbhx').html.xpath('//span[@id="tm-meeting-code"]/text()')[0]
        send_message('会议地址', TenXunId)
    # 自动签到
    url = 'https://mobilelearn.chaoxing.com/v2/apis/sign/signIn'
    data = f'activeId={sign_id[0]}'
    response = session.get(url, params=data).json()
    if response['msg'] == 'success':
        logging.info(f'{response["data"]["name"]}\t签到 [成功] \n提交时间为{response["data"]["submittime"]}')
        return response["data"]["submittime"]
    else:
        logging.warning(f'{response["data"]["name"]}\t签到 [异常] \n提交时间为{response["data"]["submittime"]}')


# 发送邮件
def send_message(suject, data):
    '''
    用于发送 qq邮箱
    :param suject:  发送邮箱 的主题。一共有 三种 大类型 (当天课程提醒,签到成功,会议地址)
    :param data:  用于 传递 相关发送 信息
    :return:  返回 发送 成功与失败 (type=string)
    '''
    if suject == '当天课程提醒':
        key_word = data
    elif suject == '签到成功':
        course_name, operator_time = data
        key_word = f'<h1>{course_name}</h1><p>签到时间为:{operator_time}</p>'
    else:
        key_word = f'<p>这是会议的 会议id</p><p style="font-size:50px;color:green">{data}</p>'
    msg_from = ''
    msg_to = ''
    msg = MIMEMultipart('related')
    html_ = f"""
                <html>
                <body>
                  <div style="width: 100%; font-size: 20px;background-color: #5ebc5c;margin-top:-180px">{key_word}</div>
                </body>
                </html>
            """
    content = MIMEText(html_, 'html', 'utf-8')
    msg.attach(content)
    msg['Subject'] = suject
    msg['From'] = msg_from
    msg['To'] = msg_to
    passwd = ''
    try:
        s = smtplib.SMTP_SSL("smtp.qq.com", 465)
        s.login(msg_from, passwd)
        s.sendmail(msg_from, msg_to, msg.as_string())
        logging.info(f'邮件发送成功 [{suject}]')
        return '发送成功'
    except Exception as e:
        logging.error(f'邮件发送失败 [{suject}][{e}]')
        return '发送失败'

    finally:
        s.quit()


# 指定的 线程函数
def func_Thead(Today_course):
    '''
    开启线程的 指定函数 也就是 完成 发送当天的 课程 安排 情况
    :param Today_course:  当天 课程的 相关信息
    :return:  None
    '''
    data = f'<h2>今天一共{len(Today_course)}课</h2><br>'
    count = 1
    for i in Today_course:
        data += f'<h2>第{count}节课:</h2><br>'
        time_ = i[0]
        course_name = i[1][0]
        teacher_name = i[1][1]
        address = i[1][2]
        data += f'''
        <p style="margin-left:30px;color:#619656">课程名称:&emsp;&emsp;{course_name}</p>
        <p style="margin-left:30px;color:#619656">上课老师:&emsp;&emsp;{teacher_name}</p>
        <p style="margin-left:30px;color:#619656">上课时间:&emsp;&emsp;{time_}</p>
        <p style="margin-left:30px;color:#619656">上课地点:&emsp;&emsp;{address}</p>
        '''
        count += 1
    while True:
        if datetime.now().hour == 7 and datetime.now().minute >= 0:
            if send_message('当天课程提醒', data) == '发送成功':
                logging.info(f'当天课程提醒 发送 [成功] 信息:[{data}]')
                break
        time.sleep(120)


if __name__ == '__main__':
    attention = []  # 用于校验 是否 今天的课程 是否 已经 自动签到
    moni_request()
    time_, courses = course_table()
    Today_course = to_day_course(time_, courses)
    Thread(target=func_Thead, args=(Today_course,)).start()  # 启用线程

    currentCourse = course_data()
    while True:
        for course in Today_course:
            if get_nowTime(course[0]):  # 判断 最新时间 是否 满足条件
                url_or_other = getLastedCourseInfo(course[1][0], currentCourse)
                if url_or_other != '没有找到相关课程':
                    sign_return = get_detail_course(url_or_other)
                    if sign_return != '该课程还没有签到通知':
                        send_message('签到成功', (course[1][0], sign_return))
                        attention.append(course[1][0])
                else:
                    continue
                time.sleep(20)
        if len(Today_course) == len(attention):
            break
        time.sleep(60)

第一次 写博客(写这篇文章纯属娱乐,大佬勿喷,哈哈) 有错误的地方请大佬指正,看都看到这里 点个赞呗。最后,这篇文章纯属 用于个人 娱乐,无侵犯之意(如有侵权之处,请告知)

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值