多站点医疗爬虫的可复用Beautiful Soup解析方案咨询
解决方案
一、用JSON配置处理嵌套HTML解析
要让非技术同事能通过JSON配置定义嵌套解析规则,你可以设计一套支持层级结构的配置格式,核心用children字段描述嵌套元素的解析逻辑。
示例JSON配置
针对你提到的期刊站点,配置可以这么写:
{ "site_id": "paediatrieschweiz", "root_selector": ".journal-issues", "extract": { "issues": { "selector": ".issue-item", "children": { "issue_name": { "selector": ".issue-title", "type": "text" }, "article_count": { "selector": ".article-count", "type": "text", "parser": "int" }, "articles": { "selector": ".article-item", "children": { "title": { "selector": ".article-title", "type": "text" }, "authors": { "selector": ".article-authors", "type": "text" } }, "is_list": true } }, "is_list": true } } }
selector: BeautifulSoup支持的CSS选择器(可扩展支持XPath)type: 提取类型,比如text(文本内容)、attr:href(标签属性)parser: 可选的转换函数(比如转整数、清理字符串)is_list: 标记当前选择器匹配多个元素,需要遍历处理children: 嵌套的子元素解析规则
Python解析逻辑示例
用递归函数处理嵌套配置:
from bs4 import BeautifulSoup # 安全的解析器映射表,替代eval避免风险 PARSER_MAP = { "int": int, "strip": lambda x: x.strip(), "lower": lambda x: x.lower() } def parse_element(soup, config): elements = soup.select(config["selector"]) if not elements: return None if config.get("is_list", False): result = [] for elem in elements: item = {} for key, child_config in config["children"].items(): item[key] = parse_element(elem, child_config) result.append(item) return result else: elem = elements[0] # 提取内容 if config["type"].startswith("attr:"): attr_name = config["type"].split(":")[1] value = elem.get(attr_name, "") else: value = elem.get_text(strip=True) # 应用转换规则 if "parser" in config: parser = PARSER_MAP.get(config["parser"]) if parser: value = parser(value) return value # 使用示例 soup = BeautifulSoup(html_content, "html.parser") # 加载JSON配置(比如用json.load) config = {} report_data = parse_element(soup, config["extract"]["issues"])
二、自定义解析代码的存储与按需导入
如果部分站点结构过于复杂,JSON配置无法覆盖,就用每个站点单独的parse.py存储自定义逻辑,再通过动态导入实现按需加载。
目录结构建议
scrapers/ ├── paediatrieschweiz/ │ └── parse.py ├── cardiology_journal/ │ └── parse.py └── ...
每个parse.py必须实现统一的入口函数,比如generate_report(soup) -> dict,返回结构化的报告数据。
动态导入实现
用Python的importlib模块加载指定站点的解析模块:
import importlib.util import os def load_custom_parser(site_id): parser_path = os.path.join("scrapers", site_id, "parse.py") if not os.path.exists(parser_path): raise ValueError(f"未找到站点 {site_id} 的解析代码") # 动态加载模块 spec = importlib.util.spec_from_file_location(f"{site_id}_parser", parser_path) parser_module = importlib.util.module_from_spec(spec) spec.loader.exec_module(parser_module) # 返回统一入口函数 return parser_module.generate_report # 使用示例 parser = load_custom_parser("paediatrieschweiz") report_data = parser(soup)
混合方案建议
你可以把两种方式结合:优先用JSON配置解析,对于复杂站点,在站点配置里标记parser_type: "custom",主程序根据这个字段自动选择用配置解析还是加载自定义代码。
内容的提问来源于stack exchange,提问作者frumsky
相关产品推荐
相关产品推荐

