如何修复openpyxl中赔率着色异常及赛事匹配丢失问题
博彩赛事赔率处理脚本问题修复
问题说明
现有Python脚本功能:
- 调用子进程运行爬虫,从两个博彩网站抓取赛事及赔率写入xlsx文件
- 使用Fuzzywuzzy匹配赛事名称,将更高赔率标绿、更低赔率标红,按赔率差值排序
当前存在两个问题:
- 赔率着色随机异常
- 不运行子进程的情况下第二次执行代码时,首个赛事丢失
问题根源分析
- 着色异常:原代码先设置单元格格式,再重新写入排序后的数据,导致格式被默认字体覆盖;且百分比差值计算逻辑嵌套在
rate2存在的判断分支中,部分赛事数据未进入排序列表。 - 首个赛事丢失:原代码写入数据时从第1行开始,覆盖了表头;第二次读取时从第2行开始,导致第1行的赛事数据被遗漏。
修复方案及修改后代码
import subprocess from openpyxl import load_workbook from plyer import notification from openpyxl.styles import Font, Color from fuzzywuzzy import fuzz from operator import itemgetter # 可选:运行爬虫脚本,注释此行可跳过爬虫直接处理现有文件 subprocess.run(['python', r'C:\Users\Kryštof\PycharmProjects\pythonProject2\Combined sure bets.py']) # 加载XLSX文件 xlsx_file = r'C:\Users\Kryštof\Desktop\Sure Bets\match_details.xlsx' wb = load_workbook(xlsx_file) ws = wb.active # 存储赛事和赔率的字典 tip_sport_matches = {} betano_matches = {} # 读取数据(从第2行开始,保留第1行表头) for row in ws.iter_rows(min_row=2, max_row=ws.max_row, min_col=1, max_col=7, values_only=True): tip_sport_name = row[0] betano_name = row[4] tip_sport_rate1 = row[1] tip_sport_rate2 = row[2] betano_rate1 = row[5] betano_rate2 = row[6] if tip_sport_name: tip_sport_matches[tip_sport_name] = { 'rate1': tip_sport_rate1, 'rate2': tip_sport_rate2 } if betano_name: betano_matches[betano_name] = { 'rate1': betano_rate1, 'rate2': betano_rate2 } # 清空原有数据行(保留第1行表头) ws.delete_rows(2, ws.max_row) # 匹配赛事并收集数据,不直接写入表格 matched_data = [] unmatched_tipsport = [] unmatched_betano = [] # 匹配赛事 tip_sport_copy = tip_sport_matches.copy() betano_copy = betano_matches.copy() for tip_name, tip_rates in tip_sport_copy.items(): for bet_name, bet_rates in betano_copy.items(): if fuzz.token_set_ratio(tip_name, bet_name) >= 80: # 收集匹配数据,包含赔率差值计算 rate1_ts = float(tip_rates['rate1']) if tip_rates['rate1'] else None rate2_ts = float(tip_rates['rate2']) if tip_rates['rate2'] else None rate1_bet = float(bet_rates['rate1']) if bet_rates['rate1'] else None rate2_bet = float(bet_rates['rate2']) if bet_rates['rate2'] else None # 计算赔率差值总和(仅当两组赔率都存在时) percentage_sum = 0.0 if rate1_ts and rate1_bet: percentage_sum += abs((rate1_ts - rate1_bet)/rate1_ts * 100) if rate2_ts and rate2_bet: percentage_sum += abs((rate2_ts - rate2_bet)/rate2_ts * 100) matched_data.append({ 'percentage_sum': percentage_sum, 'tip_name': tip_name, 'rate1_ts': rate1_ts, 'rate2_ts': rate2_ts, 'bet_name': bet_name, 'rate1_bet': rate1_bet, 'rate2_bet': rate2_bet }) # 从副本中删除已匹配项 del tip_sport_matches[tip_name] del betano_matches[bet_name] break # 避免一个赛事匹配多个对手 # 收集未匹配数据 for tip_name, tip_rates in tip_sport_matches.items(): unmatched_tipsport.append({ 'tip_name': tip_name, 'rate1_ts': float(tip_rates['rate1']) if tip_rates['rate1'] else None, 'rate2_ts': float(tip_rates['rate2']) if tip_rates['rate2'] else None }) for bet_name, bet_rates in betano_matches.items(): unmatched_betano.append({ 'bet_name': bet_name, 'rate1_bet': float(bet_rates['rate1']) if bet_rates['rate1'] else None, 'rate2_bet': float(bet_rates['rate2']) if bet_rates['rate2'] else None }) # 按赔率差值总和降序排序匹配数据 matched_data.sort(key=itemgetter('percentage_sum'), reverse=True) # 写入匹配数据(从第2行开始) current_row = 2 for item in matched_data: ws.cell(row=current_row, column=1).value = item['tip_name'] ws.cell(row=current_row, column=2).value = item['rate1_ts'] ws.cell(row=current_row, column=3).value = item['rate2_ts'] ws.cell(row=current_row, column=4).value = f"{item['percentage_sum']:.2f}%" ws.cell(row=current_row, column=5).value = item['bet_name'] ws.cell(row=current_row, column=6).value = item['rate1_bet'] ws.cell(row=current_row, column=7).value = item['rate2_bet'] current_row += 1 # 写入未匹配TipSport数据 for item in unmatched_tipsport: ws.cell(row=current_row, column=1).value = item['tip_name'] ws.cell(row=current_row, column=2).value = item['rate1_ts'] ws.cell(row=current_row, column=3).value = item['rate2_ts'] current_row += 1 # 写入未匹配Betano数据 for item in unmatched_betano: ws.cell(row=current_row, column=5).value = item['bet_name'] ws.cell(row=current_row, column=6).value = item['rate1_bet'] ws.cell(row=current_row, column=7).value = item['rate2_bet'] current_row += 1 # 着色逻辑:所有数据写入完成后统一设置格式 GREEN = '00FF00' RED = 'FF0000' for row_num in range(2, current_row): # 获取单元格值 rate1_ts = ws.cell(row=row_num, column=2).value rate2_ts = ws.cell(row=row_num, column=3).value rate1_bet = ws.cell(row=row_num, column=6).value rate2_bet = ws.cell(row=row_num, column=7).value # 处理rate1着色 if rate1_ts is not None and rate1_bet is not None: if rate1_ts > rate1_bet: ws.cell(row=row_num, column=2).font = Font(color=Color(rgb=GREEN)) ws.cell(row=row_num, column=6).font = Font(color=Color(rgb=RED)) elif rate1_ts < rate1_bet: ws.cell(row=row_num, column=2).font = Font(color=Color(rgb=RED)) ws.cell(row=row_num, column=6).font = Font(color=Color(rgb=GREEN)) # 处理rate2着色 if rate2_ts is not None and rate2_bet is not None: if rate2_ts > rate2_bet: ws.cell(row=row_num, column=3).font = Font(color=Color(rgb=GREEN)) ws.cell(row=row_num, column=7).font = Font(color=Color(rgb=RED)) elif rate2_ts < rate2_bet: ws.cell(row=row_num, column=3).font = Font(color=Color(rgb=RED)) ws.cell(row=row_num, column=7).font = Font(color=Color(rgb=GREEN)) # 保存文件 wb.save(xlsx_file) # 发送完成通知 notification.notify( title='代码执行完成', message='赛事赔率处理已完成', app_icon=None, timeout=10 )
关键修改点
- 保留表头:写入数据从第2行开始,避免覆盖表头;读取数据始终从第2行开始,确保不会遗漏数据。
- 数据先收集后写入:先把所有匹配、未匹配数据收集到列表,排序后再统一写入表格,避免多次修改单元格导致的格式丢失。
- 着色逻辑后置:所有数据写入完成后再执行着色操作,确保格式不会被后续的数据写入覆盖。
- 修复差值计算逻辑:将百分比差值计算从
rate2的判断分支中移出,确保所有匹配赛事都能计算差值并参与排序。
内容的提问来源于stack exchange,提问作者Kryštof Bochníček
相关产品推荐
相关产品推荐

