probe.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. """
  2. 多源数据探头 — 测试各药品数据源的可用性
  3. """
  4. import asyncio
  5. import httpx
  6. import re
  7. from bs4 import BeautifulSoup
  8. HEADERS = {
  9. "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/125.0.0.0 Safari/537.36",
  10. "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
  11. "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
  12. }
  13. async def probe(url, name, method="GET", json_data=None, timeout=15):
  14. """探测一个数据源"""
  15. try:
  16. async with httpx.AsyncClient(timeout=timeout, follow_redirects=True) as c:
  17. if method == "POST":
  18. resp = await c.post(url, headers=HEADERS, json=json_data)
  19. else:
  20. resp = await c.get(url, headers=HEADERS)
  21. size = len(resp.text)
  22. status = resp.status_code
  23. snippet = resp.text[:200].replace("\n", " ").strip()
  24. # 检查是否有验证码/反爬
  25. anticrawl = any(
  26. k in resp.text.lower()
  27. for k in ["captcha", "验证码", "滑块", "verify", "forbidden", "403", "blocked", "请滑动"]
  28. )
  29. flag = "⚠ 反爬" if anticrawl else "✅" if status == 200 else f"❌ {status}"
  30. has_drug = bool(re.search(r"阿莫西林|药品|批号|标准", resp.text)) if status == 200 else False
  31. print(f"{flag} {name}")
  32. print(f" URL: {url}")
  33. print(f" {status} | {size:,} bytes | 含药品信息: {has_drug}")
  34. print(f" {snippet}\n")
  35. return {"ok": status == 200 and not anticrawl, "size": size, "has_drug": has_drug}
  36. except Exception as e:
  37. print(f"❌ {name}: {str(e)[:100]}\n")
  38. return {"ok": False, "size": 0, "has_drug": False}
  39. async def main():
  40. print("═" * 60)
  41. print(" 数据源探测报告")
  42. print("═" * 60 + "\n")
  43. sources = [
  44. # 1. NMPA 公告列表(静态页面)
  45. ("NMPA 最新公告", "https://www.nmpa.gov.cn/xxgk/ggtg/index.html"),
  46. # 2. NMPA 药品查询 API(可能存在的 JSON 接口)
  47. ("NMPA 药品查询 API (试探)",
  48. "https://www.nmpa.gov.cn/datasearch/data/nmpa/search.json",
  49. "POST",
  50. {"category": "ps", "condition": "阿莫西林", "pageSize": 5, "pageNum": 1}),
  51. # 3. 药智网(公开信息)
  52. ("药智网-阿莫西林", "https://www.yaozh.com/product/103009.html"),
  53. # 4. 中国药典在线(公开)
  54. ("药典在线", "http://www.drugfuture.com/standard/"),
  55. # 5. 药智数据 API(公开试用)
  56. ("药智数据 search", "https://db.yaozh.com/drugcompatible/search"),
  57. # 6. 国家药品标准查询
  58. ("药品标准查询", "https://www.nifdc.org.cn/nifdc/xxgk/bzxx/index.html"),
  59. # 7. 丁香园用药助手
  60. ("丁香园用药助手", "https://drugs.dxy.cn/search/index.htm"),
  61. # 8. 药物在线(drugfuture)具体药品
  62. ("药物在线-阿莫西林", "http://www.drugfuture.com/standard/search.aspx?key=%B0%A2%C4%AA%CE%F7%C1%D6"),
  63. # 9. 百度百科(结构化药品信息)
  64. ("百度百科-阿莫西林", "https://baike.baidu.com/item/%E9%98%BF%E8%8E%AB%E8%A5%BF%E6%9E%97"),
  65. ]
  66. results = []
  67. for args in sources:
  68. name = args[0]
  69. url = args[1]
  70. method = args[2] if len(args) > 2 else "GET"
  71. json_data = args[3] if len(args) > 3 else None
  72. r = await probe(url, name, method, json_data)
  73. results.append((name, r))
  74. print("═" * 60)
  75. print(" 总结")
  76. print("═" * 60)
  77. ok_sources = [(n, r) for n, r in results if r["ok"] and r["has_drug"]]
  78. partial = [(n, r) for n, r in results if r["ok"] and not r["has_drug"]]
  79. failed = [(n, r) for n, r in results if not r["ok"]]
  80. print(f"\n✅ 可用(含药品数据): {len(ok_sources)}")
  81. for n, r in ok_sources:
  82. print(f" - {n} ({r['size']:,} bytes)")
  83. print(f"\n🟡 可访问但无药品数据: {len(partial)}")
  84. for n, r in partial:
  85. print(f" - {n} ({r['size']:,} bytes)")
  86. print(f"\n❌ 不可用: {len(failed)}")
  87. for n, r in failed:
  88. print(f" - {n}")
  89. print(f"\n推荐优先采集: ", end="")
  90. if ok_sources:
  91. print(", ".join(n for n, _ in ok_sources[:3]))
  92. else:
  93. print("无可用数据源,建议手动整理药典 PDF 或购买第三方 API")
  94. if __name__ == "__main__":
  95. asyncio.run(main())