(机选彩票号码+爬取最新开奖号码 | 2021-04-21)
因为是自学状态,没有大佬带路, 想知道写的这些代码有木有不规范的,或者说是不应该这么写等等,一开始就想发在 V2 上,但怕丢人....今天想了想还是发出来, 毕竟大佬们的批评才能让我进步么
这个程序作用是<机选三种彩票类型的号码>
程序内包含功能有如下:
- 自动获取最新的三种彩票的开奖号码
- 随机生成三种彩票类型的号码
- 注册
- 登录
- 密码加密
- 数据写入文件
- 文件中提取数据
- 时间模块判断早中晚
先上个演示 | 再附上打包后的程序 exe | 再贴上完整源代码
截至 2021-04-20 的最新彩票开奖信息和程序获取的一致,如图:
演示程序下载>>>
点击下载提取密码:cisj
import re as m_re
import os as m_os
import time as m_time
import random as m_random
import easygui as m_easygui
import requests as m_requests
# 取开奖号码
class HtmlInfo():
def __init__(self):
self.browser_header = {
"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36"
"(KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"}
#福利彩票
self.ddd_nums = {}# 福彩 3D
self.ssq_nums = {}# 双色球
self.qlc_nums = {}# 七乐彩
self.urls = [
r'http://kaijiang.500.com/sd.shtml',
r'http://kaijiang.500.com/ssq.shtml',
r'http://kaijiang.500.com/qlc.shtml'
]
self.html_lable = [
r'<span class="span_right">(.*?)</span>',# 开奖日期-0
r'<strong>(.*?)</strong>',# 开奖期号-1
r'<li class="ball_orange">(.*?)</li>',# 福彩 3D-2
r'<li class="ball_red">(.*?)</li>',# 双色球-红-3
r'<li class="ball_blue">(.*?)</li>',# 双色球-蓝-4
r'<li class="ball_red">(.*?)</li>',# 七乐彩-红-5
r'<li class="ball_blue">(.*?)</li>'# 七乐彩-蓝-6
]
def get_HtmlInfo(self, url):
try:
html = m_requests.get(url, headers=self.browser_header)
html.encoding = 'gbk'# 一下午试图想在正则中解决它...cnm,耽误不少时间
except TypeError:
print('网络中断或其他原因!')
return# 直接中断程序, 函数内用 return | 函数外用 exit()
# 取最新的日期
# lottery_date = m_re.findall('<td align="center">(.*?)</td>', html.text, m_re.S)
# 取最新的开奖号码
# for loop_urls in range(len(self.urls)):
if self.urls[0] == url:
# 福彩 3D: 开奖日期, 开奖期号, 开奖号码, 写入字典
self.lottery_date = m_re.findall(self.html_lable[0], html.text, m_re.S)[0]
lottery_date_nums = m_re.findall(self.html_lable[1], html.text, m_re.S)[1]
lottery_nums = m_re.findall(self.html_lable[2], html.text, m_re.S)
# 数据进字典
self.ddd_nums[lottery_date_nums] = lottery_nums
# print(self.ddd_nums)
if self.urls[1] == url:
# 双色球: 号码列表-合并期号, 开奖日期, 开奖期号, 开奖号码 红六个 / 蓝一个, 红蓝号码合并列表
lottery_nums = []
lottery_date = m_re.findall(self.html_lable[0], html.text, m_re.S)[0]
lottery_date_nums = m_re.findall(self.html_lable[1], html.text, m_re.S)[1]
lottery_nums_red = m_re.findall(self.html_lable[3], html.text, m_re.S)[:6]
lottery_nums_blue = m_re.findall(self.html_lable[4], html.text, m_re.S)[:1]
# 下面用 extend 插了两次是因为我想把红蓝球变成一个列表, 如果弄成只插一次就会造成列表套列表,很是疑惑
lottery_nums.extend(lottery_nums_red)
lottery_nums.extend(lottery_nums_blue)
# 数据进字典
self.ssq_nums[lottery_date_nums] = lottery_nums
# print(self.ssq_nums)
if self.urls[2] == url:
# 七乐彩: 号码列表-合并期号, 开奖日期, 开奖期号, 开奖号码 红七个 / 蓝一个, 红蓝号码合并列表
lottery_nums = []
lottery_date = m_re.findall(self.html_lable[0], html.text, m_re.S)[0]
lottery_date_nums = m_re.findall(self.html_lable[1], html.text, m_re.S)[1]
lottery_nums_red = m_re.findall(self.html_lable[5], html.text, m_re.S)[:7]
lottery_nums_blue = m_re.findall(self.html_lable[6], html.text, m_re.S)[:1]
lottery_nums.extend(lottery_nums_red)
lottery_nums.extend(lottery_nums_blue)
# 数据进字典
self.qlc_nums[lottery_date_nums] = lottery_nums
# print(self.qlc_nums)
# 对密码加密-解密, 上面代码很长 总感觉可以再精简优化下, 目前对于类的使用不是很熟练
class AppCryptos():
def __init__(self):
self.token_list = [
'0000000000_'
'bC0>aB3?aA0!aG1%hI1$'
'eK6)dA8&qB4@lC0$bA0&aI0eP1#cC0$'
'!@#$%^&*()_+aF0!bG2!cE4_eC1^bE0^aE4@bC0{nB'
]
self.enctry_str = ''# 加密数据
self.dectry_str = ''# 解密数据
# 加密
def enctry_AppCryptos(self, key):# key 为传入的密码
for i,j in zip(key, str(self.token_list)):
temp_ij = str(ord(i) + ord(j)) + str(self.token_list[0][:15])
self.enctry_str += temp_ij
# print(self.enctry_str)
return self.enctry_str
# 解密
def dectry_AppCryptos(self, key):# key 为传入的密文
for i,j in zip(key.split(str(self.token_list[0][:15]))[:-1], str(self.token_list)):
temp_ij = chr(int(i) - ord(j))
self.dectry_str += temp_ij
# print(self.dectry_str)
return self.dectry_str
# EasyGui
class EasyGuiUI():
def __init__(self):
self.app_config = [
r'AppConfig.ini',# 文件可自定义目录存放,也可以跟主程序走
r'MainConfig.ini',# 文件跟着主程序走
r'config_path=',# 用户自己选择的配置文件目录路径
r'configfile_path=',# 用户自己选择的配置文件目录路径 + AppConfig.ini 路径
r'username=',# 用户昵称
r'userkey='# 加密的程序密码的密文
]
# 取三个数为一注
self.loop_ddd = (0, 9)
# 取六个红球,一个蓝球为一注
self.loop_ssq_red = (1, 33)
self.loop_ssq_blue = (1, 16)
# 七个基本号, 一个特别号为一注
self.loop_qlc = (1, 30)
self.box_msg = [
'欢迎进入彩票号码生成器!\n 是否进行程序初始化?\n 自定义配置文件目录或程序默认目录',
'选择一个文件夹!',
'欢迎使用机选彩票号码程序, 祝您早日中大奖!',
'请输入程序密码',
'注册成功!',
'\n\n\n 点击下方[查看最新开奖号码]按钮以获取开奖号码'
]
self.box_title = [
'程序初始化',
'激活程序',
'登录验证',
'机选号码'
]
self.indexbox_choices = [
['0', '确定'],# 用来判断 choices_msg 按钮返回值
['1', '默认'],
['2', '机选号码'],
['3', '查看最新开奖号码'],
['4', '退出程序']
]
self.indexbox_errmsg = [
'未知错误!',
'文件不存在',
'',
'',
'主程序文件失效,请重新运行程序!'
]
self.multpasswordbox_fields = [
'*昵称',
'*程序密码'
]
# 如果函数中已经有相同变量,应该是优先读函数中的, 有点忘记了...
# 想起来一个, 相同方法下的变量互相调用之前,得先调用被调用的那个方法
self.temp_enctry_str = ''# 临时存放加密的密文,写入到文件 MainConfig.ini. 如果函数中已经有相同变量,应该是优先读函数中的
self.temp_ext_userkey = []# 临时存放提取出来的密文.
self.temp_ext_username = []# 临时存放提取出来的昵称
# 运行程序 初始化界面 | 用户选择目录或者程序直接走默认目录
def init_EasyGuiUI(self):
try:
with open(self.app_config[1], encoding='utf-8') as init_f1:
init_f1.close()# 走个过程就直接关掉, 不占用
EasyGuiUI().reg_EasyGuiUI()
except OSError:
# 初始界面, 因为检查不到文件 MainConfig.ini, 所以初始化一下 目的是生成 AppConfig.ini 和 MainConfig.ini 两个文件
# 原本还想弄一个判断有无文件 AppConfig.ini, 因为这个文件是可以让用户自定义目录去生成的,
# 如果作这个判断就把文件 AppConfig.ini 的目录路径也写到文件 MainConfig.ini 中, 想了下还是没写这个, 只验证有无文件 MainConfig.ini
choices_msg = m_easygui.indexbox(
self.box_msg[0],
self.box_title[0],
choices=(self.indexbox_choices[0][1],self.indexbox_choices[1][1])
)
# 判断用户是选择的[确定]按钮, 开始调用 diropenbox() 函数选择目录
if choices_msg == int(self.indexbox_choices[0][0]):
choices_path = m_easygui.diropenbox(self.box_msg[1])
# 这里会先生成文件 MainConfig.ini, 避免与下方的 m_os.chdir() 函数冲突
with open(self.app_config[1], 'w', encoding='utf-8') as user_f1:
user_f1.write(
self.app_config[2] + m_os.getcwd() + ';' + '\n' +
self.app_config[3] + m_os.getcwd() + '\\' + self.app_config[1] + ';'
)
# 选择目录窗口如果直接关闭在这里做个 TypeError 处理
try:
m_os.chdir(choices_path)
except TypeError:
return
# 合并一下选择的目录跟文件名 AppConfig.ini,得到一个完整路径
merge_path = m_os.path.join(m_os.getcwd(), self.app_config[0])
# 根据用户选择的目录直接覆写文件 AppConfig.ini
with open(merge_path, 'w', encoding='utf-8') as user_f2:
user_f2.write(
self.app_config[2] + choices_path + ';' + '\n' +
self.app_config[3] + merge_path + ';'
)
EasyGuiUI().reg_EasyGuiUI()
# 判断用户是选择的[默认]按钮, 直接在程序目录下进行覆写两个文件 MainConfig.ini AppConfig.ini
elif choices_msg == int(self.indexbox_choices[1][0]):
# 选择[默认] 直接在程序所在目录生成文件
with open(self.app_config[0], 'w', encoding='utf-8') as app_f1,\
open(self.app_config[1], 'w', encoding='utf-8') as app_f2:
app_f1.write(
self.app_config[2] + m_os.getcwd() + ';' + '\n' +
self.app_config[3] + m_os.getcwd() + '\\' + self.app_config[0] + ';'
)
app_f2.write(
self.app_config[2] + m_os.getcwd() + ';' + '\n' +
self.app_config[3] + m_os.getcwd() + '\\' + self.app_config[1] + ';'
)
EasyGuiUI().reg_EasyGuiUI()
# 注册
def reg_EasyGuiUI(self):
# 在启用注册 UI 之前 判断下是否已经进行了注册, 判断文件 MainConfig.ini 中有无 self.app_config[4]/[5]这两个字符串
# 这种判断应该还有更好的写法, 截至学到目前的知识 确实想不出了
with open(self.app_config[1], 'r', encoding='utf-8') as read_f1:
#在这个变量出来前, 原地转圈琢磨了好久. read()这个好像并不能用在 if 语句的多重判断中,期望的结果会 False
read_f1_temp = read_f1.read()
if self.app_config[4] in read_f1_temp and self.app_config[5] in read_f1_temp:
return EasyGuiUI().login_EasyGuiUI()
# 这下面是原地转圈时琢磨的
# if (self.app_config[4] and self.app_config[5]) in read_f1:
# return EasyGuiUI().login_EasyGuiUI()
# aa = m_re.search('username=*|userkey=*', i)
# if aa:
# print(aa.group())
# print(i)
# if [read_f1_temp1.strip().find('username=') for read_f1_temp1 in read_f1]:
# print('11')
# with open(self.app_config[1], 'r+', encoding='utf-8') as read_f2:
# if [read_f2_temp1.strip().find(self.app_config[5]) for read_f2_temp1 in read_f2.readlines()]:
# return EasyGuiUI().login_EasyGuiUI()
reg_values = []
reg_values = m_easygui.multpasswordbox(
self.box_msg[2],
self.box_title[1],
fields=(self.multpasswordbox_fields[0],self.multpasswordbox_fields[1])
)
while True:
# 循环后 重置 self.indexbox_errmsg[2]的消息为空字符串
self.indexbox_errmsg[2] = self.indexbox_errmsg[3]
# 返回值为 None,结束
if reg_values == None:
return
# 这块还想写一个昵称只能输入纯英文, 程序密码只能输入纯数字
for i in range(len(self.multpasswordbox_fields)):
if reg_values[i] == '' and self.multpasswordbox_fields[i][0] == '*' and self.multpasswordbox_fields[i][0] == '*':
self.indexbox_errmsg[2] += (' [%s ] 为必填项,请重新填写!\n' % self.multpasswordbox_fields[i])
if self.indexbox_errmsg[2] == '':
break
reg_values = m_easygui.multpasswordbox(
self.indexbox_errmsg[2],
self.box_title[1],
fields=(self.multpasswordbox_fields[0],self.multpasswordbox_fields[1]),
values=reg_values# 这里是留存填写过的内容,并显示在程序,避免用户二次输入
)
# 联动 enctry_AppCryptos() 加密函数, 对用户输入的<程序密码>进行加密
link_AppCryptos = AppCryptos()
link_AppCryptos.enctry_AppCryptos(reg_values[1])
self.temp_enctry_str = link_AppCryptos.enctry_str
# 将昵称和加密的程序密码存入文件 MainConfig.ini
# 这块应该还要做个文件中是否存在 username 和 userkey 的字符串做对应的写入判断(如果字符串存在多个,或者没有,或者文件不在,或者权限不够等)
# 不过这次就简单写一下数据存入文件, 其他的心里写了就行
try:
with open(self.app_config[1], 'a', encoding='utf-8') as enctry_f1:
enctry_f1.write(
'\n' + self.app_config[4] + reg_values[0] + ';' +
'\n' + self.app_config[5] + self.temp_enctry_str + ';'
)
m_easygui.msgbox(self.box_msg[4])
EasyGuiUI().login_EasyGuiUI()
except OSError:
m_easygui.msgbox(self.indexbox_errmsg[4])
# print(self.enctry_str)
# 登录
def login_EasyGuiUI(self):
# 登录这块应该也要写一个取文件中密文的时候来个文件是否存在或文件中需要的字符串是否存在, 心里写了!
# 想了下 验证文件这些步骤其实可以单独弄个函数放在那边来调用, 这样代码应该可以精简一些
# 在 open 的时候用'x'模式 检查文件是否存在?
login_return_cont = m_easygui.passwordbox(self.box_msg[3], self.box_title[2])
# 读取文件中 userkey 存放的密文
with open(self.app_config[1], encoding='utf-8') as dectry_f1:
# for read_line in dectry_f1:
ext_userkey = m_re.findall(r'userkey=(.*?);', dectry_f1.read(), m_re.S)
# print(ext_userkey)
# 联动 enctry_AppCryptos(),进行一次密码加密,然后验证加密好的密文是否存在文件内
link_AppCryptos = AppCryptos()
link_AppCryptos.dectry_AppCryptos(ext_userkey[0])
if login_return_cont == link_AppCryptos.dectry_str:
EasyGuiUI().panel_EasyGuiUI()
elif login_return_cont == None:
return
else:
return EasyGuiUI().login_EasyGuiUI()
# 登录成功后调用的, 获取最新开奖号码给面板显示
# 此方法被 panel_EasyGuiUI(self) 方法来调用
def get_nums_ForPanel(self):
# 获取最新中奖号码, 为了给面板显示
link_HtmlInfo = HtmlInfo()
for loop_urls in range(len(link_HtmlInfo.urls)):
link_HtmlInfo.get_HtmlInfo(link_HtmlInfo.urls[loop_urls])
dict_ddd = link_HtmlInfo.ddd_nums
dict_ssq = link_HtmlInfo.ssq_nums
dict_qlc = link_HtmlInfo.qlc_nums
dict_ddd_value = ','.join(list(dict_ddd.values())[0])
dict_ssq_value = ','.join(list(dict_ssq.values())[0])
dict_qlc_value = ','.join(list(dict_qlc.values())[0])
self.marge_msgs_get_nums_ForPanel = (
# ext_username[0] + self.box_msg[2] + '\n' +
'第[' + list(dict_ddd)[0] + ']期福彩 3D 开奖号码: ' + dict_ddd_value + '\n' +
'第[ ' + list(dict_ssq)[0] + ' ]期双色球开奖号码: ' + dict_ssq_value + '\n' +
'第[ ' + list(dict_qlc)[0] + ' ]期七乐彩开奖号码: ' + dict_qlc_value
)
# 机选号码, 机选的规则都为:彩票投注中的单式投注!
# 此方法被 panel_EasyGuiUI(self) 方法来调用
def get_loopnums_ForPanel(self, keyloopnums):
if keyloopnums == 'start':
# 机选福彩 3D
# 使用列表推导式 把需要的每一个元素转换成字符串,这样做的方式是方便呈现的时候 数字后面自带逗号
# loopnums_1_ddd = ''
loopnums_1_ddd = [str(m_random.randint(self.loop_ddd[0],self.loop_ddd[1])) for loop in range(3)]
loopnums_1_ddd = ','.join(loopnums_1_ddd)# # 三个数字转换成字符串且用逗号来分割进行展示
# print(loopnums_1_ddd)# 输出一下 机选出来的三个数字
# 机选双色球
# loopnums_2_ssq = []
loopnums_2_red = [str(m_random.randint(self.loop_ssq_red[0],self.loop_ssq_red[1])) for loop in range(6)]
loopnums_2_blue = [str(m_random.randint(self.loop_ssq_blue[0],self.loop_ssq_blue[1])) for loop in range(1)]
loopnums_2_red.append(loopnums_2_blue[0])# 蓝球合并到红球的列表
loopnums_2_ssq = ','.join(loopnums_2_red)# 定一个新的变量名称 | 七个数字转换成字符串且用逗号来分割进行展示
# print(loopnums_2_ssq)# 输出一下 机选出来且合并后的 红+蓝七个数字
# 机选七乐彩
loopnums_3_red = [str(m_random.randint(self.loop_qlc[0],self.loop_qlc[1])) for loop in range(7)]
loopnums_3_blue = [str(m_random.randint(self.loop_qlc[0],self.loop_qlc[1])) for loop in range(1)]
loopnums_3_red.append(loopnums_3_blue[0])
loopnums_3_qlc = ','.join(loopnums_3_red)
# print(loopnums_3_qlc)
# 三个彩种机选的号码合并掉,给 panel_EasyGuiUI() 函数中的 indexbox() 调用
self.marge_msgs_get_loopnums_ForPanel = (
'机选的福彩 3D 号码: ' + loopnums_1_ddd + '\n' +
'机选的双色球号码: ' + loopnums_2_ssq + '\n' +
'机选的七乐彩号码: ' + loopnums_3_qlc + '\n' +
'截屏或拍照后去彩票站购买, 祝你鸿运当头!'
)
else:
pass
# 登录成功后 显示获取的最新彩票号码 | 按钮形式机选号码并以 msgbox() 函数显示给用户 | over!
def panel_EasyGuiUI(self):
self.get_nums_ForPanel()
# self.get_loopnums_ForPanel()
# 读取文件中 username 存放的昵称
with open(self.app_config[1], encoding='utf-8') as read_f1:
ext_username = m_re.findall(r'username=(.*?);', read_f1.read(), m_re.S)
# 加一个时间判断
# 06:00~10:00>早上 10:00~12:00>中午 12:00~18:00>下午 18:00~06:00>晚上
get_time = m_time.localtime()
# print(get_time.tm_hour)
if 0 <= get_time.tm_hour <= 4:
indexbox_msg_time = '凌晨时段,注意休息: '
elif 5 <= get_time.tm_hour <= 10:
indexbox_msg_time = '早上好: '
elif 11 <= get_time.tm_hour <= 12:
indexbox_msg_time = '中午好: '
elif 13 <= get_time.tm_hour <= 18:
indexbox_msg_time = '下午好: '
elif 19 <= get_time.tm_hour <= 23:
indexbox_msg_time = '晚上好: '
else:
indexbox_msg_time = '奈何桥见: '
choices_msg = m_easygui.indexbox(
msg=(indexbox_msg_time + ext_username[0] + self.box_msg[5]),
title=self.box_title[3],
choices=(self.indexbox_choices[2][1],self.indexbox_choices[3][1],self.indexbox_choices[4][1])
)
while True:
if choices_msg == 0:
self.get_loopnums_ForPanel('start')# 写这里是因为要每次点击都获取新的随机数
# 按钮返回 0 则去机选随机号码展示给用户
choices_msg = m_easygui.indexbox(
msg=self.marge_msgs_get_loopnums_ForPanel,
title=self.box_title[3],
choices=(self.indexbox_choices[2][1],self.indexbox_choices[3][1],self.indexbox_choices[4][1])
)
elif choices_msg == 1:
# 按钮返回 1 则去获取最新号码展示给用户
choices_msg = m_easygui.indexbox(
msg=self.marge_msgs_get_nums_ForPanel,
title=self.box_title[3],
choices=(self.indexbox_choices[2][1],self.indexbox_choices[3][1],self.indexbox_choices[4][1])
)
elif choices_msg == 2:
# 直接结束
break
else:
# 这里比如直接关闭 返回了 None
break
app_run = EasyGuiUI()
app_run.init_EasyGuiUI()
# app_run.get_loopnums_ForPanel()
1
xwayway 2021-06-11 13:52:19 +08:00
不懂就问,我不是写 py 的,所以不清楚各位 pyer 的规范。但是我感觉一个 function 为啥又有下划线,又有驼峰。看起来感觉很难受
|
2
Macv1994 2021-06-11 14:03:37 +08:00
方法命名好像不规范吧...
|
5
Macv1994 2021-06-11 14:20:55 +08:00
@TcDhl https://www.python.org/dev/peps/pep-0008/ PEP8 可以参考这个连接
|
6
Macv1994 2021-06-11 14:22:18 +08:00
![1623392526199.png]( https://7.dusays.com/2021/06/11/15061b2c4db5c.png)
|
7
nullboy 2021-06-11 14:34:48 +08:00
一个本地脚本程序,为啥为有注册登录的功能
|
10
IgniteWhite 2021-06-11 16:56:03 +08:00
第一次写就这么厉害!
@xwayway 按照 https://google.github.io/styleguide/pyguide.html#316-naming ( PEP8 比 Google style guide 多说了一个 TypeVar 的命名,比较特殊 https://www.python.org/dev/peps/pep-0008/#id42 ): 除了 ClassName 是驼峰,全局常量是 GLOBAL_CONSTANT_NAME,其他都是 lower_case_separated_by_underscore |
12
bytesfold 2021-06-11 17:21:49 +08:00
方法看了下用了不少特性,但是看起来很冗余
|
13
destinism 2021-06-11 17:57:24 +08:00
厉害啊
|
14
TcDhl OP @IgniteWhite 谢谢大佬 #10
|
17
freakxx 2021-06-13 06:12:59 +08:00
因为是自学状态,没有大佬带路, 想知道写的这些代码有木有不规范的,或者说是不应该这么写等等,一开始就想发在 V2 上,但怕丢人....今天想了想还是发出来, 毕竟大佬们的批评才能让我进步么
如果实话说的话,代码质量有点脏,但对于是自学来说,第一个写的程序,还是很棒的。 如果说代码问题的话,大概可能有这几个方面可以改进 最主要的 - 逻辑 - 规范 - 语法 逻辑上来说,你要写这样的一个东西,可以拆成 获取数据,保存数据,展示数据; 你已经拆成几个类去做是 ok 的,但最后调用是有些怪的, 不如写一个大类,然后再引用对应的模块; 如 app = Lottery() app.run_ui() 调用之间分离,入口出口收窄; 规范的话,就楼上说的,PEP8 语法的话,就看熟练度了,多看多写就好; 代码质量还有一个问题就是不统一,这个主要是抄(语法)之类,没重新做调整; 也有一个就是,写的过程,为了实现这个放得过重; 可能从怎么实现变为怎么组合,这个角度去思考,代码能够轻灵些; |