高考分数查询结果自动推送至微信(卷II)

news2024/11/25 21:53:20

祝各位端午节安康!只要心中无,每天都是

        在上一篇文章高考分数查询结果自动推送至微信(卷Ⅰ)-CSDN博客中谈了思路,今天具体实现。文中将敏感信息已做处理,读者根据自己的实际情况替换。

主要逻辑

- 轮询接口列表

- 提取IP和端口

- 替换查询接口的IP和端口

- 替换验证码接口的IP和端口

- 检测可达性

- 成功查询后保存结果并推送消息,跳出循环

某省去年放出的查询接口主页

主页源码中与查询入口有关的网页源码(部分)

<ul class="cfrk">
    <li> <a href="http://xxx.xxx.xxx.xxx:81/n_score/index.jsp" class="gkcj-btn">入口一</a></li>
    <li>  <a href="http://xxx.xxx.xxx.xxx:82/n_score/index.jsp" class="gkcj-btn">入口二</a></li>
    <li><a href="http://xxx.xxx.xxx.xxx:81/n_score/index.jsp" class="gkcj-btn">入口三</a></li>
    <li>  <a href="http://xxx.xxx.xxx.xxx:83/n_score/index.jsp" class="gkcj-btn">入口四</a></li>
    <li><a href="http://xxx.xxx.xxx.xxx:81/n_score/index.jsp" class="gkcj-btn">入口五</a> </li>
	中间省略若干行......
    <li>  <a href="http://xxx.xxx.xxx.xxx:86/n_score/index.jsp" class="gkcj-btn">入口十六</a></li>
</ul>

我们从上面接口列表中提取IP和端口,检测可达性,自动查询分数,然后推送微信。

完整程序

# coding=utf-8
import requests
from bs4 import BeautifulSoup
import time
import itertools
import json
import random
import ddddocr
import onnxruntime
import schedule

# 设置日志级别用于去除ddddocr广告
onnxruntime.set_default_logger_severity(3)

# 你的 Server 酱 SCKEY
SERVER_CHAN_SCKEY = 'YOUR_SERVER_CHAN_SCKEY'  # 替换成你的 SCKEY


def imgRecognition(img):
    """
    识别验证码图片内容

    :param img: 图片文件路径
    :return: 返回识别结果字符串,如果失败则返回None
    """
    try:
        ocr = ddddocr.DdddOcr()
        with open(img, 'rb') as f:
            img_bytes = f.read()
        res = ocr.classification(img_bytes)
        return res
    except Exception as e:
        print(f"OCR failed: {e}")
        return None


def getVerifyimagepage(session, headers, cookies, captcha_url):
    """
    获取验证码图片并保存为 img.jpg

    :param session: requests.Session 对象
    :param headers: 请求头信息
    :param cookies: 请求时带的Cookie
    :param captcha_url: 验证码URL
    :return: 无
    """
    time.sleep(random.random())
    t = random.uniform(0.0, 1.0)
    params = {
        'rnd': t,
    }
    try:
        res = session.get(captcha_url, headers=headers, params=params, cookies=cookies, verify=False)
        with open('img.jpg', 'wb') as img:
            img.write(res.content)
    except Exception as e:
        print(f"Failed to get image: {e}")


def get_Kaptcha(session, headers, cookies, captcha_url):
    """
    获取验证码图片并进行识别

    :param session: requests.Session 对象
    :param headers: 请求头信息
    :param cookies: 请求时带的Cookie
    :param captcha_url: 验证码URL
    :return: 返回识别结果字符串,如果失败则返回None
    """
    getVerifyimagepage(session, headers, cookies, captcha_url)
    return imgRecognition('img.jpg')


