
本文介绍如何在解析无明确结构的 html 表格时,根据 `
在实际网页爬虫开发中,常会遇到一类“伪多表”结构:整个
需要创建、读取、编辑或处理 Word 文档(.docx / .dotx)时使用。触发词:'Word'、'word 文档'、'.docx'、'生成报告'、'制作合同/简历/方案/说明书'、'提取 word 内容'、'在文档里查找替换'、'给文档加页眉页脚/目录/表格'。也用于把任意内容排版成一份正式的 Word 文件。不要用它处理 PDF、Excel 或无关编程任务。中文创作场景尤其适合:小说大纲稿、投稿文档、合同、简历、工作总结。
| (如 "MLB Spring Training - Monday, March 27th")作为逻辑分组标识,其后紧跟若干 | 表示具体赛事条目,直至下一个同类 | 出现——这种模式虽不符合标准 HTML 表格规范,却广泛存在于体育赛事、日程公告等动态页面中。 要可靠提取某类分组(如 MLB)下的全部赛事数据,关键在于建立上下文感知的遍历逻辑。以下是两种经过验证的专业方案: ✅ 方案一:基于 find_next_siblings() 的声明式定位(推荐)该方法语义清晰、可读性强,适用于已知目标 |
易于识别的场景(如含明确文本关键词):from bs4 import BeautifulSoup
# 假设 soup 已加载完整 HTML
td_headers = soup.find_all('td', class_='head1') # 精准定位标题行(避免匹配 head1x)
target_keyword = 'MLB'
for td in td_headers:
if target_keyword in td.get_text():
# 获取该 <td> 后所有同级兄弟标签,直到下一个 <td class="head1">
next_ths = []
for sibling in td.find_next_siblings():
if sibling.name == 'td' and 'head1' in sibling.get('class', []):
break # 遇到新分组标题,停止收集
if sibling.name == 'th':
next_ths.append(sibling)
# 解析每个 <th> 中的时间与队伍信息
games = []
for th in next_ths:
date_div = th.find('div') # 第一个 div 通常是日期
time_div = th.find('div', class_='time') # 时间子 div
teams = [div.get_text(strip=True) for div in th.find_all('div') if div != date_div and div != time_div]
games.append({
'date': date_div.get_text(strip=True) if date_div else None,
'time': time_div.get_text(strip=True) if time_div else None,
'teams': teams[:2] if len(teams) >= 2 else teams
})
print(f"Found {len(games)} MLB games:")
for g in games:
print(f" {g['date']} {g['time']} — {' vs '.join(g['teams'])}")
break # 仅处理第一个匹配项(如需全部,移除此行)⚠️ 注意事项: 使用 soup.find_all('td', class_='head1') 而非泛化的 find_all('td'),可避免误匹配 等装饰性单元格; find_next_siblings() 返回的是文档顺序中紧邻的后续兄弟节点,天然符合“标题后连续数据行”的逻辑; break 在遇到下一个 head1 时终止,确保不跨组混入 NHL 或 NBA 数据。 ✅ 方案二:单次线性遍历 + 状态机(内存高效)当 ResultSet 已预先扁平化为 cells = row.find_all(['th','td']) 列表时,推荐用状态变量控制流程,避免重复 DOM 遍历: cells = []
for row in soup_table.tbody.find_all("tr"):
cells.extend(row.find_all(['th', 'td']))
mlb_games = []
in_mlb_section = False
for cell in cells:
# 进入 MLB 分组
if (cell.name == 'td'
and 'head1' in cell.get('class', [])
and 'MLB' in cell.get_text()):
in_mlb_section = True
continue
# 退出当前分组(遇到任意其他 head1 标题)
if (cell.name == 'td'
and 'head1' in cell.get('class', [])
and 'MLB' not in cell.get_text()):
in_mlb_section = False
continue
# 收集 MLB 分组内的 th 数据
if in_mlb_section and cell.name == 'th':
# 提取时间与队伍(同上)
date = cell.find('div').get_text(strip=True) if cell.find('div') else ''
time_el = cell.find('div', class_='time')
time_str = time_el.get_text(strip=True) if time_el else ''
teams = [d.get_text(strip=True) for d in cell.find_all('div')[1:]][:2]
mlb_games.append({'date': date, 'time': time_str, 'teams': teams})
print(f"Extracted {len(mlb_games)} MLB entries.")? 总结建议
掌握这两种模式,即可灵活应对各类“标题+数据块”混合结构,为体育、金融、课表等垂直领域爬虫打下坚实基础。 |
|---|

















