| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- """Xiaohongshu formal acquisition adapter."""
- from __future__ import annotations
- from typing import Any, Iterable
- from acquisition.content_mode import infer_content_mode
- from acquisition.crawler import fetch_post_detail
- from acquisition.platforms.base import PlatformCandidate, PlatformItem, PlatformSearchPage
- from acquisition.search import search_xiaohongshu_page
- from core.config import Settings
- class XiaohongshuAdapter:
- platform = "xiaohongshu"
- def search(
- self,
- query: str,
- *,
- settings: Settings,
- limit: int,
- rate_limiter: Any,
- ) -> list[PlatformCandidate]:
- pages = self.search_pages(
- query,
- settings=settings,
- limit=limit,
- rate_limiter=rate_limiter,
- max_pages=1,
- )
- return [candidate for page in pages for candidate in page.candidates]
- def search_pages(
- self,
- query: str,
- *,
- settings: Settings,
- limit: int,
- rate_limiter: Any,
- max_pages: int = 2,
- ) -> Iterable[PlatformSearchPage]:
- cursor = ""
- rank = 1
- seen: set[str] = set()
- for page_index in range(1, max_pages + 1):
- page = search_xiaohongshu_page(
- query,
- cursor=cursor,
- content_type=None,
- limit=limit,
- settings=settings,
- rate_limiter=rate_limiter,
- )
- candidates: list[PlatformCandidate] = []
- for page_rank, row in enumerate(page.rows, start=1):
- source_id = row.get("id") or ""
- if not source_id or source_id in seen:
- continue
- seen.add(source_id)
- raw = {
- **row,
- "page_index": page_index,
- "page_rank": page_rank,
- "source_cursor": page.cursor,
- "request_payload": page.request_payload,
- }
- candidates.append(
- PlatformCandidate(
- rank=rank,
- platform=self.platform,
- source_id=source_id,
- url=row.get("url") or "",
- title=row.get("title") or "",
- author=row.get("nick_name") or "",
- cover_url=row.get("cover_url") or "",
- raw=raw,
- )
- )
- rank += 1
- yield PlatformSearchPage(
- page_index=page_index,
- candidates=candidates,
- raw_count=page.raw_count,
- cursor=page.cursor,
- next_cursor=page.next_cursor,
- has_more=page.has_more,
- provider=page.provider,
- request_payload=page.request_payload,
- )
- if not page.has_more or not page.next_cursor or page.next_cursor == cursor:
- break
- cursor = page.next_cursor
- def fetch_detail(
- self,
- candidate: PlatformCandidate,
- *,
- settings: Settings,
- rate_limiter: Any,
- ) -> PlatformItem:
- post = fetch_post_detail(
- candidate.source_id or candidate.url,
- settings=settings,
- rate_limiter=rate_limiter,
- )
- image_urls = post.image_urls or ([candidate.cover_url] if candidate.cover_url else [])
- content_mode = infer_content_mode(
- platform=self.platform,
- content_type=post.content_type,
- body_text=post.body_text,
- image_urls=image_urls,
- video_urls=post.video_urls,
- raw={**candidate.raw, "detail_content_type": post.content_type},
- )
- return PlatformItem(
- platform=self.platform,
- source_id=post.content_id or candidate.source_id,
- url=post.url or candidate.url,
- content_type=post.content_type or "",
- content_mode=content_mode,
- title=post.title or candidate.title,
- author=post.author_name or candidate.author,
- body_text=post.body_text or "",
- image_urls=image_urls,
- video_urls=post.video_urls,
- raw=post.raw if isinstance(post.raw, dict) else {},
- )
|