def get_query_endpoints(session, base_url):
    """
    获取包含查询接口的URL列表

    :param session: requests.Session 对象
    :param base_url: 主页URL
    :return: 返回查询接口URL列表,如果获取失败则返回空列表
    """
    try:
        response = session.get(base_url)
        response.raise_for_status()
        html_content = response.text
        soup = BeautifulSoup(html_content, 'html.parser')
        ul_tag = soup.find('ul', class_='cfrk')
        if ul_tag:
            return [a['href'] for a in ul_tag.find_all('a')]
        else:
            print("No query endpoints found.")
            return []
    except requests.RequestException as e:
        print(f"Failed to get query endpoints: {e}")
        return []


def extract_ip_port(url):
    """
    从URL中提取IP和端口部分

    :param url: 完整URL
    :return: 返回IP和端口字符串,如果提取失败则返回None
    """
    parts = url.split('/')
    if len(parts) > 2:
        return parts[2]
    return None


def ping_website(session, full_url):
    """
    检查网站的可达性

    :param session: requests.Session 对象
    :param full_url: 完整网站URL
    :return: 网站可达返回True,否则返回False
    """
    try:
        response = session.head(full_url)
        return response.status_code == 200
    except requests.RequestException as e:
        print(f"Request failed: {e}")
        return False


def send_to_wechat(message):
    """
    通过 Server 酱发送消息到微信

    :param message: 要发送的消息文本
    :return: 返回响应的JSON数据,如果发送失败则返回None
    """
    url = f'https://sc.ftqq.com/{SERVER_CHAN_SCKEY}.send'
    data = {
        'text': '查询结果通知',
        'desp': message
    }
    try:
        response = requests.post(url, data=data)
        response.raise_for_status()
        return response.json()
    except requests.RequestException as e:
        print(f"Failed to send message: {e}")
        return None


def perform_query(session, headers, cookies, query_url, idNumber, ticketNumber, captcha_url):
    """
    执行具体的查询任务

    :param session: requests.Session 对象
    :param headers: 请求头信息
    :param cookies: 请求时带的Cookie
    :param query_url: 查询接口URL
    :param idNumber: 身份证号
    :param ticketNumber: 准考证号
    :param captcha_url: 验证码URL
    :return: 返回查询结果的JSON数据,如果查询失败则返回None
    """
    randCode = get_Kaptcha(session, headers, cookies, captcha_url)
    if randCode is None:
        print("Failed to get CAPTCHA code.")
        return None
    query_full_url = f'{query_url}?idNumber={idNumber}&ticketNumber={ticketNumber}&randCode={randCode}'
    try:
        response = session.get(query_full_url, headers=headers, cookies=cookies)
        response.raise_for_status()
        return response.json()
    except requests.RequestException as e:
        print(f"Query failed: {e}")
        return None


terminate = False  # 终止程序标志


def job():
    """
    每分钟执行一次的任务,用于进行查询并处理结果

    :return: 无,但当查询成功并发送消息后,会返回return schedule.CancelJob取消定时任务
    """
    global terminate

    for full_url in url_cycle:
        ip_port = extract_ip_port(full_url)
        if ip_port:
            base_query_url = base_query_url_template.format(ip_port)
            captcha_url = base_captcha_url_template.format(ip_port)
            if ping_website(session, full_url):
                print(f"{full_url} is reachable")
                # 动态设置 headers 中的 Referer 和 Host
                headers["Referer"] = f"http://{ip_port}/n_score/"
                headers["Host"] = ip_port
                result = perform_query(session, headers, cookies, base_query_url, idNumber, ticketNumber, captcha_url)

                if result:
                    print("Query success:", result)
                    score_text = json.dumps(result, ensure_ascii=False, indent=4)

                    json_path = 'result.json'
                    with open(json_path, 'w') as json_file:
                        json.dump(result, json_file, ensure_ascii=False, indent=4)

                    response = send_to_wechat(score_text)

                    if response and response.get('message') == '':
                        print("信息发送成功")
                        terminate = True
                        return schedule.CancelJob  # 成功后返回取消任务
                    else:
                        print("发送信息失败!")
                else:
                    print("查询失败")

                break  # 无论成功还是失败,都跳出循环

            else:
                print(f"{full_url} 暂时不能访问!")
        else:
            print(f"IP、端口错误 {full_url}")


