ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python正则表达式实战:从多语言OST标题中提取结构化数据

Python正则表达式实战:从多语言OST标题中提取结构化数据 在实际项目开发中我们经常需要处理来自不同渠道、格式不规范的原始数据并将其转化为结构化的、可供程序处理的信息。例如从社交媒体、内容平台或第三方API获取的标题字符串往往混杂了多种语言、特殊符号、情感标记和业务关键词。直接使用这样的原始字符串进行搜索、分类或存储是低效且容易出错的。本文将以一个典型的、包含多语言和特殊格式的影视OST原声带标题字符串为例演示如何通过Python进行系统性的数据清洗、解析和信息提取最终构建一个结构化的数据模型。这个过程不仅适用于媒体内容处理也广泛适用于电商商品标题解析、日志信息格式化、用户输入标准化等场景。通过本文你将掌握一套从混乱的原始文本中提取关键元数据如语言、剧集名、歌曲名、类型、发布者的完整方法。我们将从分析字符串结构开始逐步实现正则表达式匹配、字典构建、异常处理并最终输出一个清晰的JSON对象。无论你是数据分析师、后端开发工程师还是爬虫工程师这套文本预处理和解析的思路都能直接应用于你的日常工作。1. 理解原始字符串的结构与解析目标我们拿到的原始字符串是【中字】【Net JJ】《รักที่เป็นเธอ (The Love I Found)》Ost.LoveUponATimeSerieOfficial MV。这并非一个随意的乱码而是包含了特定社区文化如使用【】标注字幕信息、多语言泰语、英语、中文和明确业务字段OST、剧集名、MV类型的复合体。直接进行字符串分割如按空格或特定符号会非常脆弱因为不同字段的边界模糊且符号使用不统一。我们的解析目标是将这个字符串转化为一个结构化的字典或JSON包含以下字段title_original: 原始标题字符串。title_clean: 清理掉所有修饰性标记如【】、后的核心歌曲名。language_tags: 识别出的语言标签列表如[“中字”, “Thai”, “English”]。publisher: 发布者或来源频道如“Net JJ”。song_name_th: 泰语歌曲名。song_name_en: 英语歌曲名。content_type: 内容类型如“OST”。series_name: 所属剧集或系列名如“LoveUponATimeSerie”。video_type: 视频类型如“Official MV”。为了实现这个目标我们需要先观察字符串的组成部分前缀修饰符【中字】和【Net JJ】通常用方括号包裹表示附加信息。核心歌曲名《รักที่เป็นเธอ (The Love I Found)》用书名号包裹内部包含泰语原名和括号内的英语译名。后缀元数据Ost.LoveUponATimeSerieOfficial MV用点号连接类型与剧集名用竖线分隔其他类型信息。这种结构化的“包装”虽然对人眼友好但对程序来说需要精确的规则来拆解。2. 环境准备与核心工具选择本次解析工作主要使用Python因其在文本处理和数据清洗方面拥有强大的生态系统。我们不需要复杂的深度学习模型用标准库和正则表达式就能高效完成任务。2.1 基础环境与依赖确保你有一个可运行的Python环境建议Python 3.7及以上。我们将主要使用以下内置库re: 用于正则表达式匹配是本次解析的核心。json: 用于最后的结构化输出。typing: 可选用于类型注解让代码更清晰。不需要安装额外的第三方包。你可以通过以下命令检查环境并创建一个新的脚本文件# 检查Python版本 python --version # 创建一个新的Python脚本文件例如 parse_title.py touch parse_title.py2.2 设计解析策略与正则表达式正则表达式是我们从文本中提取模式化信息的利器。针对原始字符串的每一部分我们需要设计对应的正则模式。提取【】内的标签模式r【([^】])】可以匹配所有中文方括号及其内部内容。[^】]表示匹配一个或多个非】的字符。提取《》内的歌曲名模式r《([^《》])》可以匹配书名号及其内部内容。我们需要进一步处理内部可能存在的(英文译名)。分离泰语与英语歌名假设英文译名总是被括号包裹在泰语名之后我们可以用r(.*?) 来匹配括号内的英文部分。提取Ost.后的剧集名模式rOst\.([^])可以匹配Ost.之后、竖线之前的所有字符。注意点号.在正则中是特殊字符需要转义\.。提取后的视频类型模式r(.)可以匹配竖线之后的所有字符直到字符串末尾。将这些模式组合起来并考虑它们可能不存在的情况即匹配不到是我们编写解析函数的关键。3. 分步实现标题解析函数我们将编写一个名为parse_ost_title的函数它接收原始标题字符串返回一个结构化的字典。3.1 函数框架与初始字典首先我们定义函数并初始化结果字典同时保存原始标题。import re import json from typing import Dict, Any, List, Optional def parse_ost_title(raw_title: str) - Dict[str, Any]: 解析包含多语言和标签的OST视频标题。 Args: raw_title: 原始标题字符串。 Returns: 包含解析后各个字段的字典。 result { “title_original”: raw_title, “title_clean”: “”, “language_tags”: [], “publisher”: “”, “song_name_th”: “”, “song_name_en”: “”, “content_type”: “”, “series_name”: “”, “video_type”: “”, } # 后续的解析步骤将填充这个字典 return result3.2 步骤一提取所有【】标签我们使用findall方法找到所有方括号标签然后遍历它们根据常见规则进行分类。# 步骤1: 提取所有【】内的标签 bracket_tags re.findall(r【([^】])】, raw_title) for tag in bracket_tags: # 判断标签类型 if “字” in tag: # 例如“中字”、“英字” result[“language_tags”].append(tag) elif “JJ” in tag or “Net” in tag: # 简单的发布者识别实际可根据已知列表判断 result[“publisher”] tag else: # 如果不确定也暂时放入语言标签或忽略 result[“language_tags”].append(tag)3.3 步骤二提取并处理《》内的歌曲名首先提取整个书名号内的内容然后尝试分离泰语名和英语译名。# 步骤2: 提取《》内的歌曲名 song_full_match re.search(r《([^《》])》, raw_title) if song_full_match: song_full song_full_match.group(1).strip() # 获取《》内的完整字符串 # 尝试分离泰语名和括号内的英文名 # 假设格式为“泰语名 (英文名)” thai_en_match re.match(r([^(])(?:\((.)\))?, song_full) if thai_en_match: result[“song_name_th”] thai_en_match.group(1).strip() if thai_en_match.group(2): result[“song_name_en”] thai_en_match.group(2).strip() else: # 如果没有括号整个内容可能是泰语或其它语言 result[“song_name_th”] song_full3.4 步骤三提取Ost.后的剧集名和后的视频类型这两个信息通常位于字符串后部。# 步骤3: 提取 Ost. 后的剧集名 series_match re.search(rOst\.([^]), raw_title) if series_match: result[“series_name”] series_match.group(1).strip() result[“content_type”] “OST” # 因为匹配到了Ost.所以类型是OST # 步骤4: 提取 后的视频类型 video_type_match re.search(r(.), raw_title) if video_type_match: result[“video_type”] video_type_match.group(1).strip()3.5 步骤四生成清理后的标题清理后的标题通常是歌曲名优先使用英文其次泰文加上剧集名去掉所有修饰性符号。# 步骤5: 生成清理后的标题 (clean title) clean_parts [] if result[“song_name_en”]: clean_parts.append(result[“song_name_en”]) elif result[“song_name_th”]: clean_parts.append(result[“song_name_th”]) if result[“series_name”]: clean_parts.append(f”from {result[‘series_name’]}“) result[“title_clean”] “ - “.join(clean_parts) if clean_parts else raw_title # 备用方案3.6 完整函数与测试将以上所有步骤组合起来就得到了完整的解析函数。我们写一个简单的测试来验证效果。if __name__ “__main__”: test_title “【中字】【Net JJ】《รักที่เป็นเธอ (The Love I Found)》Ost.LoveUponATimeSerieOfficial MV” parsed_data parse_ost_title(test_title) # 使用json.dumps美化输出ensure_asciiFalse确保非英文字符正常显示 print(json.dumps(parsed_data, indent2, ensure_asciiFalse))运行这个脚本预期会得到如下输出{ “title_original”: “【中字】【Net JJ】《รักที่เป็นเธอ (The Love I Found)》Ost.LoveUponATimeSerieOfficial MV”, “title_clean”: “The Love I Found - from LoveUponATimeSerie”, “language_tags”: [“中字”], “publisher”: “Net JJ”, “song_name_th”: “รักที่เป็นเธอ”, “song_name_en”: “The Love I Found”, “content_type”: “OST”, “series_name”: “LoveUponATimeSerie”, “video_type”: “Official MV” }4. 处理边界情况与增强鲁棒性上面的基础版本能处理“标准格式”但真实数据往往充满意外。一个健壮的解析器必须考虑边界情况。4.1 常见边界情况与处理策略情况描述示例输入问题增强策略标签顺序变化【Net JJ】【中字】《...》发布者标签可能不在第二个位置。提取所有标签后根据内容如是否包含“字”、“sub”等判断其类别而非顺序。缺少某些部分《รักที่เป็นเธอ》Official MV缺少【】标签、英文译名和Ost.信息。在每个re.search或re.match后都进行if match判断对缺失字段提供默认值空字符串。符号使用不统一[中字] (Net JJ) “歌曲名” OST: 剧集名使用了[]、()、“”等不同符号。扩展正则表达式使其支持多种常见符号变体。例如用r(?:【多余空格或换行【中字】 《...》 Ost. ...符号前后有空格。在提取字符串后使用.strip()方法清理首尾空格。在正则模式中可以使用\s*来匹配可能存在的空白。语言标签更复杂【泰语中字】一个标签包含多种信息。可以进一步拆分标签例如按“泰语”、“中字”拆分成两个独立的标签存入language_tags列表。剧集名包含点号Ost.Love.Upon.A.Time.Serie简单的Ost\.([^])会提前在第一个点号后截断。修改正则明确剧集名的结束边界例如到竖线或字符串结尾r’Ost.(.?)(?4.2 增强版解析函数片段以下是对几个关键部分进行增强的示例def parse_ost_title_enhanced(raw_title: str) - Dict[str, Any]: result { ... } # 初始化字典同上 # 增强1: 支持多种括号的标签提取 # 匹配 【】、[]、()、 all_tags re.findall(r(?:【|\[|\(|)([^】\]\)])(?:】|\]|\)|), raw_title) for tag in all_tags: tag_lower tag.lower() if ‘字’ in tag or ‘sub’ in tag_lower or ‘字幕’ in tag: result[“language_tags”].append(tag) elif ‘jj’ in tag_lower or ‘net’ in tag_lower: # 更灵活的发布者识别 result[“publisher”] tag # 可以在此处添加更多规则... # 增强2: 更健壮的歌曲名提取支持多种括号和引号 # 匹配 《》、、“”、‘’、{} song_match re.search(r(?:《||“|‘|{)([^《》“”‘’{}])(?:》||”|’|}), raw_title) if song_match: song_full song_match.group(1).strip() # 分离语言的处理逻辑同上 ... # 增强3: 提取剧集名处理可能包含点号的情况结束于竖线或字符串尾 series_match re.search(rOst\.\s*(.?)(?\s*|$), raw_title, re.IGNORECASE) if series_match: result[“series_name”] series_match.group(1).strip() result[“content_type”] “OST” # 增强4: 提取视频类型支持多种前置分隔符(、-、–) video_type_match re.search(r(?:|-|–)\s*(.), raw_title) if video_type_match: result[“video_type”] video_type_match.group(1).strip() # ... 其他逻辑 return result5. 集成到数据处理流程与生产环境建议一个解析函数很少单独使用。它通常是一个更大数据处理管道Pipeline的一部分。5.1 构建一个简单的处理管道假设我们从某个数据源如文件、API读取了一批标题需要批量处理并存储。def process_titles_batch(title_list: List[str]) - List[Dict[str, Any]]: “”“批量处理标题列表”“” parsed_results [] for idx, raw_title in enumerate(title_list): try: parsed_data parse_ost_title_enhanced(raw_title) parsed_results.append(parsed_data) except Exception as e: # 记录处理失败的行和错误而不是让整个程序崩溃 print(f”Error parsing title at index {idx}: ‘{raw_title}’. Error: {e}“) # 可以选择附加一个包含错误信息的空结果或None parsed_results.append({“title_original”: raw_title, “parse_error”: str(e)}) return parsed_results # 模拟批量数据 batch_titles [ “【中字】【Net JJ】《รักที่เป็นเธอ (The Love I Found)》Ost.LoveUponATimeSerieOfficial MV”, “《เพียงเธอ (Only You)》Ost.Club Friday Series”, “[Eng Sub] ‘My Love’ OST. The Series - Official Audio”, “Malformed Title (Missing Brackets”, # 格式错误的数据 ] results process_titles_batch(batch_titles) for res in results: print(json.dumps(res, indent2, ensure_asciiFalse)) print(“-” * 40)5.2 生产环境注意事项日志与监控在process_titles_batch函数中我们仅打印了错误。在生产环境中应使用成熟的日志库如logging记录错误并可能将解析失败的原始数据发送到死信队列Dead Letter Queue供后续人工审查。性能考虑如果处理海量数据数百万条正则表达式编译可以优化。可以使用re.compile预先编译好所有用到的正则模式避免在循环中重复编译。配置化管理发布者列表、语言标签关键词、内容类型映射等规则应从代码中抽离放入配置文件如YAML、JSON或数据库。这样规则变更时无需重新部署代码。单元测试为解析函数编写全面的单元测试覆盖正常案例、边界案例和异常案例。这是保证代码质量、防止回归的关键。结果验证解析出的数据在入库前应对关键字段如song_name_en,series_name进行基本的有效性检查如非空、长度、字符集。6. 常见问题排查在实际使用中你可能会遇到以下问题问题现象可能原因检查与解决方案解析结果为空或部分字段缺失1. 原始标题格式与正则模式不匹配。2. 字符串编码问题如包含非常规空格。3. 标签使用了未预料的符号。1. 打印原始字符串和其repr()形式查看隐藏字符print(repr(raw_title))。2. 逐一测试每个正则模式看是否能匹配到目标部分。3. 扩展正则表达式以支持更多符号变体。正则表达式匹配到了多余内容正则表达式过于“贪婪”.*匹配了超出预期的字符。将贪婪匹配改为非贪婪匹配.*?。例如从r’Ost\.(.)’改为r’Ost\.(.?)’。处理包含特殊正则字符的标题时出错标题本身包含点号.、星号*、加号等这些在正则中有特殊含义。在将用户输入传入re函数前如果模式是动态构建的应使用re.escape()进行转义。在我们的例子中模式是固定的标题是输入所以问题不大。批量处理时内存或速度问题数据量极大一次性加载所有数据到列表。使用流式处理streaming。逐行或分批读取数据解析后立即写入输出文件或数据库而不是累积在内存列表中。无法准确区分“发布者”和“普通标签”规则过于简单如仅判断是否包含“JJ”。建立已知发布者列表或使用更复杂的启发式规则如标签长度、常见发布者关键词等。对于不确定的可以放入一个other_tags字段。7. 扩展方向与最佳实践引入机器学习可选对于极其复杂、格式千变万化的标题规则系统会变得难以维护。可以考虑使用简单的文本分类模型如基于scikit-learn或序列标注模型如CRF来识别字段。但对于大多数有固定模式的场景正则表达式仍是性价比最高的选择。构建领域知识库维护一个剧集名、歌曲名、发布者的知识库。解析后可以将提取出的名称与知识库进行模糊匹配如使用fuzzywuzzy库以进行标准化和纠错。例如将LoveUponATimeSerie纠正为标准的Love Upon A Time Serie。设计可插拔的解析器可以定义一个解析器接口针对不同平台或不同格式的标题实现不同的解析器如YouTubeTitleParserBilibiliTitleParser。通过工厂模式或配置来选择使用哪个解析器。结果数据标准化解析后的数据应遵循统一的模式Schema。可以使用Pydantic或dataclasses来定义数据模型自动进行类型验证和序列化。编写防御性代码始终假设输入数据是脏的、不完整的。在每个步骤都进行None检查为关键字段设置合理的默认值并用try...except包裹可能失败的操作。文本解析是数据工程的基石之一。从看似杂乱的字符串中精准提取信息需要细心观察模式、谨慎设计规则并充分考虑边界情况。本文提供的从正则表达式设计、函数实现、批量处理到生产建议的完整链路为你处理类似问题提供了一个可靠的模板。下次当你面对一堆非结构化的文本数据时不妨先静下心来分析其潜在结构然后用系统化的代码将其转化为清晰、可用的信息。
返回列表