如何用BeautifulSoup从随机HTML片段提取指定字段信息
HTML元数据字段提取优化方案
需求说明
需要从结构随机的HTML片段中提取以下6类元数据,缺失字段设为None:["badge", "issue", "date", "publisher", "city", "page"]
当前输出 vs 预期输出
当前代码仅能提取前3类字段,输出示例:
[None, 'nro 4 A.', '6.12.1939', None, None, None] # html_1 [None, None, '1905', None, None, None] # html_2 ['22', 'nro 12 ZZ', '10.2016', None, None, None] # html_3
预期完整输出:
[None, 'nro 4 A.', '6.12.1939', 'JR 10', None, 'sivu 1'] # html_1 [None, None, '1905', 'Aksel Paul', 'Helsinki', 'page 63'] # html_2 ['22', 'nro 12 ZZ', '10.2016', 'Arbetarförlaget Ab', 'Stockholm', 'sida 20'] # html_3
问题分析
现有代码仅处理了badge、issue、date,剩余字段(publisher、city、page)因在HTML中位置随机,soup.find_all("span", class_="ng-star-inserted")返回的列表长度不固定,无法通过固定索引提取。
优化实现方案
核心思路:通过文本特征匹配识别剩余字段,而非依赖固定位置。具体步骤:
- 初始化结果列表,默认全为
None - 提取
badge:基于固定class定位 - 提取
issue和date:从font-weight-bold容器下的元素中,通过文本关键词(nro)和日期格式特征区分 - 提取剩余字段:遍历所有
ng-star-inserted元素,排除已提取的issue和date,再通过文本特征匹配page,剩余文本按顺序映射到publisher和city
完整代码
from bs4 import BeautifulSoup def extract_metadata(html): # 初始化结果列表,对应顺序:badge, issue, date, publisher, city, page desired_list = [None] * 6 soup = BeautifulSoup(html, "lxml") # 1. 提取badge badge = soup.find("span", class_="badge badge-secondary ng-star-inserted") if badge: desired_list[0] = badge.text.strip() # 2. 提取issue和date fwb = soup.find("span", class_="font-weight-bold") if fwb: issue_date_elements = fwb.select("span.ng-star-inserted") for el in issue_date_elements: text = el.text.strip() if "nro" in text.lower(): desired_list[1] = text # 判断日期:包含数字和分隔符(.),或纯年份 elif any(char.isdigit() for char in text): desired_list[2] = text # 3. 提取publisher、city、page all_ng_elements = soup.find_all("span", class_="ng-star-inserted") # 收集已识别的文本,用于排除重复 identified_texts = [t for t in desired_list if t is not None] remaining_texts = [] for el in all_ng_elements: text = el.text.strip() if text and text not in identified_texts: remaining_texts.append(text) # 匹配page字段(含sivu/page/sida等关键词) page_keywords = {"sivu", "page", "sida"} for idx, text in enumerate(remaining_texts): if any(keyword in text.lower() for keyword in page_keywords): desired_list[5] = text remaining_texts.pop(idx) break # 剩余文本按顺序分配给publisher和city if len(remaining_texts) >= 1: desired_list[3] = remaining_texts[0] if len(remaining_texts) >= 2: desired_list[4] = remaining_texts[1] return desired_list
代码说明
- badge提取:保持原有逻辑,通过特定class定位
- issue/date提取:修复原代码的覆盖问题,通过关键词
nro识别issue,通过数字特征识别日期(支持纯年份或带分隔符的格式) - 剩余字段提取:
- 先收集所有未被识别的
ng-star-inserted文本,排除已提取内容 - 通过多语言关键词匹配
page(适配不同地区的页面标识) - 剩余文本按顺序填充
publisher和city,适配不同HTML的结构差异
- 先收集所有未被识别的
测试验证
将三段HTML分别传入extract_metadata函数,即可得到预期的完整输出。
内容的提问来源于stack exchange,提问作者farid
相关产品推荐
相关产品推荐