# 初始化配置
session = requests.Session()
headers = {
    "Accept": "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8",
    "Accept-Encoding": "gzip, deflate",
    "Accept-Language": "zh-CN,zh;q=0.9",
    "Cache-Control": "no-cache",
    "Pragma": "no-cache",
    "Proxy-Connection": "keep-alive",
    "Referer": "http://xxx.xxx.xxx.xxx:81/n_score/",  # 此处应与查询的IP和端口一致
    "Host": "xxx.xxx.xxx.xxx:81",  # 此处应与查询的IP和端口一致
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"
}
cookies = {
    "JSESSIONID": "FB32CDA83DF30554A9C01A2BE77260B6",  # 实际使用时请替换
    "Hm_lvt_9b3b578b0b797c6004bfd68445de9e88": "1687654663"  # 时间戳可以不要
}

base_url = "https://xxx.xxx.xxx.xxx/cx/"  # 查询入口主页
idNumber = "your_id_number"  # 替换为实际身份证号
ticketNumber = "your_ticket_number"  # 替换为实际准考证号
base_query_url_template = 'http://{}/n_score/rest/api/queryscore/snquery'  # 查询接口模板
base_captcha_url_template = 'http://{}/n_score/Kaptcha.do'  # 生成验证码接口模板

# 获取查询接口列表
href_list = get_query_endpoints(session, base_url)
url_cycle = itertools.cycle(href_list)  # 将查询接口用于循环迭代

# 使用 schedule 模块每分钟执行一次 job
schedule.every(1).minutes.do(job)

while not terminate:
    schedule.run_pending()
    time.sleep(1)

print("程序结束")

几点说明:

1、在Server酱中传送json或text

# 假定得到的结果为字典
text = {"querytime": "查询时间:2022-09-22 12:46:47", "totalScore": " 433"}
# 将结果转为json
json_text = json.dumps(text, ensure_ascii=False)
data = {
    'title': '查询结果通知',
    'desp': json_text
}
headers = {'Content-Type': 'application/json'} # 在头中设置Content-Type 为 json
response = requests.post("https://sctapi.ftqq.com/你自己的key.send?title={}", data=data, headers=headers)

#############################################################################################
# 如果结果为table, 可将table转为列表,再转成文格传送
# 提取表格数据
table_data = []
for table in soup.find_all('table'):
    for row in table.find_all('tr'):
        cols = row.find_all(['td'])
        cols = [ele.text.strip() for ele in cols]
        table_data.append(cols)
        
# 将列表中的元素都转换为字符串类型
str_list = [str(item) for item in table_data]
# 使用 join() 方法将列表转换为文本
text = ', '.join(str_list)

data = {
    'title': '查询结果通知',
    'desp': text
}
response = requests.post("https://sctapi.ftqq.com/你自己的key.send?title={}", data=data)

2、为了确保在查询分数网页和获取验证码时使用同一个会话,我们需要确保所有与网络交互的请求都通过同一个requests.Session。

3、可以将程序部署到阿里云使用FC功能 ,在高考查分前一天触发,让其自动运行直到查询到结果。

       

        此程序是基于我去年成功运行的版本进行修改的。目前,由于今年的查分接口尚未发布,我只能使用去年的接口列表进行开发。因此,当下的接口无法Ping通。基于此限制,我在理论上完成了整个程序,但尚未进行实际测试。

        希望以后高考分数,家长和考生第一时间知晓,自动推送。家长无需费心,考生无需烦恼,无需查询,消息早已送达。此时我无需多言,只愿学子一举夺魁,金榜题名。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1809050.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2024-6-10-Model-Agnostic Meta-Learning (MAML)

