weixin.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. """Weixin formal acquisition adapter."""
  2. from __future__ import annotations
  3. import hashlib
  4. from typing import Any, Iterable
  5. from acquisition.content_mode import ARTICLE
  6. from acquisition.platforms.base import PlatformCandidate, PlatformItem, PlatformSearchPage
  7. from acquisition.search import fetch_weixin_detail, search_weixin_page
  8. from core.config import Settings
  9. def _hash(value: str, n: int = 16) -> str:
  10. return hashlib.md5(value.encode("utf-8")).hexdigest()[:n]
  11. class WeixinAdapter:
  12. platform = "weixin"
  13. def search(
  14. self,
  15. query: str,
  16. *,
  17. settings: Settings,
  18. limit: int,
  19. rate_limiter: Any,
  20. ) -> list[PlatformCandidate]:
  21. pages = self.search_pages(
  22. query,
  23. settings=settings,
  24. limit=limit,
  25. rate_limiter=rate_limiter,
  26. max_pages=1,
  27. )
  28. return [candidate for page in pages for candidate in page.candidates]
  29. def search_pages(
  30. self,
  31. query: str,
  32. *,
  33. settings: Settings,
  34. limit: int,
  35. rate_limiter: Any,
  36. max_pages: int = 2,
  37. ) -> Iterable[PlatformSearchPage]:
  38. cursor = "0"
  39. rank = 1
  40. seen: set[str] = set()
  41. for page_index in range(1, max_pages + 1):
  42. page = search_weixin_page(
  43. query,
  44. cursor=cursor,
  45. limit=limit,
  46. settings=settings,
  47. rate_limiter=rate_limiter,
  48. )
  49. candidates: list[PlatformCandidate] = []
  50. for page_rank, row in enumerate(page.rows, start=1):
  51. url = row.get("url") or ""
  52. source_id = _hash(url)
  53. if not url or source_id in seen:
  54. continue
  55. seen.add(source_id)
  56. raw = {
  57. **row,
  58. "page_index": page_index,
  59. "page_rank": page_rank,
  60. "source_cursor": page.cursor,
  61. "request_payload": page.request_payload,
  62. }
  63. candidates.append(
  64. PlatformCandidate(
  65. rank=rank,
  66. platform=self.platform,
  67. source_id=source_id,
  68. url=url,
  69. title=row.get("title") or "",
  70. author=row.get("nick_name") or "",
  71. cover_url=row.get("cover_url") or "",
  72. raw=raw,
  73. )
  74. )
  75. rank += 1
  76. yield PlatformSearchPage(
  77. page_index=page_index,
  78. candidates=candidates,
  79. raw_count=page.raw_count,
  80. cursor=page.cursor,
  81. next_cursor=page.next_cursor,
  82. has_more=page.has_more,
  83. provider=page.provider,
  84. request_payload=page.request_payload,
  85. )
  86. if not page.has_more or not page.next_cursor or page.next_cursor == cursor:
  87. break
  88. cursor = page.next_cursor
  89. def fetch_detail(
  90. self,
  91. candidate: PlatformCandidate,
  92. *,
  93. settings: Settings,
  94. rate_limiter: Any,
  95. ) -> PlatformItem:
  96. body_text, images = fetch_weixin_detail(
  97. candidate.url,
  98. settings=settings,
  99. rate_limiter=rate_limiter,
  100. )
  101. return PlatformItem(
  102. platform=self.platform,
  103. source_id=candidate.source_id or _hash(candidate.url),
  104. url=candidate.url,
  105. content_type="图文",
  106. content_mode=ARTICLE,
  107. title=candidate.title,
  108. author=candidate.author,
  109. body_text=body_text,
  110. image_urls=images or [candidate.cover_url],
  111. raw=candidate.raw,
  112. )