tap
Back

sohu/user_posts

sohuRead-only

获取搜狐号用户近期发布文章列表(自动滚动加载,直到满足日期截止或到底)

mp.sohu.com
Last 7 days
0
Last 30 days
0
All time
0
sohu/user_posts.js
/* @meta
{
  "name": "sohu/user_posts",
  "description": "获取搜狐号用户近期发布文章列表(自动滚动加载,直到满足日期截止或到底)",
  "domain": "mp.sohu.com",
  "args": {
    "xpt": {"required": true, "description": "用户 xpt 标识(从 URL ?xpt= 参数中提取)"},
    "days": {"required": false, "description": "获取最近 N 天内的文章(默认 90)"}
  },
  "capabilities": ["network"],
  "readOnly": true,
  "example": "bb-browser site sohu/user_posts MzEwNzhiYTEtYTZjNy00ZjMxLTk4YTUtMmQzYzNlODc0NjA4"
}
*/

async function(args) {
  // 兼容完整 URL 或纯 xpt:bb-browser site sohu/user_posts <url_or_xpt>
  const raw = args.xpt || '';
  const xptFromUrl = raw.match(/[?&]xpt=([^&]+)/)?.[1];
  args.xpt = xptFromUrl ? decodeURIComponent(xptFromUrl) : raw;
  if (!args.xpt) return {error: 'Missing argument: xpt'};
  const days = parseInt(args.days) || 90;

  // 等待页面稳定,读取当前页面实际 xpt
  await new Promise(r => setTimeout(r, 1500));
  const currentXpt = new URLSearchParams(location.search).get('xpt');
  // 使用当前页面的实际 xpt(collect.js 已通过 bbOpen 确保页面正确)
  // 单独命令行调用时,请先执行 bb-browser open <url> 再执行此命令
  const effectiveXpt = currentXpt || args.xpt;

  // 解析时间文本 → "YYYY-MM-DD"
  function parseDate(text) {
    const now = new Date();
    const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
    const pad = n => String(n).padStart(2, '0');
    const fmt = d => `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())}`;
    if (!text) return null;
    if (text.includes('小时前') || text.includes('分钟前') || text.includes('刚刚')) {
      return fmt(today);
    }
    if (text.includes('昨天')) {
      const d = new Date(today); d.setDate(d.getDate() - 1);
      return fmt(d);
    }
    if (text.includes('前天')) {
      const d = new Date(today); d.setDate(d.getDate() - 2);
      return fmt(d);
    }
    const nDays = text.match(/(\d+)天前/);
    if (nDays) {
      const d = new Date(today); d.setDate(d.getDate() - parseInt(nDays[1]));
      return fmt(d);
    }
    const m = text.match(/(\d{4})\.(\d{2})\.(\d{2})/);
    if (m) return `${m[1]}-${m[2]}-${m[3]}`;
    return null;
  }

  // 计算截止日期字符串(YYYY-MM-DD)
  const cutoff = new Date();
  cutoff.setDate(cutoff.getDate() - days);
  const pad = n => String(n).padStart(2, '0');
  const cutoffStr = `${cutoff.getFullYear()}-${pad(cutoff.getMonth()+1)}-${pad(cutoff.getDate())}`;

  // 滚动循环:加载文章直到停止条件
  const seenIds = new Set();
  let reachedEnd = false;
  let noNewCount = 0;

  while (true) {
    const prevSize = seenIds.size;
    let hitCutoff = false;

    for (const card of document.querySelectorAll('.TPLImageTextFeedItem')) {
      const a = card.querySelector('a[href*="sohu.com/a/"]');
      if (!a) continue;
      const match = a.href.match(/sohu\.com\/a\/(\d+)/);
      if (!match || seenIds.has(match[1])) continue;

      const extraItems = [...(card.querySelector('.extra-info-list')?.querySelectorAll('.extra-info-item') || [])]
        .map(el => el.textContent.trim());
      const dateStr = parseDate(extraItems[0] || '');

      if (dateStr && dateStr < cutoffStr) { hitCutoff = true; break; }
      seenIds.add(match[1]);
    }

    if (hitCutoff) break;
    if (document.body.innerText.includes('暂无更多内容')) { reachedEnd = true; break; }

    if (seenIds.size === prevSize) {
      noNewCount++;
      if (noNewCount >= 2) { reachedEnd = true; break; }
    } else {
      noNewCount = 0;
    }

    window.scrollBy(0, 3000);
    await new Promise(r => setTimeout(r, 1500));
  }

  // 二次遍历:提取完整字段(按 DOM 顺序,遇到超期截止)
  const articles = [];
  const seenFinal = new Set();

  for (const card of document.querySelectorAll('.TPLImageTextFeedItem')) {
    const a = card.querySelector('a[href*="sohu.com/a/"]');
    if (!a) continue;
    const match = a.href.match(/sohu\.com\/a\/(\d+)_(\d+)/);
    if (!match || seenFinal.has(match[1])) continue;

    const extraItems = [...(card.querySelector('.extra-info-list')?.querySelectorAll('.extra-info-item') || [])]
      .map(el => el.textContent.trim());
    const publishedAt = parseDate(extraItems[0] || '');
    if (publishedAt && publishedAt < cutoffStr) break;

    seenFinal.add(match[1]);

    const title = (card.querySelector('.item-text-content-title')?.textContent || '')
      .replace(/\s+/g, ' ').trim().slice(0, 100);
    const img = a.querySelector('img');
    const cover = (img?.src || img?.dataset?.src || '').replace(/^\/\//, 'https://');
    const reads = parseInt(extraItems[1]) || 0;
    const comments = parseInt(extraItems[2]) || 0;

    articles.push({
      id: match[1],
      title,
      url: 'https://www.sohu.com/a/' + match[1] + '_' + match[2],
      cover,
      published_at: publishedAt,
      reads,
      comments,
    });
  }

  return {
    xpt: effectiveXpt,
    days,
    reached_end: reachedEnd,
    count: articles.length,
    articles,
  };
}
Updated Apr 14, 2026Created Apr 14, 2026SHA-256: 0b472db1c63f