
本文介绍如何使用正则与字符串分割技术,从结构化文本中准确提取两位球员的逐局得分,并确保顺序严格对应原始行序,避免因简单切片导致的错位问题。
本文介绍如何使用正则与字符串分割技术,从结构化文本中准确提取两位球员的逐局得分,并确保顺序严格对应原始行序,避免因简单切片导致的错位问题。
在处理网球赛事文本(如比赛实录、直播字幕或爬虫抓取的原始数据)时,常需从非结构化字符串中精确分离双方球员的每局得分。关键挑战在于:得分在文本中按时间/行序严格排列,但若仅依赖空格分割 + 步长切片(如 numeric_parts[::2]),会错误地将交替出现的数字误判为“玩家1/玩家2轮替”,而实际数据是“先连续列出P1全部局分,再连续列出P2全部局分”——这是本例的根本逻辑前提。
因此,正确解法必须尊重原始文本的行级结构,而非词级无序分割。
✅ 推荐方案:基于 splitlines() 的行级过滤与对半分割
观察示例文本:
Singles Round of 16. 01:33:55 D. Altmaier 4 4 M. Kecmanovic 6 6 Game Set and Match Miomir Kecmanovic. Miomir Kecmanovic wins the match 6-4 6-4 .
可见,所有纯数字行("4"、"4"、"6"、"6")各自独占一行,且前半部分属于 P1(Altmaier),后半部分属于 P2(Kecmanovic)。利用此规律,可安全地:
- 按行分割(splitlines());
- 筛选出纯数字行(line.strip().isdigit());
- 将结果列表均分为两段:前 n//2 项为 p1_score,后 n//2 项为 p2_score。
以下是完整、鲁棒的实现函数:
def extract_player_scores(row):
# 安全分割:保留原始行结构
lines = row['score'].splitlines()
# 提取每行中仅含数字(且非空)的字符串
numeric_lines = [line.strip() for line in lines if line.strip().isdigit()]
# 若无数字或奇数个数字,给出警告(可选)
n = len(numeric_lines)
if n == 0:
return pd.Series(["", ""])
if n % 2 != 0:
# 建议日志记录:异常数据,但仍按向下取整处理(如 5 行 → 前2后3)
pass
mid = n // 2
p1_scores = " ".join(numeric_lines[:mid])
p2_scores = " ".join(numeric_lines[mid:])
return pd.Series([p1_scores, p2_scores])
# 应用于 DataFrame
df_final[['p1_score', 'p2_score']] = df_final.apply(extract_player_scores, axis=1)⚠️ 关键注意事项
- 勿用 split() + 步长切片:parts.split() 会破坏行序(例如 "4\n4" 变成 ["4", "4"],但无法区分它们是否同属一人),而 ::2 更假设“P1/P2交替出现”,与实际数据模式矛盾。
- strip() 不可省略:防止 " 4 " 或 "\t6" 等含空白符的数字行被 isdigit() 拒绝。
- 异常容错建议:实战中应加入日志或标记异常样本(如 n==0 或 n%2==1),便于人工复核原始数据质量。
- 进阶扩展(可选):若需支持更复杂格式(如含 * 标记抢七、7-6(5) 等),建议改用正则 re.findall(r'^\s*(\d+)\s*$', text, re.MULTILINE) 精准匹配行首行尾纯数字。
该方法简洁、高效、语义清晰,完全贴合网球比分“先列P1各局、再列P2各局”的物理逻辑,确保提取结果顺序零偏差。










