# /// script # requires-python = ">=3.12" # dependencies = [ # "httpx", # ] # /// import httpx import re import os import sys def download_twitter_photo_nitter(url, output_dir="downloads"): if not os.path.exists(output_dir): os.makedirs(output_dir) # 提取推文 ID match = re.search(r'status/(\d+)', url) if not match: print("❌ 无法提取推文 ID") return tweet_id = match.group(1) # 使用 Nitter 镜像站绕过限制(这是目前 Docker 环境最稳的方法) # Nitter 实例列表可以替换,如果这个挂了可以换另一个 nitter_instances = ["https://nitter.net", "https://nitter.cz", "https://nitter.moomoo.me"] headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" } success = False for instance in nitter_instances: try: nitter_url = f"{instance}/i/status/{tweet_id}" print(f"🕵️ 尝试通过镜像站抓取: {nitter_url}") with httpx.Client(headers=headers, follow_redirects=True, timeout=20.0) as client: response = client.get(nitter_url) if response.status_code != 200: continue # 寻找图片链接 (Nitter 的图片通常是 /pic/media%2F...) # 示例正则匹配 img_paths = re.findall(r'/pic/media%2F[^"]+', response.text) if not img_paths: # 尝试另一种匹配 img_paths = re.findall(r'/pic/orig/media%2F[^"]+', response.text) if img_paths: for i, path in enumerate(set(img_paths)): full_img_url = f"{instance}{path}" print(f"📥 发现图片: {full_img_url}") img_res = client.get(full_img_url) if img_res.status_code == 200: save_path = os.path.join(output_dir, f"tweet_{tweet_id}_{i}.jpg") with open(save_path, "wb") as f: f.write(img_res.content) print(f"✅ 保存成功: {save_path}") success = True if success: break # 下载到就停 except Exception as e: print(f"⚠️ 尝试实例 {instance} 时出错: {e}") if not success: print("❌ 所有尝试均失败。可能镜像站也无法访问该推文,或该推文是私密的。") if __name__ == "__main__": if len(sys.argv) < 2: print("用法: uv run scripts/twitter_dl_v2.py ") else: download_twitter_photo_nitter(sys.argv[1])