摘自&#xff1a;Meta-Transfer Learning for Zero-Shot Super-Resolution 近年来&#xff0c;提出了各种元学习算法。它们可以分为三类&#xff1a; 基于度量的方法&#xff1a;这些方法通过学习度量空间&#xff0c;使得在少量样本内进行高效的学习。例如[35, 38, 39]。基于…

认识和使用 Vite 环境变量配置,优化定制化开发体验

Vite 官方中文文档&#xff1a;https://cn.vitejs.dev/ 环境变量 Vite 内置的环境变量如下&#xff1a; {"MODE": "development", // 应用的运行环境"BASE_URL": "/", // 部署应用时使用的 URL 前缀"PROD": false, //应用…

第1章Hello world 3/5:Cargo.lock:确保构建稳定可靠:运行第一个程序

讲动人的故事,写懂人的代码 1.6 Cargo.lock:确保构建稳定可靠 “看!”席双嘉一边指着屏幕一边说,“终端窗口提示符的颜色,从绿变黄了。这就意味着代码在上次提交后有点变化。” 赵可菲:“但是我们只是运行了程序,代码应该没动呀。” 席双嘉敲了下git status -uall,这…

【研发日记】Matlab/Simulink软件优化(三)——利用NaNFlag为数据处理算法降阶

文章目录 前言 背景介绍 初始算法 优化算法 分析和应用 总结 前言 见《【研发日记】Matlab/Simulink软件优化(一)——动态内存负荷压缩》 见《【研发日记】Matlab/Simulink软件优化(二)——通信负载柔性均衡算法》 背景介绍 在一个嵌入式软件开发项目中&#xff0c;需要开…

kubernetes(k8s)集群部署(2)

目录 k8s集群类型 k8s集群规划&#xff1a; 1.基础环境准备&#xff1a; &#xff08;1&#xff09;保证可以连接外网 &#xff08;2&#xff09;关闭禁用防火墙和selinux &#xff08;3&#xff09;同步阿里云服务器时间&#xff08;达到集群之间时间同步&#xff09; &…

上心师傅的思路分享(三)--Nacos渗透

目录 1. 前言 2. Nacos 2.1 Nacos介绍 2.2 鹰图语法 2.3 fofa语法 2.3 漏洞列表 未授权API接口漏洞 3 环境搭建 3.1 方式一: 3.2 方式二: 3.3 访问方式 4. 工具监测 5. 漏洞复现 5.1 弱口令 5.2 未授权接口 5.3.1 用户信息 API 5.3.2 集群信息 API 5.3.3 配置…

前端开发部署:Visual Studio Code + vue

〇 说明 本教程全部采用默认安装路径&#xff0c;因为在进行自定义路径安装的时候&#xff0c;需要配置各种环境变量&#xff0c;在这个配置过程中&#xff0c;可能出现各种很混乱的问题。 一 安装Node.js 1 下载https://nodejs.org/en 2 按照默认NEXT执行 C:\Program Files…

flutter日历范围选择器

1.传入日期跨度&#xff0c;选择上架日期时&#xff0c;自动显示下架日期 2.手动选择上架日期和下架日期(图中下架日期自动填了只需CalendarDateRangePicker在initState方法中使用_startDate widget.initialStartDate; _endDate widget.initialEndDate;&#xff0c;而不直接…

史上最详细四叉树地图不同技术应用和代码详解

四叉树地图在计算机和机器人领域应用的很广&#xff0c;但是初学者可能会发现四叉树地图有各种不同的实现方式&#xff0c;很多在机器人领域不适用或是在计算机存储领域不适用。今天我就讲解下各类四叉树的实现方式和应用场景。 史上最详细四叉树地图不同技术应用和代码详解 本…

出现 Navicat 和 Cmd 下SQL 版本 | 查询不一致的解决方法

