#!/usr/bin/env python3 """后台批量维基采集,从 drug_index.json 循环取药""" import json, time, httpx, os, sys from pathlib import Path from bs4 import BeautifulSoup from urllib.parse import quote PROJECT = Path(__file__).resolve().parent.parent.parent os.chdir(PROJECT) DATA_DIR = PROJECT / "data-pipeline" / "data" H = {"User-Agent": "PharmacopoeiaAI/1.0 (ResearchBot) Mozilla/5.0", "Accept-Language": "zh-CN,zh;q=0.9"} SECTION_MAP = { "性状": ["性状","外观","物理"], "适应症": ["适应","用途","主治","医疗用途","临床应用"], "用法与用量": ["用法","用量","剂量","给药","给药方式"], "禁忌": ["禁忌","禁用"], "不良反应": ["不良反应","副作用","不良事件"], "注意事项": ["注意","警告","谨慎"], "药理": ["药理","药效","作用机制","药物代谢","药物动力学","药代"], "药物相互作用": ["交互作用","药物相互"], "贮藏": ["贮藏","储存","保存"], } def parse_sections(html): soup = BeautifulSoup(html, "lxml") body = soup.find("div", class_="mw-parser-output") if not body: return {"概述": ""} overview_ps = [] for elem in body.children: if elem.name == "div" and "mw-heading" in (elem.get("class") or []): break if elem.name == "p": t = elem.get_text(" ", strip=True) if len(t) > 15: overview_ps.append(t) sections = {"概述": " ".join(overview_ps)[:2000]} for h_div in body.find_all("div", class_=lambda c: c and "mw-heading" in c): span = h_div.find("span", class_="mw-headline") heading = (span.get_text() if span else h_div.get_text()).replace("[编辑]","").strip() if len(heading) < 2 or len(heading) > 60: continue if any(s in heading for s in ["参见","参考","外部","延伸","来源","引用","导航","链接","注释"]): continue matched_key = None for std_key, patterns in SECTION_MAP.items(): if any(p in heading for p in patterns): matched_key = std_key; break texts = []; nxt = h_div.find_next_sibling() while nxt: if nxt.name == "div" and "mw-heading" in (nxt.get("class") or []): break if nxt.name in ("p","ul","ol"): t = nxt.get_text(" ", strip=True) if len(t) > 10: texts.append(t) nxt = nxt.find_next_sibling() text = " ".join(texts)[:3000] if text: sections[matched_key or heading] = text return sections # 读索引 with open(DATA_DIR / "drug_index.json") as f: drugs = json.load(f) # 只取化学药+生物制品,跳过中药 candidates = [d for d in drugs if d["volume"] in (2, 3)] out_dir = DATA_DIR / "wiki_drugs" out_dir.mkdir(parents=True, exist_ok=True) already = set(f.stem for f in out_dir.glob("*.json")) total = 0; ok = 0 client = httpx.Client(timeout=20, follow_redirects=True) print(f"🚀 启动采集: {len(candidates)} 候选, 已采 {len(already)}, {len(candidates)-len(already)} 待采\n") for drug in candidates: name = drug["name"] safe = name.replace("/","_").replace(":","_") if safe in already: continue total += 1 if total > 50: break # 首轮限量50个 try: r = client.get(f"https://zh.wikipedia.org/wiki/{quote(name)}", headers=H) if r.status_code != 200: print(f" [{total}] ❌ {name} HTTP{r.status_code}") time.sleep(2); continue sections = parse_sections(r.text) if len(sections) <= 1: print(f" [{total}] ⚠️ {name} (空)") time.sleep(2); continue entry = {"drug_id":f"WIKI-{name}","name":name,"sections":sections, "source":{"version":"维基百科","volume":f"https://zh.wikipedia.org/wiki/{quote(name)}","page":"线上"}} with open(out_dir/f"{safe}.json","w") as f: json.dump(entry, f, ensure_ascii=False, indent=2) ok += 1 print(f" [{total}] ✅ {name} ({len(sections)}段)") time.sleep(3) if ok % 15 == 0 and ok > 0: print(f" ⏸ 暂停45s...\n"); time.sleep(45) except Exception as e: print(f" [{total}] ❌ {name}: {e}") time.sleep(1) # 合并入库 merged = [] for f in out_dir.glob("*.json"): entry = json.load(open(f)) if len(entry["sections"]) > 1: entry["category"] = "化学药" merged.append(entry) Path(DATA_DIR / "wiki_merged.json").write_text(json.dumps(merged, ensure_ascii=False, indent=2)) print(f"\n✅ 完成: {ok} 新增 / {total} 处理, 合并 {len(merged)} 条 → wiki_merged.json") client.close()