fix: 给采集结果填 content/summary(主站资源库对空 content 拒收)

- ScrapedItem 加 content/summary 字段;to_ingest_dict 输出它们,content 为空时回退用 title
- scraper 新增 _extract_description:抓 meta/og/jsonld 描述 + 详情面板文本 + 标签,拼成 content
This commit is contained in:
huyue
2026-05-12 17:17:12 +08:00
parent 497fae99c6
commit 907af7684e
2 changed files with 56 additions and 1 deletions
+7
View File
@@ -20,6 +20,9 @@ class ScrapedItem:
cover_image: str | None = None
# 视频 URL(上传 MinIO 后改为 MinIO 地址,否则是 xchina 限时签名 m3u8)
video_url: str | None = None
# 简介 / 正文(主站资源库要求 content 非空才入库;为空时回退用 title)
summary: str | None = None
content: str | None = None
# 列表页页码(单详情页直采时为 1)
page_no: int = 1
# xchina 详情页里的 id 段;作 MinIO object key 前缀
@@ -36,6 +39,8 @@ class ScrapedItem:
if self.video_id:
extra_data["video_id"] = self.video_id
extra_data.update(self.extra or {})
# 主站 ingest 对空 content 直接拒收 —— 兜底用 title,确保非空
content = (self.content or "").strip() or (self.title or "").strip() or self.source_url
return {
"source_type": "video",
"source_name": self.source_name,
@@ -43,6 +48,8 @@ class ScrapedItem:
"content_type": "video",
"title": self.title,
"cover_image": self.cover_image,
"summary": (self.summary or None),
"content": content,
"video_url": self.video_url,
"extra_data": extra_data,
}
+49 -1
View File
@@ -324,13 +324,16 @@ class XChinaScraper:
await self._assert_not_blocked(page)
await self._handle_interstitial_ad(page, detail_url)
title = await self._extract_title(page)
summary, content = await self._extract_description(page, title)
cover_url = await self._find_cover_url(page)
video_url = await self._find_video_url(page, media_urls)
self.log(f"采集: {title} | cover={'有' if cover_url else '无'} | video={video_url or '无'}")
self.log(f"采集: {title} | cover={'有' if cover_url else '无'} | video={video_url or '无'} | content={len(content or '')}字")
from .media import extract_video_id
return ScrapedItem(
source_url=detail_url,
title=title,
summary=summary,
content=content,
cover_image=cover_url,
video_url=video_url,
page_no=page_no,
@@ -458,6 +461,51 @@ class XChinaScraper:
low = title.lower()
return not any(w.lower() in low for w in bad)
# ---- description / content -------------------------------------------
async def _extract_description(self, page, title: str) -> tuple[str | None, str]:
"""返回 (summary, content)。content 保证非空(实在没有就用 title)——
主站资源库 ingest 对空 content 直接拒收。"""
data = await page.evaluate(
"""() => {
function clean(t){ return (t || '').replace(/\\s+/g,' ').trim(); }
const out = {};
const m = document.querySelector('meta[name="description"], meta[property="og:description"], meta[name="twitter:description"]');
out.meta_desc = clean(m && m.getAttribute('content'));
for (const s of document.querySelectorAll('script[type="application/ld+json"]')) {
try { const d = JSON.parse(s.textContent||'null'); const rows = Array.isArray(d)?d:[d];
for (const r of rows) { if (r && r.description) { out.jsonld_desc = clean(r.description); break; } } } catch(_){}
}
// 影片信息 / 详情面板里的文本(键值对、简介等)
const panel = document.querySelector('.video-detail') || document.querySelector('.tab-content.active') || document.querySelector('.tab-content');
out.panel = panel ? clean(panel.innerText).slice(0, 4000) : '';
// 标签
const tags = [];
for (const a of document.querySelectorAll('.tags a, .tag a, a[href*="/tags/"], .video-detail .item a')) {
const t = clean(a.innerText); if (t && t.length<=20 && !tags.includes(t)) tags.push(t);
}
out.tags = tags.slice(0, 30);
return out;
}"""
)
meta_desc = (data.get("meta_desc") or "").strip()
jsonld_desc = (data.get("jsonld_desc") or "").strip()
panel = (data.get("panel") or "").strip()
tags = data.get("tags") or []
# content: 优先详情面板文本(信息量最大),否则 og/jsonld 描述,再否则 标签拼接,最后 title
parts: list[str] = []
body = panel or jsonld_desc or meta_desc
if body:
parts.append(body)
if tags:
parts.append("标签: " + " ".join(tags))
content = "\n".join(parts).strip()
if not content:
content = title
# summary: og/jsonld 描述,或 content 头一截
summary = (meta_desc or jsonld_desc or content)[:300].strip() or None
return summary, content
# ---- cover -----------------------------------------------------------
# 明显不是封面的图: 站点 logo / 占位图 / 图标 / 广告