初始提交:抖音视频批量采集流水线
App 端免签名接口(设备注册 + detail + CDN 直链),一体化流水线 下载→抽音→传COS→记录→删本地,AI 作品四档判定,四份文档: README(使用指南)、PROGRESS(批次进度)、RE(逆向全过程)、AI(判定规则)。 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
85
douyin/urls.py
Normal file
85
douyin/urls.py
Normal file
@@ -0,0 +1,85 @@
|
||||
"""链接解析:把用户随手粘的东西还原成 aweme_id。
|
||||
|
||||
支持的输入形态:
|
||||
7xxxxxxxxxxxxxxxxxx 裸 id
|
||||
https://v.douyin.com/xxxxxxx/ 分享短链(需要一次网络跳转)
|
||||
4.53 复制打开抖音... https://v.douyin.com/xxx/ ... 整段分享口令
|
||||
https://www.douyin.com/video/7xxxxxx
|
||||
https://www.douyin.com/note/7xxxxxx 图文
|
||||
https://www.douyin.com/?modal_id=7xxxxxx
|
||||
https://www.iesdouyin.com/share/video/7xxxxxx/
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from typing import Optional
|
||||
|
||||
import requests
|
||||
|
||||
# 从一段中文分享口令里把 url 抠出来
|
||||
URL_RE = re.compile(r"https?://[-\w./?=&%#:]+")
|
||||
|
||||
SHORT_HOSTS = ("v.douyin.com", "v.iesdouyin.com")
|
||||
|
||||
# 按优先级尝试的 id 提取规则
|
||||
ID_PATTERNS = (
|
||||
re.compile(r"/(?:video|note|slides|share/video|share/note)/(\d{15,25})"),
|
||||
re.compile(r"[?&](?:modal_id|aweme_id|item_ids|vid)=(\d{15,25})"),
|
||||
)
|
||||
|
||||
BARE_ID_RE = re.compile(r"^\d{15,25}$")
|
||||
|
||||
|
||||
class ParseError(ValueError):
|
||||
"""输入里找不到可用的 aweme_id。"""
|
||||
|
||||
|
||||
def _first_url(text: str) -> Optional[str]:
|
||||
m = URL_RE.search(text)
|
||||
return m.group(0) if m else None
|
||||
|
||||
|
||||
def _extract_id(url: str) -> Optional[str]:
|
||||
for pat in ID_PATTERNS:
|
||||
m = pat.search(url)
|
||||
if m:
|
||||
return m.group(1)
|
||||
return None
|
||||
|
||||
|
||||
def resolve_short_link(url: str, session: requests.Session, timeout: float = 15.0) -> str:
|
||||
"""跟随 v.douyin.com 短链跳转,返回落地长链。"""
|
||||
resp = session.get(url, timeout=timeout, allow_redirects=True)
|
||||
return resp.url
|
||||
|
||||
|
||||
def extract_aweme_id(raw: str, session: Optional[requests.Session] = None) -> str:
|
||||
"""从任意输入中解析出 aweme_id,必要时联网还原短链。"""
|
||||
raw = raw.strip()
|
||||
if BARE_ID_RE.match(raw):
|
||||
return raw
|
||||
|
||||
url = _first_url(raw)
|
||||
if not url:
|
||||
raise ParseError(f"输入里没找到链接也不是纯数字 id: {raw[:80]!r}")
|
||||
|
||||
aid = _extract_id(url)
|
||||
if aid:
|
||||
return aid
|
||||
|
||||
if any(h in url for h in SHORT_HOSTS):
|
||||
if session is None:
|
||||
session = requests.Session()
|
||||
session.headers["User-Agent"] = (
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) "
|
||||
"AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 "
|
||||
"Mobile/15E148 Safari/604.1"
|
||||
)
|
||||
final = resolve_short_link(url, session)
|
||||
aid = _extract_id(final)
|
||||
if aid:
|
||||
return aid
|
||||
raise ParseError(f"短链 {url} 跳转到 {final},但里面没有 aweme_id")
|
||||
|
||||
raise ParseError(f"无法从 {url} 中解析出 aweme_id")
|
||||
Reference in New Issue
Block a user