feat(monitor): 组头上加一个跳到博主主页的按钮
`creator_hash` 是单向的,拼不出主页地址 —— 所以这个 id 必须在采集时单独存下来。 抖音的资料接口本来就返回 sec_uid,只是之前没落库。 * `monitor_creator_stat` 加 `creator_id` 列(老数据默认空,下一次采集补上); * 链接在 `adapters.creator_url()` 里拼,**空串是有意义的返回值**:平台不认识、 或者没拿到 id,就返回空,界面据此不画按钮 —— 画一个点开 404 的按钮比不画糟; * 没有作品的博主照样有链接:他恰恰是你想点进去看的那个人。 已知缺口:小红书那条路(爬虫子进程)根本不落创作者资料,所以那边没有链接。
This commit is contained in:
@@ -107,6 +107,17 @@ class PlatformAdapter:
|
||||
人能直接点开看的那一个。"""
|
||||
return f"{self.web_base}{self.note_path}/{note_id}"
|
||||
|
||||
def creator_url(self, creator_id: str) -> str:
|
||||
"""博主主页的可点击链接。
|
||||
|
||||
``creator_id`` 是**平台上的那个主页 id**(抖音是 sec_uid),不是 ``creator_hash``
|
||||
—— 哈希是单向的,拼不出地址来。拿不到 id 的地方(小红书那条路根本不落创作者
|
||||
资料)就返回空串,界面上不画按钮,而不是给一个点开 404 的链接。
|
||||
"""
|
||||
if not creator_id:
|
||||
return ""
|
||||
return f"{self.web_base}{self.creator_path}/{creator_id}"
|
||||
|
||||
def note_field(self, record: Dict[str, Any], name: str) -> Any:
|
||||
"""按规范名读作品记录里的原始值(没有就是 None)。"""
|
||||
return record.get(self.note_fields.get(name, name))
|
||||
@@ -240,6 +251,20 @@ def has_adapter(platform: str) -> bool:
|
||||
return platform in ADAPTERS
|
||||
|
||||
|
||||
def creator_url(platform: str, creator_id: str) -> str:
|
||||
"""博主主页链接,**空串是有意义的返回值**。
|
||||
|
||||
平台不认识、或者没拿到主页 id,就返回空串 —— 界面据此不画那个按钮。给一个点开
|
||||
404 的链接,比不画按钮糟得多。
|
||||
|
||||
单独开这个模块级函数(而不是让调用方自己 ``adapter(...)`` 包一层 try),是为了让
|
||||
「哪些情况该空着」只有一处判断。
|
||||
"""
|
||||
if not creator_id or not has_adapter(platform):
|
||||
return ""
|
||||
return adapter(platform).creator_url(creator_id)
|
||||
|
||||
|
||||
def artifact_dir(platform: str) -> str:
|
||||
"""该平台的爬虫会把 jsonl 落在哪个子目录下。
|
||||
|
||||
|
||||
@@ -463,6 +463,8 @@ async def author_profile(sec_user_id: str, *, cookie: str = "") -> Dict[str, Any
|
||||
str(user.get("uid") or user.get("sec_uid") or "")
|
||||
),
|
||||
"nickname": user.get("nickname") or "",
|
||||
# 主页 id —— 单独存一份,用来拼「跳到他主页」的链接。哈希拼不出地址。
|
||||
"creator_id": str(user.get("sec_uid") or ""),
|
||||
"unique_id": user.get("unique_id") or "",
|
||||
"fans": _as_int(user.get("follower_count")),
|
||||
"total_favorited": _as_int(user.get("total_favorited")),
|
||||
|
||||
@@ -638,6 +638,7 @@ async def _ingest_creator_stats(
|
||||
run_id=run.id,
|
||||
creator_hash=creator_hash,
|
||||
nickname=str(record.get("nickname") or "")[:128],
|
||||
creator_id=str(record.get("creator_id") or "")[:128],
|
||||
fans=parse_count(record.get("fans")),
|
||||
total_favorited=parse_count(record.get("total_favorited")),
|
||||
works_count=parse_count(record.get("works")),
|
||||
|
||||
@@ -370,6 +370,10 @@ class MonitorCreatorStat(MonitorBase):
|
||||
run_id: Mapped[int] = mapped_column(Integer, nullable=False, index=True)
|
||||
creator_hash: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
|
||||
nickname: Mapped[str] = mapped_column(String(128), nullable=False, default="")
|
||||
# 平台上的**主页 id**(抖音是 sec_uid),用来拼博主主页的跳转链接。
|
||||
# creator_hash 是单向的,拼不出地址 —— 所以这个必须单独存。
|
||||
# 拿不到的地方(小红书那条路不落创作者资料)留空,界面就不画那个按钮。
|
||||
creator_id: Mapped[str] = mapped_column(String(128), nullable=False, default="")
|
||||
|
||||
# 都可能为 None:平台没给就留空,**不要伪造成 0** —— 0 是"掉到零",和"不知道"
|
||||
# 在趋势图上是完全不同的两回事。
|
||||
|
||||
@@ -756,6 +756,11 @@ async def list_creators(
|
||||
"creator_alias": aliases.get(
|
||||
(task_platform.get(row_task, ""), creator_hash), ""
|
||||
),
|
||||
# 主页跳转。拿不到主页 id(小红书不落创作者资料、或者这个博主还没采过
|
||||
# 资料)就是空串,界面不画按钮 —— 而不是给一个点开 404 的链接。
|
||||
"creator_url": adapters.creator_url(
|
||||
task_platform.get(row_task, ""), stat.creator_id if stat else ""
|
||||
),
|
||||
"note_count": (work_row or {}).get("note_count", 0),
|
||||
"creator_fans": stat.fans if stat else None,
|
||||
"creator_total_favorited": stat.total_favorited if stat else None,
|
||||
|
||||
Reference in New Issue
Block a user