#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 构建《中医入门》应用理论依据库 → 02_加工数据/理论依据库.json 用途: 为体质调理/药膳推荐/处方分析等应用提供可调用的理论依据(原文+行号溯源) 每条依据含: 原文引文 + 行号(指向 02_加工数据/中医入门.md) """ import json, os, re, datetime BOOK = "/home/songyi/Documents/ai_agent_scraper_study/data/中医入门资料库/02_加工数据/中医入门.md" OUT = "/home/songyi/Documents/ai_agent_scraper_study/data/中医入门资料库/02_加工数据/理论依据库.json" TODAY = datetime.date.today().isoformat() lines = open(BOOK, encoding="utf-8").read().splitlines() text = "\n".join(lines) def ln(idx): # 字符偏移 → 行号(1-based) return text[:idx].count("\n") + 1 def find_seg(start, end): i, j = text.find(start), text.find(end) return (i, j) if 0 <= i < j else (i, len(text)) lib = {"metadata": { "生成日期": TODAY, "来源": "中医入门资料库/02_加工数据/中医入门.md(秦伯未《中医入门》体例,用户定级: 重要理论基础)", "用途": "应用调用层——体质调理/药膳推荐/处方分析/配伍安全核查时,调用本库返回《中医入门》理论依据(原文+行号)", "查询接口": "../05_脚本工具/theory_api.py"}} # ============ 八法 ============ i0, j0 = find_seg("三、八法", "四、常用治法") bafa = {} for m in re.finditer(r'^\d+[\.、]\s*([^\s::,。]{1,2}法)[::]\s*(.+)$', text[i0:j0], re.M): name = m.group(1) seg_after = text[i0+j0 if False else i0+m.end(): j0] first = seg_after.split("。")[0] bafa[name] = {"定义原文": (m.group(2).split("。")[0] + "。").strip() if m.group(2) else "", "行号": ln(i0 + m.start())} lib["八法"] = bafa # ============ 常用治法 72 ============ i0, j0 = find_seg("四、常用治法", "## 第三章") zhifa, missing = {}, [] for m in re.finditer(r'^(\d+)[\.、,,]?\s*([^\s::,。]{2,12}法)[::]\s*(.*)$', text[i0:j0], re.M): num, name, rest = int(m.group(1)), m.group(2), m.group(3) use = re.search(r'用于(.*?)(?:药如|。|$)', rest) yao = re.search(r'药如([^.。;;]*)', rest) zhifa[str(num)] = {"名称": name, "适应症": use.group(1).strip(",,。 ") if use else None, "药如": ([x.strip() for x in re.split(r'[、,,]', yao.group(1)) if x.strip()] if yao else []), "行号": ln(i0 + m.start())} nums = sorted(int(k) for k in zhifa) gaps = [n for n in range(nums[0], nums[-1]+1) if n not in nums] lib["治法72"] = {"条目": zhifa, "总数": len(zhifa), "编号范围": [nums[0], nums[-1]], "缺号": gaps, "缺号说明": "原书编号缺号或OCR漏行,如实记录" if gaps else None} # ============ 七方 ============ # 切片止于七方段结尾(其后是张景岳八阵与汪昂22类剂型分类,"22. 救急方"属剂型分类,非七方) i0, j0 = find_seg("二、七方", "三、剂型") cut = text.find("七方是方剂组成的法则之一", i0) if 0 < cut < j0: j0 = cut qifang, cur, cur_pos = {}, None, None for m in re.finditer(r'^\d+[\.、]\s*([^\s::,。]{1,3})方[::]\s*(.+)$|^(二、七方)', text[i0:j0], re.M): if m.group(3): cur = None; continue cur = m.group(1) + "方" para = m.group(2) # (.+)$ 已捕获整段(每段一行),无需再找空行 # 清洗举例药名: 剥离连接词前缀("如下法中的大承气汤"→"大承气汤") examples = [] for tok in re.findall(r'([\u4e00-\u9fff]{2,9}(?:汤|丸|散|饮|膏))', para): tok = re.split(r'[如用和及等是的中的将]', tok)[-1] if len(tok) >= 3 and tok not in examples: examples.append(tok) qifang[cur] = {"定义原文": para[:150].strip(), "书内举例": examples, "行号": ln(i0 + m.start())} lib["七方"] = qifang # ============ 效能分类 15 ============ i0, j0 = find_seg("二、效能", "#### 1. 扶正类") xiaoneng = {} for m in re.finditer(r'^(\d+)[\.、]\s*([^\s::,。]{2,8}药)[::]\s*(.+)$', text[i0:j0], re.M): seg = m.group(3) reps = [] for km in re.finditer(r'([\u4e00-\u9fff]{2,8})如([、\u4e00-\u9fff()]{2,60})', seg): for h in re.split(r'[、]', km.group(2)): h = re.sub(r'([^)]*)', '', h).strip("等") if h: reps.append(h) xiaoneng[m.group(2)] = {"定义原文": seg.split("。")[0] + "。", "代表药": reps[:24], "行号": ln(i0 + m.start())} lib["效能分类"] = xiaoneng # ============ 扶正/祛邪功效药组 ============ groups = [] def parse_groups(start, end, kind): i0, j0 = find_seg(start, end) cur_sys = "" for m in re.finditer(r'^\((\d+)\)\s*属于(.+?):|^[^\n#\d(].*?——.*$', text[i0:j0], re.M): line = m.group(0).strip() if m.group(1): cur_sys = m.group(2); continue eff, _, rhs = line.partition("——") label = f"{kind}·{cur_sys}·{eff.strip()}" if cur_sys else f"{kind}·{eff.strip()}" herbs = [] for h in re.split(r'[、,,]', rhs.strip().strip("。")): h = re.sub(r'([^)]*)', '', h).strip() h = re.sub(r'^(亦可用鲜|亦可用)', '', h).strip() if h: herbs.append(h) groups.append({"组": label, "药": herbs, "行号": ln(i0 + m.start())}) parse_groups("#### 1. 扶正类", "#### 2. 祛邪类", "扶正类") parse_groups("#### 2. 祛邪类", "三、归经", "祛邪类") lib["功效药组"] = {"条目": groups, "总数": len(groups)} # ============ 示例方 40 ============ i0, j0 = find_seg("一、基本方剂", "## 第四章") shili = {} for m in re.finditer(r'^(\d+)[\.、]\s*([^::\n]+)[::]\s*(.+)$', text[i0:j0], re.M): name = m.group(2).strip() rest = m.group(3) comp = [c.strip() for c in rest.split("。")[0].split("、") if c.strip()] pos = re.search(r'为([^,。]{2,12})主方', rest) zhi = re.search(r'用于([^。]*)', rest) shili[name] = {"序号": int(m.group(1)), "组成_书": comp, "主方定位": pos.group(1) if pos else None, "主治": zhi.group(1).strip() if zhi else None, "行号": ln(i0 + m.start())} lib["示例方40"] = shili # ============ 二十八脉主症 ============ i0, j0 = find_seg("二十八脉的主症", "## 第三章") mai = {} for m in re.finditer(r'^([^\s,。]{1,2})脉主(.+)$', text[i0:j0], re.M): mai[m.group(1) + "脉"] = {"主症原文": m.group(2).strip().rstrip("。"), "行号": ln(i0 + m.start())} lib["脉象主症"] = {"条目": mai, "总数": len(mai), "说明": "标题为二十八脉,实列27条,长脉仅见总括韵文(原书编排,如实记录)"} # ============ 用药安全 ============ ph_i = text.find("一、配合"); ph_j = text.find("二、用量") ph = text[ph_i:ph_j] i18 = ph.find("十八反歌:") seg18_prose = ph[ph.find("歌中所提十八种药"):ph.find("歌中所提十九种药")] seg19_prose = ph[ph.find("歌中所提十九种药"):ph.find("此外,妊娠禁忌")] # 畏对提取: 从"如硫黄畏朴硝"起取,避开首句"即表示相畏比较显著"的噪音 iex = seg19_prose.find("如硫黄畏朴硝") pairs = [(a.replace("如", "", 1) if a.startswith("如") else a, b) for a, b in re.findall(r'([\u4e00-\u9fff]{1,4})畏([\u4e00-\u9fff]{1,5})', seg19_prose[iex:]) if len(a) >= 2 and len(b) >= 2] for extra in [("川乌", "犀角"), ("草乌", "犀角")]: # "川乌、草乌畏犀角"正则只捕草乌,补川乌 if extra not in pairs: pairs.append(extra) i19 = ph.find("十九畏歌:") seg_preg = ph[ph.find("此外,妊娠禁忌"):ph.find("经验告诉我们")] safety = {"十八反": { "相反组": [ {"组": ["乌头"], "组_通行扩展": ["川乌", "草乌", "附子"], "反": ["半夏", "瓜蒌", "贝母", "白蔹", "白及"], "反_通行扩展": [], "说明": "书列'乌头';附子为乌头子根,通行本明列附子反半夏/瓜蒌等,安全核查按通行扩展执行"}, {"组": ["甘草"], "组_通行扩展": [], "反": ["海藻", "大戟", "甘遂", "芫花"], "反_通行扩展": [], "说明": None}, {"组": ["藜芦"], "组_通行扩展": [], "反": ["人参", "沙参", "细辛", "芍药"], "反_通行扩展": ["丹参", "玄参", "苦参"], "说明": "书列'诸参'散文明列人参、沙参;通行本扩展丹参/玄参/苦参"}], "歌诀原文": re.search(r'十八反歌:[^\n]+', ph).group(0), "散文原文": seg18_prose.strip().split("\n")[0], "行号": ln(ph_i + i18), "通行扩展说明": "歌诀'诸参辛芍反藜芦',通行本藜芦组扩展含丹参/玄参/苦参;乌头组扩展含附子。扩展项在核查结果中标注[通行扩展]"}, "十九畏": { "畏对": pairs, "散文原文": seg19_prose.strip().split("\n")[0], "行号": ln(ph_i + i19)}, "妊娠禁忌": {"药": [], "原文摘录": seg_preg.strip()[:400], "行号": ln(ph_i + ph.find("此外,妊娠禁忌"))}} for m in re.finditer(r'(植物|动物|矿物)药如([^;。]+)', seg_preg): for x in re.split(r'[、]', m.group(2)): x = x.strip() if 2 <= len(x) <= 6: safety["妊娠禁忌"]["药"].append(x) lib["用药安全"] = safety # ============ 输出 ============ json.dump(lib, open(OUT, "w", encoding="utf-8"), ensure_ascii=False, indent=1) print("理论依据库:", OUT, f"({os.path.getsize(OUT)} bytes)") print(f"八法 {len(bafa)} | 治法 {len(zhifa)}(缺号{gaps}) | 七方 {len(qifang)} | 效能 {len(xiaoneng)} | " f"药组 {len(groups)} | 示例方 {len(shili)} | 脉 {len(mai)} | 十八反组3 | 十九畏 {len(safety['十九畏']['畏对'])}对 | 妊娠禁忌 {len(safety['妊娠禁忌']['药'])}味")