支持文字直播&聊天室&比赛数据三种模式,bug还是有的,大家看看能不能玩一下。

屏幕截图 2025-04-27 154825.png (296.33 KB, 下载次数: 1)
下载附件
2025-4-27 15:49 上传

屏幕截图 2025-04-27 154844.png (285.15 KB, 下载次数: 0)
下载附件
2025-4-27 15:49 上传

屏幕截图 2025-04-27 154911.png (269.97 KB, 下载次数: 1)
下载附件
2025-4-27 15:49 上传
更新说明:支持斯诺克/网球/F1下拉选项,做了些错误处理检查。
[Python] 纯文本查看 复制代码import tkinter as tk
from tkinter import ttk
import requests
from datetime import datetime
from random import random
from tabulate import tabulate
Living_Matches_Url = 'https://bifen4m.qiumibao.com/json/list.htm'
Match_Max_Sid_Url = 'https://dingshi4pc.qiumibao.com/livetext/data/cache/max_sid/%s/0.htm?time=%s'
Match_Living_Text_Url = 'https://dingshi4pc.qiumibao.com/livetext/data/cache/livetext/%s/0/lit_page_2/%d.htm?get=%s'
Match_Info_Url = 'https://bifen4pc2.qiumibao.com/json/%s/%s.htm?get=%s'
Match_Chat_Count_Url = 'https://dan.zhibo8.cc/data/%s/nba/match%sv_count.htm?rand=%s'
Match_Chat_Text_Url = 'https://dan.zhibo8.cc/data/%s/nba/match%sv_%s.htm?rand=%s'
Match_Score_Rank_Url = 'https://dc4pc.qiumibao.com/dc/matchs/data/%s/score_rank_%s.htm?get=%s'
Match_Player_Url = 'https://dc4pc.qiumibao.com/dc/matchs/data/%s/player_%s.htm?get=%s'
MATCH_TYPES = {'足球': 'football', '篮球': 'basketball', '网球': 'tennis', 'F1': 'f1', '斯诺克': 'snooker'}
class TextLiving:
"""
文字直播类
"""
def __init__(self, match_info, **kwargs):
self.home_team = match_info['home_team']
self.visit_team = match_info['visit_team']
self.period_cn = match_info['period_cn']
self.live_text = kwargs['live_text']
self.home_score = kwargs['home_score']
self.visit_score = kwargs['visit_score']
def __repr__(self):
return f'{self.home_team} {self.home_score} - {self.visit_score} {self.visit_team} {self.period_cn}\n{self.live_text}\n{"*"*60}'
class Match:
"""
比赛类
"""
def __init__(self, **kwargs):
self.id = kwargs['id']
self.home_team = kwargs['home_team']
self.visit_team = kwargs['visit_team']
self.home_score = kwargs['home_score']
self.visit_score = kwargs['visit_score']
self.period_cn = kwargs['period_cn'].replace('\n', ' ')
def __repr__(self):
return f'{self.id} {self.home_team} {self.home_score} - {self.visit_score} {self.visit_team} {self.period_cn}'
class Chat:
"""
聊天室类
"""
def __init__(self, **kwargs):
self.id = kwargs['id']
self.room = kwargs['room']
self.content = kwargs['content']
self.order_id = kwargs['order_id']
self.createtime = kwargs['createtime']
def __repr__(self):
return f'{self.order_id} @ {self.createtime} {self.content}'
def _get_living_list(type='basketball'):
"""
直播比赛列表
"""
response = requests.get(Living_Matches_Url)
if response.status_code == requests.codes.ok:
result = response.json()
return [Match(**match) for match in result['list'] if match['type'] == type and match['period_cn'] != '完赛']
return []
def get_match_max_sid(match_id, last_max_sid=-1):
"""
获取比赛最大sid
"""
response = requests.get(Match_Max_Sid_Url % (match_id, random()))
if response.status_code == requests.codes.ok:
return int(response.text)
return last_max_sid
def get_match_living(match_id, max_sid):
"""
获取比赛直播数据(文字直播数据)
"""
match_info = get_match_info(match_id)
response = requests.get(Match_Living_Text_Url % (match_id, max_sid, random()))
if response.status_code == requests.codes.ok:
result = response.json()
return [TextLiving(match_info, **living) for living in result]
return []
def get_match_chat_page(match_id, latest_page=1):
"""
获取比赛评论最新页码
"""
year = datetime.now().strftime('%Y')
response = requests.get(Match_Chat_Count_Url % (year, match_id, random()))
if response.status_code == requests.codes.ok:
result = response.json()
num = int(result['num'])
per_page = int(result['per_page'])
return num // per_page
return latest_page
def get_match_latest_chat(match_id, latest_page=1):
"""
获取比赛最新聊天内容
"""
year = datetime.now().strftime('%Y')
response = requests.get(Match_Chat_Text_Url % (year, match_id, latest_page, random()))
if response.status_code == requests.codes.ok:
return [Chat(**comment) for comment in response.json()]
return []
def get_player_rank(match_id):
"""
获取两队球员数据之最
"""
today = datetime.now().strftime('%Y-%m-%d')
response = requests.get(Match_Score_Rank_Url % (today, match_id, random()))
if response.status_code == requests.codes.ok:
return response.json()
return {}
def get_player_data(match_id):
"""
获取两队球员数据
"""
today = datetime.now().strftime('%Y-%m-%d')
response = requests.get(Match_Player_Url % (today, match_id, random()))
if response.status_code == requests.codes.ok:
return response.json()
return {}
def get_match_info(match_id, last_match_info=[]):
"""
获取比赛信息
"""
today = datetime.now().strftime('%Y-%m-%d')
response = requests.get(Match_Info_Url % (today, match_id, random()))
if response.status_code == requests.codes.ok:
return response.json()
return last_match_info
def get_living_matches(type='basketball'):
"""
获取直播比赛列表
"""
matches = _get_living_list(type)
for match in matches:
print(match)
return matches
def get_watch_match(matches):
"""
获取用户输入的比赛信息
"""
match_id = input('请输入比赛ID:')
for match in matches:
if match.id == match_id:
return match
else:
print('输入的ID不正确!!!')
return None
def get_type(id):
return MATCH_TYPES.get(id, 'basketball')
class SportsApp:
def __init__(self, root):
self.root = root
self.root.title("体育直播")
self.root.geometry("800x600")
# 顶部控制面板
self.create_controls()
# 底部显示区域
self.create_display_area()
# 比赛uuid
self.sport_uuid = None
def create_controls(self):
# 顶部框架容器
control_frame = ttk.Frame(self.root, padding=10)
control_frame.pack(fill="x")
# 第一个下拉框:体育项目
self.sport_var = tk.StringVar()
self.sport_cb = ttk.Combobox(
control_frame,
textvariable=self.sport_var,
values=['篮球', '足球', '斯诺克', '网球', 'F1'],
state="readonly"
)
self.sport_cb.pack(side="left", padx=10)
self.sport_cb.bind(">", self.update_matches)
self.sport_cb.config(width=20)
# 第二个下拉框:比赛选择
self.match_var = tk.StringVar()
self.match_cb = ttk.Combobox(
control_frame,
textvariable=self.match_var,
state="readonly"
)
self.match_cb.pack(side="left", padx=10)
self.match_cb.bind(">", self.update_display)
self.match_cb.config(width=40)
# 第三个下拉框:查看模式
self.mode_var = tk.StringVar()
self.mode_cb = ttk.Combobox(
control_frame,
textvariable=self.mode_var,
values=["文字直播", "聊天室", "比赛数据"],
state="readonly"
)
self.mode_cb.pack(side="left", padx=5)
self.mode_cb.bind(">", self.update_display)
def create_display_area(self):
# 主显示区域框架
display_frame = ttk.Frame(self.root)
display_frame.pack(fill="both", expand=True, padx=10, pady=10)
# 滚动条
scrollbar = ttk.Scrollbar(display_frame)
scrollbar.pack(side="right", fill="y")
# 文本框
self.display_text = tk.Text(
display_frame,
wrap="word",
yscrollcommand=scrollbar.set,
font=("微软雅黑", 12),
padx=10,
pady=10
)
self.display_text.pack(fill="both", expand=True)
scrollbar.config(command=self.display_text.yview)
def update_matches(self, event=None):
# 更新比赛列表
selected_sport = self.sport_var.get()
if selected_sport:
matches = get_living_matches(get_type(selected_sport))
self.match_cb["values"] = [str(m) for m in matches] if len(matches) > 0 else ['暂无比赛']
self.match_cb.current(0)
self.update_display()
def update_display(self, event=None):
# 获取当前选择
sport = self.sport_var.get()
match = self.match_var.get()
mode = self.mode_var.get()
if not all([sport, match, mode]):
return
# 清空文本框
if self.sport_uuid != f'{sport}_{match}_{mode}':
self.display_text.delete(1.0, tk.END)
self.sport_uuid = f'{sport}_{match}_{mode}'
match_id = match.split(' ')[0]
if not match_id.isdigit(): return
if mode == '聊天室':
self.chat_live(match_id)
elif mode == '文字直播':
self.text_live(match_id)
elif mode == '比赛数据':
if sport == '篮球':
self.stat_basketball_live(match_id)
else:
self.display_text.delete(1.0, tk.END)
self.display_text.insert(tk.END, "暂不支持该模式\n")
self.root.after(2000, self.update_display) # 每秒更新
def text_live(self, match_id):
"""
文字直播
"""
current_match_max_sid = -1
match_max_sid = get_match_max_sid(match_id, current_match_max_sid)
if current_match_max_sid == match_max_sid: return
current_match_max_sid = match_max_sid
text_livings = get_match_living(match_id, current_match_max_sid)
text = ''
for text in text_livings:
self.display_text.insert(tk.END, str(text) + '\n')
if '完赛' in str(text):
self.display_text.insert(tk.END, '\n\n**********比赛结束**********')
def chat_live(self, match_id):
"""
聊天室
"""
current_page = 1
latest_page = get_match_chat_page(match_id, current_page)
if current_page == latest_page: return
current_page = latest_page
comments = get_match_latest_chat(match_id, current_page)
for comment in comments:
self.display_text.insert(tk.END, str(comment) + '\n')
def stat_basketball_live(self, match_id):
"""
比赛数据
"""
data_1 = get_player_data(match_id)
data_2 = get_player_rank(match_id)
host_1 = data_1.get('data', {}).get('host')
guest_1 = data_1.get('data', {}).get('guest')
host_2 = data_2.get('data', {}).get('host')
guest_2 = data_2.get('data', {}).get('guest')
if host_1 is None:
self.display_text.delete(1.0, tk.END)
self.display_text.insert(tk.END, '暂无数据\n')
return
self.display_text.delete(1.0, tk.END)
headers_1 = ['球员', '位置', '出场时间', '投篮', '三分', '罚球', '总篮板', '助攻', '抢断', '盖帽', '失误', '犯规', '得分']
self.display_text.insert(tk.END, f'**********客队: {host_1["team_name_cn"]}**********\n')
table_host_1 = []
for p in host_1['on']:
table_host_1.append([p['player_name_cn'], p['pos'], p['minutes'], p['field'], p['three'], p['free'], p['off+def'], p['ass'], p['ste'], p['blo'], p['turn'], p['fouls'], p['points']])
table_host_1.append(['总计', '-', '-', host_1['total']['field'], host_1['total']['three'], host_1['total']['free'], host_1['total']['off+def'], host_1['total']['ass'], host_1['total']['ste'], host_1['total']['blo'], host_1['total']['turn'], host_1['total']['fouls'], host_1['total']['points']])
self.display_text.insert(tk.END, tabulate(table_host_1, headers_1, tablefmt='grid') + '\n')
self.display_text.insert(tk.END, f'\n\n**********主队: {guest_1["team_name_cn"]}**********\n')
table_guest_1 = []
for p in guest_1['on']:
table_guest_1.append([p['player_name_cn'], p['pos'], p['minutes'], p['field'], p['three'], p['free'], p['off+def'], p['ass'], p['ste'], p['blo'], p['turn'], p['fouls'], p['points']])
table_guest_1.append(['总计', '-', '-', guest_1['total']['field'], guest_1['total']['three'], guest_1['total']['free'], guest_1['total']['off+def'], guest_1['total']['ass'], guest_1['total']['ste'], guest_1['total']['blo'], guest_1['total']['turn'], guest_1['total']['fouls'], guest_1['total']['points']])
self.display_text.insert(tk.END, tabulate(table_guest_1, headers_1, tablefmt='grid') + '\n')
if host_2 is None: return
headers_2 = ['最高', host_2['team_info']['team_name'], '数值', guest_2['team_info']['team_name'], '数值']
self.display_text.insert(tk.END, '\n\n**********两队技术统计之最**********\n')
table_2 = []
table_2.append([host_2['points']['name'], host_2['points']['player_name'], host_2['points']['points'], guest_2['points']['player_name'], guest_2['points']['points']])
table_2.append([host_2['off+def']['name'], host_2['off+def']['player_name'], host_2['off+def']['points'], guest_2['off+def']['player_name'], guest_2['off+def']['points']])
table_2.append([host_2['ass']['name'], host_2['ass']['player_name'], host_2['ass']['points'], guest_2['ass']['player_name'], guest_2['ass']['points']])
table_2.append([host_2['ste']['name'], host_2['ste']['player_name'], host_2['ste']['points'], guest_2['ste']['player_name'], guest_2['ste']['points']])
table_2.append([host_2['blo']['name'], host_2['blo']['player_name'], host_2['blo']['points'], guest_2['blo']['player_name'], guest_2['blo']['points']])
table_2.append([host_2['minutes']['name'], host_2['minutes']['player_name'], host_2['minutes']['points'], guest_2['minutes']['player_name'], guest_2['minutes']['points']])
table_2.append([host_2['turn']['name'], host_2['turn']['player_name'], host_2['turn']['points'], guest_2['turn']['player_name'], guest_2['turn']['points']])
table_2.append([host_2['per_fouls']['name'], host_2['per_fouls']['player_name'], host_2['per_fouls']['points'], guest_2['per_fouls']['player_name'], guest_2['per_fouls']['points']])
self.display_text.insert(tk.END, tabulate(table_2, headers_2, tablefmt='grid') + '\n')
if __name__ == "__main__":
root = tk.Tk()
app = SportsApp(root)
root.mainloop()