目录 1. 问题所示1.1 查询表格不一致1.2 版本不一致2. 原理分析3. 解决方法1. 问题所示 命令行和数据库使用工具出现不一致的情况,分别有如下情况 1.1 查询表格不一致 使用工具查询当地表格: 使用命令行查询当地表格: 1.2 版本不一致 在cmd命令下mysql --version 查询…

Vue3全局封装dialog弹框

Vue3全局封装modal弹框使用&#xff1a; 应用场景&#xff1a;全局动态form表单弹框 应用Vue3碎片&#xff1a; ref&#xff0c;reactive&#xff0c;app.component&#xff0c;defineExpose&#xff0c;defineProps&#xff0c;defineEmits 应用UI: element-plus dialog form …

【设计模式】结构型设计模式之 适配器模式

介绍 适配器模式&#xff08;Adapter Pattern&#xff09; 是一种结构型设计模式&#xff0c;它的核心目的是使接口不兼容的类能够协同工作。适配器模式通过将一个类的接口转换为客户希望的另一个接口&#xff0c;来解决两个已有接口之间不匹配的问题&#xff0c;从而增加它们…

【Vue】声明式导航-自定义类名(了解)

问题 router-link的两个高亮类名 太长了&#xff0c;我们希望能定制怎么办 解决方案 我们可以在创建路由对象时&#xff0c;额外配置两个配置项即可。 linkActiveClass和linkExactActiveClass const router new VueRouter({routes: [...],linkActiveClass: "类名1&quo…

微信小程序毕业设计-网吧在线选座系统项目开发实战(附源码+论文)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;微信小程序毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计…

Hadoop 2.0:主流开源云架构(一)

目录 一、引例&#xff08;一&#xff09;问题概述&#xff08;二&#xff09;常规解决方案&#xff08;三&#xff09;分布式下的解决方案&#xff08;四&#xff09;小结 自从云计算的概念被提出&#xff0c;不断地有IT厂商推出自己的云计算平台&#xff0c;但它们都是商业性…

LeetCode | 997.找到小镇的法官

这道题拿到后很明显是一个图论的简单出度入度问题&#xff0c;法官的标志就是图中出度为0&#xff0c;入度为n-1的结点&#xff0c;而且根据题目条件&#xff0c;满足这一条件的结点有且只有一个 但是我不知道力扣中关于图论的邻接表和邻接矩阵这些数据结构是需要自己写还是已经…

shell编程(三)—— 控制语句

程序的运行除了顺序运行外&#xff0c;还可以通过控制语句来改变执行顺序。本文介绍bash的控制语句用法。 一、条件语句 Bash 中的条件语句让我们可以决定一个操作是否被执行。结果取决于一个包在[[ ]]里的表达式。 bash中的检测命令由[[]]包起来&#xff0c;用于检测一个条…

论文中eps格式图片制作

在提交论文终稿时&#xff0c;有时需要提交论文中图片的eps格式&#xff0c;这里记录一下eps格式图片制作的过程&#xff0c;方便以后查阅。 论文中eps格式图片制作 PPT绘制的图片转换为eps格式使用代码生成的图片Latex中显示的图片大小跟Ai中设定画板的大小不一致 PPT绘制的图…

品牌策划:不只是工作,是一场创意与学习的旅程

你是否认为只有那些经验丰富、手握无数成功案例的高手才能在品牌策划界崭露头角&#xff1f; 今天&#xff0c;我要悄悄告诉你一个行业内的秘密&#xff1a;在品牌策划的世界里&#xff0c;经验虽重要&#xff0c;但绝非唯一。 1️、无止境的学习欲望 品牌策划&#xff0c;这…

智能投顾:重塑金融理财市场,引领行业新潮流

一、引言 在数字化浪潮的推动下,金融行业正经历着前所未有的变革。其中,智能投顾作为金融科技的重要分支,以其高效、便捷和个性化的服务,逐渐成为金融理财市场的新宠。本文旨在探讨智能投顾如何引领金融理财新潮流,通过丰富的案例及解决方案,展示其独特的魅力和价值。 二…