初始归档:健康管理资料库(大医网/药膳/体质监测/中医理论等12个资料集,7712个文件)
This commit is contained in:
commit
80ae3811cf
7712 files changed
+4628547
No files matched your search
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""01_merge_sunben.py — 《神农本草经·清·孙星衍》(殆知阁本) 合并整理版生成
|
||||
规则(2026-09-22 用户指令):
|
||||
1. 重复出现的「卷一/二/三 上/中/下经」卷标行只保留一次(同卷连续出现的合并);
|
||||
2. 药物条目块(药名单行+味…+吴普/名医/案)合并为组织化结构,药名转为 ####;
|
||||
3. 原件正文一字不改,仅做结构性合并(去重卷标、条目并块、空行规整);
|
||||
4. 幂等可重跑;输出至 校核参考/殆知阁-孙星衍辑本/神农本草经_孙星衍辑本_合并整理版.md
|
||||
"""
|
||||
import re, sys, json, hashlib, os
|
||||
|
||||
BASE = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 01_来源数据/..
|
||||
SRC = os.path.join(BASE, '01_来源数据', '校核参考', '殆知阁-孙星衍辑本',
|
||||
'神农本草经_孙星衍辑本_原文快照_20260922.md')
|
||||
OUT = os.path.join(BASE, '01_来源数据', '校核参考', '殆知阁-孙星衍辑本',
|
||||
'神农本草经_孙星衍辑本_合并整理版.md')
|
||||
STAT = os.path.join(BASE, '01_来源数据', '校核参考', '殆知阁-孙星衍辑本',
|
||||
'合并整理版_结构.json')
|
||||
|
||||
VOL_RE = re.compile(r'^卷([一二三四])\s* ?\s*([上中下])经$')
|
||||
|
||||
def build(lines):
|
||||
out, cur_vol, stat = [], None, {'卷标原始出现': 0, '卷标合并保留': 0, '药条': 0, '序录行': 0}
|
||||
i, n = 0, len(lines)
|
||||
# frontmatter 与前言按原样保留
|
||||
buf = []
|
||||
while i < n:
|
||||
raw = lines[i]
|
||||
s = raw.strip()
|
||||
m = VOL_RE.match(s)
|
||||
if m:
|
||||
stat['卷标原始出现'] += 1
|
||||
vol = f'## 卷{m.group(1)} {m.group(2)}经'
|
||||
# 判断此卷标行是否为重复标记:其前方上一非空行若同卷经 -> 跳过
|
||||
prev_nonempty = next((l.strip() for l in reversed(buf) if l.strip()), '')
|
||||
if prev_nonempty_still_diff(vol, buf):
|
||||
pass
|
||||
if cur_vol == vol:
|
||||
i += 1
|
||||
continue # 重复卷标,合并
|
||||
cur_vol = vol
|
||||
buf.append('')
|
||||
buf.append('---')
|
||||
buf.append(vol)
|
||||
buf.append('')
|
||||
stat['卷标合并保留'] += 1
|
||||
i += 1
|
||||
continue
|
||||
# 药名行:非空、短、无句号,且下一非空行以「味」或「KT」开头
|
||||
if s and cur_vol and len(s) <= 12 and s not in ('(原注:上此五条,出《药对》中,义旨渊深,非俗所究。虽莫可遵用,而是主统之本,故亦载之。)',):
|
||||
nxt = next((l.strip() for l in lines[i+1:] if l.strip()), '')
|
||||
nxt_is_body = nxt.startswith('味') or nxt == 'KT'
|
||||
if nxt_is_body and not s.endswith(('。',)) and not s.startswith('《'):
|
||||
buf.append(f'#### {s}')
|
||||
stat['药条'] += 1
|
||||
i += 1
|
||||
continue
|
||||
buf.append(raw.rstrip())
|
||||
i += 1
|
||||
out = buf
|
||||
return out, stat
|
||||
|
||||
def prev_nonempty_still_diff(vol, buf):
|
||||
return False
|
||||
|
||||
def main():
|
||||
src_text = open(SRC, encoding='utf-8').read()
|
||||
lines = src_text.split('\n')
|
||||
body, stat = build(lines)
|
||||
text = '\n'.join(body)
|
||||
# 空行规整:连续3+空行→1空行(结构性,不改文字)
|
||||
text = re.sub(r'\n{4,}', '\n\n\n', text)
|
||||
open(OUT, 'w', encoding='utf-8').write(text)
|
||||
md5 = hashlib.md5(open(OUT, 'rb').read()).hexdigest()
|
||||
stat['md5'] = md5
|
||||
stat['bytes'] = os.path.getsize(OUT)
|
||||
stat['行数'] = text.count('\n') + 1
|
||||
# 药条按卷统计
|
||||
sec, cnt = None, {'卷一 上经': 0, '卷二 中经': 0, '卷三 下经': 0}
|
||||
for l in text.split('\n'):
|
||||
s = l.strip()
|
||||
m = VOL_RE.match(s) or (s.startswith('## ') and VOL_RE.match(s[3:]))
|
||||
if m: sec = f'卷{m.group(1)}\u3000{m.group(2)}经'
|
||||
elif s.startswith('#### ') and sec in cnt: cnt[sec] += 1
|
||||
stat['药条_按卷'] = cnt
|
||||
json.dump(stat, open(STAT, 'w', encoding='utf-8'), ensure_ascii=False, indent=1)
|
||||
print(json.dumps(stat, ensure_ascii=False, indent=1))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,138 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""03_compare_two_refs.py — 殆知阁孙本(第一参照本) ⇄ 维基文库(第二参校本) 初步对照
|
||||
三层分离:①名称对齐层(显式异体映射,对照键不改原文)→ ②繁简/标点规整层(对照镜像)→ ③实字异文层(只登记)。
|
||||
原则:两参校本任意一方都不改原文;差异全部登记 02_加工数据/产物。"""
|
||||
import re, json, os, difflib
|
||||
|
||||
BASE = '/home/songyi/Documents/ai_agent_scraper_study/data/神农本草经'
|
||||
SUN = os.path.join(BASE, '01_来源数据/校核参考/殆知阁-孙星衍辑本/神农本草经_孙星衍辑本_合并整理版.md')
|
||||
WK = os.path.join(BASE, '01_来源数据/校核参考/维基文库/神农本草经_快照_20260922.md')
|
||||
|
||||
def splitdrug_sun(text):
|
||||
drugs = {}
|
||||
cur = None
|
||||
for l in text.split('\n'):
|
||||
s = l.strip()
|
||||
if s.startswith('#### '):
|
||||
cur = s[5:].strip(); drugs.setdefault(cur, [])
|
||||
elif s.startswith('## '):
|
||||
cur = None
|
||||
elif cur is not None and s:
|
||||
drugs[cur].append(s)
|
||||
return drugs
|
||||
|
||||
def splitdrug_wk(text):
|
||||
drugs = {}
|
||||
for l in text.split('\n'):
|
||||
s = l.strip()
|
||||
if s.startswith('#') or not s or '\u3000' not in s:
|
||||
continue
|
||||
name, body = s.split('\u3000', 1)
|
||||
drugs.setdefault(name.strip(), body)
|
||||
return drugs
|
||||
|
||||
ALIAS = dict([
|
||||
('丹沙','丹砂'),('丹參','丹参'),('丹雄雞','丹雄鸡'),('乾地黃','干地黄'),('乾漆','干漆'),
|
||||
('乾薑','干姜'),('五味','五味子'),('人參','人参'),('假蘇','假苏'),('別羈','别羁'),
|
||||
('厚朴','浓朴'),('合歡','合欢'),('商陸','商陆'),('地膚子','地肤子'),('地膽','地胆'),
|
||||
('大棗','大枣'),('大豆黃卷','大黄豆卷'),('大黃','大黄'),('天門冬','天门冬'),
|
||||
('太一禹余糧','太乙余粮'),('女菀','女萎'),('女貞實','女贞实'),
|
||||
('慈石','磁石'),('敗醬','败酱'),('旋覆花','旋复花'),
|
||||
('朮','术'),('朴消','朴硝'),('杏核','杏核仁'),('柏實','柏实'),('柳華','柳花'),
|
||||
('栝樓','栝蒌根'),('桃核','桃核仁'),('槐子','槐实'),('練實','楝实'),('款冬','款冬花'),
|
||||
('水斳','水靳'),('決明子','决明子'),('沙參','沙参'),('消石','硝石'),('澤漆','泽漆'),
|
||||
('澤瀉','泽泻'),('澤蘭','泽兰'),('烏賊魚骨','乌贼鱼骨'),('烏韭','乌韭'),('烏頭','乌头'),
|
||||
('營實','营实'),('牛黃','牛黄'),('牡蠣','牡蛎'),('狗陰莖','牡狗阴茎'),('狼毒','野狼毒'),
|
||||
('猬皮','猥皮'),('獨活','独活'),('玄參','元参'),('當歸','当归'),('白僵蠶','白僵蚕'),
|
||||
('白堊','白垩'),('白膠','白胶'),('白蘞','白蔹'),('白鮮','白藓'),('白馬莖','白马茎'),
|
||||
('皂莢','皂荚'),('瞿麥','瞿麦'),('石南草','石南'),('石鐘乳','石钟乳'),('石龍子','石龙子'),
|
||||
('石龍芮','石龙芮'),('石龍芻','石龙刍'),('石韋','石苇'),('石長生','石长生'),
|
||||
('秦椒','秦菽'),('積雪草','积雪草'),('竹葉','竹叶'),('粉錫','粉锡'),('紫參','紫参'),
|
||||
('續斷','续断'),('羊躑躅','羊踯躅'),('肉松容','肉松蓉'),('芍藥','芍药'),
|
||||
('芎藭','芎藭'),('苦參','苦参'),('茈胡','柴胡'),
|
||||
('莧實','苋实'),('莨蕩子','莨荡子'),('葈耳實','耳实'),('蒲陶','葡萄'),
|
||||
('蒲黃','蒲黄'),('蒿本','蒿本'),('蓼實','蓼实'),('蔓荊實','蔓荆实'),
|
||||
('蔥實','葱实'),('蕘華','荛花'),('蕪荑','芜荑'),('薇銜','薇衔'),('薏苡人','薏苡仁'),
|
||||
('藕實莖','藕实茎'),('藍實','蓝实'),('藎草','荩草'),('蘼蕪','蘼芜'),('蚱蟬','柞蝉'),
|
||||
('蛇蛻','蛇蜕'),('蠐螬','蛴螬'),('蜜蠟','蜜蜡'),('蜣蜋','蜣螂'),('蝦蟇','虾蟆'),
|
||||
('螢火','荧火'),('螻蛄','蝼蛄'),('蠡實','蠡实'),('衛矛','卫矛'),
|
||||
('衣魚','衣鱼'),('貝子','贝子'),('貝母','贝母'),('貫眾','贯众'),('車前子','车前子'),
|
||||
('連翹','连翘'),('遠志','远志'),('郁核','郁李仁'),('酸棗','酸枣'),('酸醬','酸酱'),
|
||||
('鉛丹','铅丹'),('長石','长石'),('防風','防风'),('阿膠','阿胶'),('陸英','陆英'),
|
||||
('陽起石','阳起石'),('雀甕','雀瓮'),('雄黃','雄黄'),('雌黃','雌黄'),
|
||||
('雞頭','鸡头实'),('雲實','云实'),('雲母','云母'),('青瑯玕','青琅玕'),('青葙','青葙子'),
|
||||
('鞠華','鞠华'),('飛廉','飞廉'),('馬先蒿','马先蒿'),('馬刀','马刀'),('馬陸','马陆'),
|
||||
('鮀魚甲','鮀鱼甲'),('鯉魚膽','鲤鱼膽'),('鱉甲','鳖甲'),('鳶尾','鳶尾'),('麥門冬','麦门冬'),
|
||||
('麻蕡','麻贲'),('麻黃','麻黄'),('黃環','黄环'),('黃芩','黃芩'),('黃連','黄连'),
|
||||
('鼠婦','鼠妇'),('龍眼','龙眼'),('龍膽','龙膽'),('龍骨','龙骨'),('龜甲','龟甲'),
|
||||
('白頭公','白头翁'),
|
||||
('蘭草','兰草'),('髮髲','发'),('太乙余粮','太乙余食'),
|
||||
('旋華','旋华'),('木蘭','木蘭'),('松蘿','松萝'),('析蓂子','析蓂子'),
|
||||
('欒華','欒花'),('桐葉','桐葉'),('梅實','梅實'),('樗雞','樗雞'),('水銀','水銀'),
|
||||
('漏蘆','漏蘆'),('鷰屎','鷰屎'),('狼牙','狼牙'),('王孫','王孫'),
|
||||
('豬苓','豬苓'),('螌蝥','班苗'),
|
||||
('甘瓜子','甘瓜子'),
|
||||
('石蠶','石蠶'),('禹余糧','禹余糧'),('紫石','紫石英'),
|
||||
('細辛','細辛'),('絡石','絡石'),('膚青','膚青'),('芫華','芫華'),('藥食根','藥實根'),
|
||||
('箘桂','菌桂'),('著實','著實'),
|
||||
('白頸蚯蚓','白頸蚯蚓'),('蜀椒','蜀椒'),('吳公','吳公'),('石灰','石灰'),
|
||||
])
|
||||
import importlib, sys
|
||||
spec = importlib.util.find_spec('opencc')
|
||||
if spec:
|
||||
from opencc import OpenCC
|
||||
cc = OpenCC('t2s')
|
||||
def t2s(s): return cc.convert(s)
|
||||
else:
|
||||
def t2s(s): return s
|
||||
|
||||
def punct_norm(s):
|
||||
s = re.sub(r'[\s\u3000]+', '', s)
|
||||
s = re.sub(r'([^)]*)|\([^)]*\)', '', s)
|
||||
s = re.sub(r'[,。、;:!?,.;:!?]', '', s)
|
||||
return s
|
||||
|
||||
sun_text = open(SUN, encoding='utf-8').read()
|
||||
wk_text = open(WK, encoding='utf-8').read()
|
||||
sd_raw = splitdrug_sun(sun_text)
|
||||
wd_raw = splitdrug_wk(wk_text)
|
||||
|
||||
# 孙本经文=药条内「味…」段与紧随非吴普名医案的段;对照键取全部段落拼接但去掉吴普/名医/案部分
|
||||
SUN_BODY_STOP = re.compile(r'^(《吴普|《名医|案:|《太平御览》引)')
|
||||
sd = {}
|
||||
for name, paras in sd_raw.items():
|
||||
body = []
|
||||
for p in paras:
|
||||
if p.startswith('《吴普') or p.startswith('《名医》') or p.startswith('案:'):
|
||||
break
|
||||
body.append(p)
|
||||
sd[name] = t2s(SUN_BODY_STOP and punct_norm(''.join(body)))
|
||||
|
||||
wd = {ALIAS.get(n, n): t2s(punct_norm(b)) for n, b in wd_raw.items()}
|
||||
|
||||
matched = sorted(set(sd) & set(wd))
|
||||
same, diff_items = [], []
|
||||
diff_detail = {}
|
||||
for n in matched:
|
||||
if sd[n] == wd[n]:
|
||||
same.append(n)
|
||||
else:
|
||||
diff_items.append(n)
|
||||
sm = difflib.SequenceMatcher(None, sd[n], wd[n], autojunk=False)
|
||||
frag = []
|
||||
for op, a1, a2, b1, b2 in sm.get_opcodes():
|
||||
if op != 'equal':
|
||||
frag.append({'op': op, '孙': sd[n][a1:a2], '维基': wd[n][b1:b2]})
|
||||
diff_detail[n] = frag
|
||||
|
||||
print('对齐条目:', len(matched), ';同文:', len(same), ';异文:', len(diff_items))
|
||||
print('仅孙本:', len(set(sd)-set(wd)), ';仅维基:', len(set(wd)-set(sd)))
|
||||
json.dump({'同文': same, '异文条目': diff_items,
|
||||
'未对齐_仅孙本': sorted(set(sd)-set(wd)), '未对齐_仅维基': sorted(set(wd)-set(sd)),
|
||||
'异文明细': diff_detail},
|
||||
open(os.path.join(BASE, '02_加工数据/参校本对照_药目核对.json'), 'w', encoding='utf-8'),
|
||||
ensure_ascii=False, indent=1)
|
||||
print('已写出 02_加工数据/参校本对照_药目核对.json')
|
||||
# 摘样
|
||||
for n in diff_items[:10]:
|
||||
print('■', n, json.dumps(diff_detail[n][:4], ensure_ascii=False))
|
||||
@@ -0,0 +1,23 @@
|
||||
import json
|
||||
d = json.load(open('02_加工数据/参校本对照_药目核对.json', encoding='utf-8'))
|
||||
det = d['异文明细']
|
||||
BOILER = {'治', '无毒', '有毒', '小', '微', '寒', '大寒'}
|
||||
substantive = {}
|
||||
for name, frags in det.items():
|
||||
real = []
|
||||
for f in frags:
|
||||
s, w = f['孙'], f['维基']
|
||||
if s == '' and w in BOILER:
|
||||
continue
|
||||
if w == '' and s in BOILER:
|
||||
continue
|
||||
real.append(f)
|
||||
if real:
|
||||
substantive[name] = real
|
||||
print('实字异文条目(粗):', len(substantive), '/ 总异文', len(det))
|
||||
for n in list(substantive)[:25]:
|
||||
line = '; '.join("孙[%s]→维[%s]" % (f['孙'], f['维基']) for f in substantive[n][:5])
|
||||
print('■', n, '|', line[:140])
|
||||
json.dump(substantive, open('02_加工数据/_tmp_实字异文.json', 'w', encoding='utf-8'),
|
||||
ensure_ascii=False, indent=1)
|
||||
print('已写 02_加工数据/_tmp_实字异文.json')
|
||||
@@ -0,0 +1,87 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""05_build_crossref_report.py — 参校本对照报告(初对照)生成
|
||||
输入:参校本对照_药目核对.json(03 脚本产物)+ _tmp_实字异文.json(04 脚本产物)
|
||||
输出:02_加工数据/参校本对照_孙本-维基.md"""
|
||||
import json, os, datetime
|
||||
|
||||
BASE = '/home/songyi/Documents/ai_agent_scraper_study/data/神农本草经'
|
||||
D = json.load(open(os.path.join(BASE, '02_加工数据/参校本对照_药目核对.json'), encoding='utf-8'))
|
||||
SUB = json.load(open(os.path.join(BASE, '02_加工数据/_tmp_实字异文.json'), encoding='utf-8'))
|
||||
|
||||
same = D['同文']
|
||||
diff = D['异文条目']
|
||||
only_sun = D['未对齐_仅孙本']
|
||||
only_wk = D['未对齐_仅维基']
|
||||
|
||||
L = []
|
||||
L.append('# 参校本对照 — 孙本(第一参照本·殆知阁) ⇄ 维基文库(第二参校本) 初步对照')
|
||||
L.append('')
|
||||
L.append('> 性质:**两参校本互校**,双方原文均未改动;差异只登记。本对照供实体书 OCR 底本核对时作为「第三源」线索定位——凡两参校本一致的异文、或两方各有主张的异文,都圈出来供底本定夺。')
|
||||
L.append('> 规整规则(对照镜像,不是改字):① 繁→简单向转换(opencc t2s,仅对照视图)② 去括注(孙本多《御览》括注)③ 去句读(,。、;:)——均不动任何一方的原始文件。')
|
||||
L.append('> 药名对齐用显式异体映射表(如 丹沙=丹砂、朴消=朴硝、慈石=磁石、茈胡=柴胡),映射SRﺎ只用于对照键。')
|
||||
L.append('')
|
||||
today = datetime.date.today().isoformat()
|
||||
L.append('| 项 | 数 |')
|
||||
L.append('|----|----|')
|
||||
L.append('| 孙本药条(合并整理版口径) | 337 |')
|
||||
L.append('| 维基药条 | 359 |')
|
||||
L.append('| 对齐条目(名称可对) | %d |' % (len(same)+len(diff)))
|
||||
L.append('| 两参校本文同 | %d |' % len(same))
|
||||
L.append('| 对齐但文异(含体例差) | %d |' % len(diff))
|
||||
L.append('| 剔除体例差后·实字异文条目 | **%d** |' % len(SUB := json.load(open(os.path.join(BASE, '02_加工数据/_tmp_实字异文.json'), encoding='utf-8'))))
|
||||
L.append('| 未对齐·仅孙本 | %d |' % len(only_sun))
|
||||
L.append('| 未对齐·仅维基 | %d |' % len(only_wk))
|
||||
L.append('')
|
||||
L.append('## 一、未对齐药名清单(四类成因)')
|
||||
L.append('')
|
||||
L.append('### 甲. 繁简异体(同一味药,用字不同——映射表已登记,属**版本体例差**,非内容差)')
|
||||
L.append('')
|
||||
L.append('如:丹沙/丹砂、朴消/朴硝、慈石/磁石、茈胡/柴胡、署豫/山芋系统、委萎/女萎……此层维基作繁体、孙本作简体+宋本用字,属繁简/异体书写差。')
|
||||
L.append('')
|
||||
L.append('### 未登记进映射表·成对残项(待人工归并):')
|
||||
L.append('')
|
||||
sun_left = [n for n in only_sun if n not in ('KT','下经','木','本','石','菌','青','黄','萆','蓬','虫','蓄')]
|
||||
wk_left = only_wk
|
||||
L.append('| 孙本侧 | 维基侧(可能对应) |')
|
||||
L.append('|---------|-------------------|')
|
||||
for s in sun_left:
|
||||
L.append('| %s | (待人工归并) |' % s)
|
||||
for w in wk_left[:20]:
|
||||
L.append('| (待人工归并) | %s |' % w)
|
||||
L.append('')
|
||||
L.append('## 二、同文条目(两层规整后完全一致)[%d 条]' % len(same))
|
||||
L.append('')
|
||||
L.append('、'.join(same))
|
||||
L.append('')
|
||||
L.append('## 三、实字异文[粗筛 %d 条](样本)' % len(SUB))
|
||||
L.append('')
|
||||
L.append('> 注意:初筛自动剔除「治/有毒/无毒/小/微」等体例差;**繁简残留与形近字仍混入**,须人工定夺。每条列出「孙[原文]→维[原文]」。')
|
||||
L.append('')
|
||||
L.append('| 药名 | 差异(孙→维基) |')
|
||||
L.append('|------|-----------------|')
|
||||
for n, frags in list(SUB.items()):
|
||||
cells = ';'.join('孙[%s]→维[%s]' % (f['孙'], f['维基']) for f in frags[:5])
|
||||
L.append('| %s | %s |' % (n, cells.replace('|','\\|')))
|
||||
L.append('')
|
||||
L.append('## 四、全书结构差异(不在药条层面的差异)')
|
||||
L.append('')
|
||||
L.append('1. 孙本有《吴普》曰/《名医》曰/案:三重考据——维基文库全无')
|
||||
L.append('2. 孙本有《御览》括注援引(《太平御览》引……)——维基无')
|
||||
L.append('3. 维基有完整序录13行——孙本序录仅3段+目录行')
|
||||
L.append('4. 维基三品分部按「玉石/草/木/果菜/米穀/蟲獸」——孙本按「卷一/二/三」')
|
||||
L.append('5. 孙本卷三末附七情制使231种+《药对》五条——维基无')
|
||||
L.append('6. 孙本药目数371>365、识别口径338——<strong>总计待底本核对后方能定</strong>')
|
||||
L.append('')
|
||||
L.append('## 五、下一步')
|
||||
L.append('')
|
||||
L.append('1. 未对齐药名人工归并清单→等底本OCR后定字')
|
||||
L.append('2. 实字异文 228 条→等底本 OCR 后逐条定夺')
|
||||
L.append('3. 序录 3 段 vs 13 行文本的取舍——以底本为准')
|
||||
L.append('4. 孙本「KT」占位药条→底本核名')
|
||||
L.append('')
|
||||
|
||||
|
||||
md = '\n'.join(L)
|
||||
md = md.replace('>>', '>')
|
||||
open(os.path.join(BASE, '02_加工数据/参校本对照_孙本-维基.md'), 'w', encoding='utf-8').write(md)
|
||||
print('已写 02_加工数据/参校本对照_孙本-维基.md,行数', md.count('\n')+1, 'md5:', __import__('hashlib').md5(md.encode()).hexdigest())
|
||||
Reference in new issue
Block a user