prezis/scraperx
GitHub: prezis/scraperx
一个无需 API 密钥的通用社交媒体与视频抓取工具,专注于真实性验证、冒充检测与跨平台视频发现。
Stars: 1 | Forks: 0
# ScraperX
**通用抓取 + 视频智能分析,无需 API 密钥。**
[](https://www.python.org/downloads/)
[](LICENSE)
[](https://github.com/prezis/scraperx/actions/workflows/ci.yml)
[](CHANGELOG.md)
ScraperX 抓取社交媒体帖子、转录视频并验证真实性——无需 API 密钥或账户凭证。基于标准库,提供可选的感知图像哈希、网页抓取辅助和 GPU 加速语音转文本功能。
## 功能
- **X / Twitter** — 推文、线程、资料、搜索。备用链(FxTwitter → vxTwitter → yt-dlp → oEmbed)在任一端点失效时保持数据流动。
- **YouTube 转录** — 自动字幕,支持 `faster-whisper`(GPU)或 `whisper`(CLI)作为回退。
- **Vimeo 转录**(1.3.0 新功能)— `oembed` + 播放器配置 + 创作者上传的 VTT 轨道,回退至 yt-dlp + whisper。
- **视频发现**(新增)— 在任意网页中扫描 6 个提供商(YouTube、Vimeo、Wistia、JWPlayer、Brightcove、HTML5)嵌入的视频。
- **线程真实性验证**(新增)— 对重构线程进行 4 属性形式化检查:`same_conversation`、`single_author`(数值 ID)、`chronological`、`no_interpolation`。
- **冒充检测**(新增)— 使用 pHash 8×8(64 位 DCT 感知哈希)的头像匹配器,配合 SQLite 缓存与滚动窗口注册表。捕捉盗用受害者头像并使用山寨账号的骗子。
- **诈骗内容检测** — 加密货币赠品短语、钱包地址、短链接域名、表情符号垃圾信息。
- **Token 提取** — `$CASHTAG` 提及与已知 Solana 代币。
- **SQLite 持久化** — 推文、资料、提及、头像哈希、搜索缓存。
为何不需要 API 密钥?官方 API 昂贵、限流且不稳定。ScraperX 依赖公共端点(oEmbed、FxTwitter、vxTwitter、syndication、yt-dlp),无需认证壁垒。
## 安装
```
pip install git+https://github.com/prezis/scraperx.git
```
尚未在 PyPI 上提供;通过 GitHub 安装。
或克隆并以可编辑模式安装:
```
git clone https://github.com/prezis/scraperx.git
cd scraperx
pip install -e .
```
### 可选扩展
| 扩展 | 安装内容 | 功能 |
|---|---|---|
| `[vision]` | `imagehash>=4.3`, `Pillow>=10.0` | 感知哈希头像匹配(缺失时回退至 SHA256) |
| `[video-discovery]` | `beautifulsoup4>=4.12` | 更健壮的 HTML 解析,用于 `discover_videos` |
| `[whisper]` | `faster-whisper>=1.0` | GPU 加速转录(比 openai-whisper 在 CPU 上快 4 倍) |
| `[twscrape]` | `twscrape>=0.12` | 可选的账户支持 twscrape 后端 |
组合安装:
```
pip install "scraperx[vision,video-discovery,whisper] @ git+https://github.com/prezis/scraperx.git"
```
系统工具(可选):`yt-dlp` 用于 Vimeo/Whisper 路径的音频下载;`whisper` CLI 作为 `faster-whisper` 未安装时的回退。
## 快速开始
### CLI
```
scraperx https://x.com/user/status/123456789 # scrape a tweet
scraperx https://x.com/user/status/123 --thread # full thread
scraperx @elonmusk # profile
scraperx search "Meteora DLMM" --limit 10 # search (DDG + FxTwitter)
scraperx https://youtube.com/watch?v=dQw4w9WgXcQ # YouTube transcript
scraperx https://vimeo.com/76979871 # Vimeo transcript
scraperx discover https://some-company.com/tour # find embedded videos
```
### Python
```
from scraperx import XScraper, VimeoScraper, discover_videos, check_thread_authenticity
scraper = XScraper()
tweet = scraper.get_tweet("https://x.com/user/status/1234567890")
print(f"{tweet.author_handle}: {tweet.text}")
print(f" reply={tweet.is_reply} quote={tweet.is_quote}")
print(f" author verified={tweet.author_verified} ({tweet.author_verified_type})")
print(f" joined={tweet.author_joined} followers={tweet.author_followers}")
vimeo = VimeoScraper()
result = vimeo.get_transcript("https://vimeo.com/76979871")
print(result.transcript[:500])
refs = discover_videos("https://some-blog.example.com/post")
for v in refs:
print(f"{v.provider}: {v.canonical_url}")
```
## 架构
```
URL or @handle or query
│
▼
┌───────────────────────────┐
│ __main__.py CLI router │
└───────────────────────────┘
┌────────┬─────────┬─────────┬─────────┬──────────┬──────────┐
▼ ▼ ▼ ▼ ▼ ▼ ▼
Tweet Profile Thread YouTube Vimeo Discover Search
│ │ │ │ │ │ │
scraper.py profile thread.py yt_sc.. vimeo_sc.. disco... search.py
│ │ │ │ │ │ │
Fallback Fx+synd walk up captions oEmbed + regex+bs4 DDG+Fx
chain timeline (Fx) + → whisper config scan enrich
┌──────┐ walk JSON
│ Fx │ down │
│ vx │ (synd+DDG) ▼
│yt-dlp│ text_tracks
│oembed│ → whisper
└──────┘
\ │ / \ / │
▼ ▼ ▼ ▼ ▼ │
┌────────────────────────────┐ │
│ impersonation.py │ │
│ • handle typosquat │ │
│ • scam content regex │ │
│ • AvatarMatcher (pHash) │ │
│ • VerifiedAvatarRegistry │ │
└────────────────────────────┘ │
│ │
▼ │
┌──────────────────┐ │
│ authenticity.py │ │
│ 4-property check│ │
└──────────────────┘ │
│ │
▼ ▼
┌──────────────────────────────────┐
│ social_db.py (SQLite) │
│ tweets · profiles · mentions │
│ avatar_hash · verified_avatars │
└──────────────────────────────────┘
```
## 功能指南
### 1. 推文抓取 — 1.3.0 的 21 个新字段
```
from scraperx import XScraper
scraper = XScraper()
t = scraper.get_tweet("https://x.com/user/status/123")
# Core (existed pre-1.3.0)
t.id, t.text, t.author_handle, t.likes, t.retweets, t.views, t.media_urls, t.quoted_tweet
# NEW — reply/quote/thread context
t.is_reply, t.in_reply_to_tweet_id, t.in_reply_to_handle, t.in_reply_to_author_id
t.is_quote, t.conversation_id
# NEW — temporal + locale
t.created_at, t.created_timestamp, t.lang, t.possibly_sensitive, t.source_client
# NEW — community/note flags
t.is_note_tweet, t.is_community_note_marked
# NEW — author trust signals
t.author_verified, t.author_verified_type # "blue" | "business" | "government"
t.author_affiliation # org-linked badge dict
t.author_followers, t.author_following
t.author_joined # RFC 2822 — account age, strong scam signal
t.author_protected, t.is_pinned
```
全部向后兼容 — 每个新字段都有安全默认值。
### 2. 线程重构 + 真实性验证
```
from scraperx import get_thread, check_thread_authenticity
thread = get_thread("https://x.com/user/status/123456")
for t in thread.all_tweets:
print(t.text)
auth = check_thread_authenticity(thread)
print(f"Authentic: {auth.is_authentic}")
print(f" same conversation: {auth.same_conversation}")
print(f" single author: {auth.single_author}")
print(f" chronological: {auth.chronological}")
print(f" no interpolation: {auth.no_interpolation}")
if auth.reasons:
for r in auth.reasons:
print(f" ↳ {r}")
```
**形式化真实性属性:**
1. `same_conversation` — 所有推文共享根推文的 `conversation_id`
2. `single_author` — 所有推文共享根推文的数值 `author_id`(句柄可变;ID 不可变)
3. `chronological` — `created_timestamp` 在回复链中非递减
4. `no_interpolation` — 每个 `in_reply_to_tweet_id` 均在线程集合内解析
**建议标志:** `has_branches`(作者对同一父推文回复两次 — 路径而非树)、`root_deleted`(设置了 conversation_id 但根内容缺失)。
**优雅降级**:当 API 省略字段时:`missing_fields` 告知原因,检查器回退(缺失数值 ID 时使用 `author_handle`;缺失时间戳时使用推文 ID 排序)。
### 3. 冒充检测 — 感知头像哈希
骗子复制已验证账户的头像并重新上传 — URL 不同但像素相同。URL 字符串比较毫无用处。`AvatarMatcher` 使用 pHash 8×8(通过 DCT 生成的 64 位感知哈希)与汉明距离阈值。
```
from scraperx import AvatarMatcher, VerifiedAvatarRegistry
matcher = AvatarMatcher()
registry = VerifiedAvatarRegistry()
# Seed the registry with known-good avatars
registry.record_avatar("elonmusk", "https://pbs.twimg.com/profile_images/...", matcher)
# A reply from @elonmuskk (typosquat) claiming to be Elon
is_match, hamming, matched = registry.check_impersonation(
claimed_handle="elonmuskk",
avatar_url="https://pbs.twimg.com/profile_images/NEW_URL.jpg",
matcher=matcher,
)
if not is_match and matched and matched != "elonmuskk":
print(f"IMPERSONATION: @elonmuskk sporting @{matched}'s avatar (hamming={hamming})")
```
**汉明阈值(64 位 pHash):**
| 距离 | 解释 |
|---|---|
| ≤ 6 位 | 几乎确定是同一图像(重新上传 + 轻度 JPEG) |
| 7–12 位 | 同一图像被修改(边框/覆盖/色调)— **标记** |
| 13–20 位 | 模糊,需权衡 |
| > 20 位 | 不同图像 |
默认阈值 `10`。哈希缓存在 SQLite 中,TTL 为 30 天。每句柄滚动窗口保留 5 个哈希以容忍故意的头像变更。
**安全性:** 主机允许列表(`pbs.twimg.com`)、2MB 大小上限、`image/*` 内容类型检查 — 防止 SSRF。
**无 `[vision]` 扩展时:** 降级为内容 SHA256 比较(仅字节级相同)。完全可选。
### 4. YouTube + Vimeo 转录
```
from scraperx import VimeoScraper
from scraperx.youtube_scraper import YouTubeScraper
# YouTube
yt = YouTubeScraper()
res = yt.get_transcript("https://youtube.com/watch?v=dQw4w9WgXcQ")
print(res.transcript[:500])
# Vimeo
vm = VimeoScraper()
res = vm.get_transcript("https://vimeo.com/76979871")
print(f"{res.title} / {res.author} / {res.duration_seconds}s")
print(f"method: {res.transcript_method}") # text_tracks | whisper_faster | whisper_cli
print(res.transcript[:500])
# Embed-domain-locked Vimeo — pass the embedder URL as referer
res = vm.get_transcript(
"https://player.vimeo.com/video/123456",
referer="https://some-company.com/product-tour",
)
```
转录优先级:创作者上传的 VTT → `faster-whisper`(GPU)→ `whisper` CLI。自动检测 GPU(CUDA 上使用 float16,Metal 上使用 int8,CPU 回退)。
### 5. 视频发现 — 扫描任意网页
```
from scraperx import discover_videos, fetch_any_video_transcript
refs = discover_videos("https://some-company.example.com/product")
for v in refs:
print(f"{v.provider}: {v.canonical_url} (embed: {v.embed_url})")
# Top-level dispatcher — direct URL or webpage, auto-routes
result = fetch_any_video_transcript("https://some-blog.com/post-with-vimeo-embed")
```
**检测 6 个提供商模式:**
- YouTube / youtube-nocookie iframe
- Vimeo iframe(包括带哈希的未列出链接 `?h=abc`)
- Wistia iframe 与 JS 嵌入(`
标签:Faster-Whisper, OEmbed, pHash, Python标准库, SEO, Solana, Splunk, SQLite缓存, Token提取, Vimeo抓取, Whisper, X Twitter抓取, YouTube抓取, ytt-dlp, 加密货币诈骗检测, 反冒充检测, 开源, 感知哈希, 文本摘要, 无API密钥, 短链接检测, 社交媒体抓取, 离线抓取, 线程真实性验证, 网页嵌入视频检测, 自动字幕, 表情符号垃圾检测, 视频抓取, 视频转录, 语音转文本, 逆向工具, 通用视频发现, 钱包地址检测, 防伪检测