test_crawler.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. """M2 离线测试:用 5 个真实 capture 验证 parse_detail_response 字段映射。"""
  2. from __future__ import annotations
  3. import json
  4. from pathlib import Path
  5. import pytest
  6. from acquisition.crawler import (
  7. PLATFORMS,
  8. detect_platform_and_id,
  9. parse_content_id,
  10. parse_detail_response,
  11. )
  12. FIXTURES = Path(__file__).parent / "fixtures"
  13. # content_id -> (期望作者, 标题里应出现的子串)
  14. EXPECTED = {
  15. "67e4bdf50000000006028a59": ("海狸教自媒体运营", "短视频脚本"),
  16. "698481e1000000000a02a7c1": ("Irvin是个编剧(接稿版)", "叙事结构"),
  17. "67e2e39b0000000003028ff0": ("拾意", "剧本创作"),
  18. "699308fa0000000016009697": ("方圆的增长飞轮", "选题"),
  19. "680659e8000000001a007a11": ("故事设计原理拆解学习"[:2], "故事设计"),
  20. }
  21. def _load(content_id: str) -> dict:
  22. return json.loads((FIXTURES / f"xhs_case_{content_id}.json").read_text("utf-8"))
  23. @pytest.mark.parametrize("content_id", list(EXPECTED))
  24. def test_parse_detail_fields(content_id: str):
  25. # 重抓的 xhs_case_*.json 即原始响应 {code,msg,data},无 response 包裹
  26. response = _load(content_id)
  27. post = parse_detail_response(response, fallback_content_id=content_id)
  28. assert post.id == f"xhs_{content_id}"
  29. assert post.platform == "xiaohongshu"
  30. assert post.content_id == content_id
  31. assert content_id in post.url
  32. assert post.title, "title 不应为空"
  33. _, title_sub = EXPECTED[content_id]
  34. assert title_sub in post.title
  35. assert post.raw.get("code") == 0
  36. def test_authors():
  37. for cid, (author, _) in EXPECTED.items():
  38. post = parse_detail_response(_load(cid), fallback_content_id=cid)
  39. if cid in ("67e4bdf50000000006028a59", "698481e1000000000a02a7c1",
  40. "67e2e39b0000000003028ff0", "699308fa0000000016009697"):
  41. assert post.author_name == author
  42. def test_shiyi_posts_text_sparse():
  43. """拾意两条正文几乎只有话题串——M3 多模态提取存在的理由。"""
  44. for cid in ("67e2e39b0000000003028ff0", "680659e8000000001a007a11"):
  45. post = parse_detail_response(_load(cid), fallback_content_id=cid)
  46. stripped = post.body_text.replace("#", "").replace("话题", "").strip()
  47. assert len(stripped) < 40, f"{cid} body_text 应很稀疏: {post.body_text!r}"
  48. def test_parse_content_id_from_url():
  49. url = ("https://www.xiaohongshu.com/explore/67e4bdf50000000006028a59"
  50. "?xsec_token=ABC=&xsec_source=pc_like")
  51. assert parse_content_id(url) == "67e4bdf50000000006028a59"
  52. assert parse_content_id("67e4bdf50000000006028a59") == "67e4bdf50000000006028a59"
  53. @pytest.mark.parametrize("url,platform,cid", [
  54. # 长链
  55. ("https://www.xiaohongshu.com/explore/67e4bdf50000000006028a59?xsec_token=A",
  56. "xiaohongshu", "67e4bdf50000000006028a59"),
  57. ("https://www.xiaohongshu.com/discovery/item/680659e8000000001a007a11",
  58. "xiaohongshu", "680659e8000000001a007a11"),
  59. ("https://www.douyin.com/video/7612631899479648866", "douyin", "7612631899479648866"),
  60. ("https://www.douyin.com/search/x?modal_id=7612631899479648866&type=general",
  61. "douyin", "7612631899479648866"),
  62. ("https://www.gifshow.com/fw/photo/3xepqgwddgbickc", "kuaishou", "3xepqgwddgbickc"),
  63. ("https://www.kuaishou.com/short-video/3xepqgwddgbickc", "kuaishou", "3xepqgwddgbickc"),
  64. ("https://www.bilibili.com/video/BV1qd4y1x76w/?spm_id_from=333", "bilibili", "BV1qd4y1x76w"),
  65. # 裸 id 兜底
  66. ("67e4bdf50000000006028a59", "xiaohongshu", "67e4bdf50000000006028a59"),
  67. ("7612631899479648866", "douyin", "7612631899479648866"),
  68. ("BV1qd4y1x76w", "bilibili", "BV1qd4y1x76w"),
  69. ])
  70. def test_detect_platform_and_id(url, platform, cid):
  71. assert detect_platform_and_id(url) == (platform, cid)
  72. def test_parse_detail_platform_prefix():
  73. """同一 parse 函数 + platform 参数 → 平台名与 id 前缀正确。"""
  74. response = _load("67e4bdf50000000006028a59")
  75. for platform, expected_prefix in [(p, c["prefix"]) for p, c in PLATFORMS.items()]:
  76. post = parse_detail_response(response, platform=platform,
  77. fallback_content_id="67e4bdf50000000006028a59")
  78. assert post.platform == platform
  79. assert post.id == f"{expected_prefix}_67e4bdf50000000006028a59"