MARC格式文本提取问题:无法捕获重复的¶7146$u字段值
MARC格式字符串重复区域提取问题解决
问题背景
现有一个名为df的DataFrame,其中text列每行是一条MARC格式的字符串,示例如下:
d20s 22 i2as¶001VNINDEA455133910000005¶008180529c 1996 frmmm wz 7b ¶009se z 1 m mm c¶008a ¶008at ¶008ap ¶008a ¶0441 $a2609-2565$c2609-2565¶0410 $afre$aeng$apor ¶0569 $a2758-8965$c4578-7854¶0300 $a789$987$754 ¶051 $atxt$asti$atdi$bc¶110 $317737535$w20..b.....$astock market situation¶3330 $aimport and export agency ABC¶7146 $q1$uwwww.abc.org$ma1¶7146 $q9$uAgency XYZ¶8799 $q1$uAgency ABC$fHTML$
需要提取:
- ¶7146区域中$u后的所有内容
- ¶0441区域中$c后的内容
期望结果如下:
| ¶7146$u | ¶0441$c |
|---|---|
| wwwww.abc.org | 2609-2565 |
| Agency XYZ | 2609-2565 |
用户编写的Python代码如下:
import os import pandas as pd import numpy as np import requests df = pd.read_csv('dataset.csv') def extract(text, start_pattern, sc): ist = text.find(start_pattern) if ist < 0: return "" ist = text.find(sc, ist) if ist < 0: return "" im = text.find("$", ist + len(sc)) iz = text.find("¶", ist + len(sc)) if im >= 0: if iz >= 0: ie = min(im, iz) else: ie = im else: ie = iz if ie < 0: return "" return text[ist + len(sc): ie] def extract_text(row, list_in_zones): text = row["text"] if pd.isna(text): return [""] * len(list_in_zones) patterns = [("¶" + p, "$" + c) for p, c in [zone.split("$") for zone in list_in_zones]] return [extract(text, pattern, sc) for pattern, sc in patterns] list_in_zones = ["7146$u", "0441$u", "200$y"] df[list_in_zones] = df.apply(lambda row: extract_text(row, list_in_zones), axis=1, result_type="expand") df.to_excel("extract.xlsx", index = False)
但代码仅能提取到¶7146$u的第一个值wwww.abc.org,无法捕获重复出现的Agency XYZ。
补充规则:每个区域以¶开头(如¶7146、¶0441),字段以$开头(如$u、$c),字段内容结束于$或¶。
代码问题分析
- 单匹配逻辑限制:原
extract函数仅执行一次find操作,只会定位到第一个匹配的目标区域,不会遍历字符串中所有重复出现的同编号区域(比如示例中的两次¶7146)。 - 需求与参数不匹配:
list_in_zones中写的是"0441$u",但实际需求是提取0441$c的内容,参数设置错误。 - 结果格式不兼容多行输出:原函数设计为返回单个字符串,无法返回多个匹配值,无法满足将重复值拆分为多行的需求。
修改后的解决方案
核心思路
- 按
¶分割MARC字符串,拆分出所有独立区域; - 遍历每个区域,筛选出目标编号的区域(如7146、0441);
- 在目标区域内,提取所有目标字段(如$u、$c)的内容;
- 整理结果,将多值提取结果转换为可扩展的格式,实现一行输入对应多行输出。
修改后的代码
import pandas as pd df = pd.read_csv('dataset.csv') def extract_all_values(text, zone_code, field_code): """提取指定区域中指定字段的所有值""" if pd.isna(text): return [] # 按¶分割所有区域,跳过空的片段 zones = [zone.strip() for zone in text.split('¶') if zone.strip()] results = [] for zone in zones: # 检查区域是否以目标编号开头 if zone.startswith(zone_code): # 提取当前区域内的所有目标字段内容 pos = 0 field_prefix = f'${field_code}' while True: # 查找当前位置后的字段 field_start = zone.find(field_prefix, pos) if field_start == -1: break # 计算字段内容的起始位置 content_start = field_start + len(field_prefix) # 查找字段内容的结束位置(下一个$或区域末尾) content_end = zone.find('$', content_start) if content_end == -1: content_end = len(zone) # 提取内容并去重(可选,根据需求) value = zone[content_start:content_end].strip() if value: results.append(value) # 更新位置,继续查找下一个同字段 pos = content_end return results def process_row(row): """处理单行数据,返回所有提取结果的组合""" text = row["text"] # 提取7146$u的所有值 u_values = extract_all_values(text, '7146', 'u') # 提取0441$c的所有值(如果有多个0441区域,也会全部提取) c_values = extract_all_values(text, '0441', 'c') # 处理结果对齐:如果某类值数量少,用最后一个值填充(或空值,根据需求调整) max_len = max(len(u_values), len(c_values)) # 填充至最大长度 u_filled = u_values + [u_values[-1]]*(max_len - len(u_values)) if u_values else ['']*max_len c_filled = c_values + [c_values[-1]]*(max_len - len(c_values)) if c_values else ['']*max_len # 返回多行数据的列表 return pd.DataFrame({'¶7146$u': u_filled, '¶0441$c': c_filled}) # 处理每一行并合并结果 final_df = pd.concat([process_row(row) for _, row in df.iterrows()], ignore_index=True) # 保存结果 final_df.to_excel("extract.xlsx", index=False)
代码说明
extract_all_values函数:负责遍历所有区域,收集指定区域内指定字段的所有值,支持同一区域内多个相同字段的提取;process_row函数:处理单行MARC数据,提取目标字段的所有值,并对齐结果长度(确保7146的每个值都对应0441的内容);- 最终通过
pd.concat将每行的多行结果合并成最终的DataFrame,符合期望的输出格式。
内容的提问来源于stack exchange,提问作者THOMAS
相关产品推荐
相关产品推荐

