"""Pinterest 爬取封装:把 pinterest_image_capture 的 CLI 逻辑封装成可调用函数,供 UI/节点后台线程调用。 用法: from pinterest_scraper.scraper import scrape_pinterest files = scrape_pinterest("vintage 70s", count=40, save_dir="output/pinterest_ref/US/vintage_70s") 增量续采:每个 save_dir 下维护 .collected_urls.json(该关键词历史已采集 URL 集合)。 重复搜索同一关键词时自动跳过旧图、往下拉采集新图,避免反复下载顶部重复图片。 """ import asyncio import json import os from pathlib import Path from typing import List, Optional, Set from pinterest_scraper.pinterest_image_capture import ( detect_proxy, download_all, get_system_proxy, scrape, ) def _load_collected_urls(save_dir: str) -> Set[str]: """读该关键词历史已采集 URL(.collected_urls.json),不存在/损坏返回空集。""" p = Path(save_dir) / ".collected_urls.json" if not p.exists(): return set() try: data = json.loads(p.read_text(encoding="utf-8")) return {str(u) for u in data if str(u).strip()} except Exception: # noqa: BLE001 return set() def _save_collected_urls(save_dir: str, urls: Set[str]) -> None: """把已采集 URL 集合持久化到 .collected_urls.json(供下次增量续采)。""" try: p = Path(save_dir) / ".collected_urls.json" p.write_text(json.dumps(sorted(urls), ensure_ascii=False), encoding="utf-8") except Exception as e: # noqa: BLE001 print(f"[pinterest] 已采集 URL 持久化失败: {e}") def scrape_pinterest(keyword: str, count: int = 40, save_dir: Optional[str] = None, proxy: Optional[str] = None, headless: bool = False, cdp_url: Optional[str] = None, login_wait: bool = False) -> List[str]: """按关键词爬取 Pinterest 图片并下载到 save_dir,返回下载成功的文件路径列表。 - proxy 为 None 时自动探测(环境变量/系统代理/本地常见端口);空字符串显式禁用。 - login_wait=False(默认):运行时检测到未登录时不阻塞,直接返回空列表(由调用方跳过并告警); login_wait=True:弹出 Chrome 窗口等待手动登录一次,登录态缓存在 pinterest_scraper/.chrome_session,之后自动复用。 - 增量续采:读取 save_dir/.collected_urls.json 历史已采集 URL,滚动时跳过旧图往下拉, 只下载新图,并把新 URL 合并写回(种子词用尽复用同一关键词时自动采集更靠后的新图)。 """ # 代理解析:显式传入 > 自动探测 > 直连 if proxy is None: proxy = detect_proxy() or get_system_proxy() if proxy: print(f"[pinterest] 使用代理: {proxy}") else: print("[pinterest] 未检测到代理,将直连(如无法访问请手动指定代理)") proxy = proxy or None out = save_dir or os.path.join(os.getcwd(), "output", "img", keyword) os.makedirs(out, exist_ok=True) print(f"[pinterest] 关键词: {keyword} | 数量: {count} | 保存目录: {out}") # 跨运行已采集 URL:重复搜索同一关键词时跳过旧图、往下拉续采 known = _load_collected_urls(out) if known: print(f"[pinterest] 该关键词已采集过 {len(known)} 个 URL,将跳过旧图往下拉续采新图") imgs_url = asyncio.run(scrape(keyword, count, headless, proxy, cdp_url, login_wait, known_urls=known)) if not imgs_url: print(f"[pinterest] 未收集到新图片(可能未登录、搜索无结果或该关键词已无新图): {keyword}") return [] asyncio.run(download_all(imgs_url, out, proxy)) # 合并本次新采集 URL 到持久化集合,供下次增量续采 known |= imgs_url _save_collected_urls(out, known) files = [str(p) for p in sorted(Path(out).iterdir()) if p.is_file()] print(f"[pinterest] 完成,本次新增 {len(imgs_url)} 张图片(累计已采集 {len(known)} 个 URL): {keyword}") return files