
本文详解如何绕过 instaloader 的评论数量限制,通过模拟浏览器+graphql api 调用,稳定获取 instagram 公开帖的全部评论(含嵌套回复),附完整可运行代码与关键注意事项。
instagram 官方未开放公开评论的完整 API 接口,且 instaloader 默认仅拉取前 100 条顶层评论(不包含子回复),也无法自动翻页加载更多。要获取全部评论(含深层嵌套、点赞数、时间戳、用户信息等),需结合浏览器自动化与 Instagram 内部 GraphQL 查询机制——这是当前最稳定、免依赖第三方账号权限的方案。
✅ 核心原理说明
Instagram 网页版在加载评论时,实际向以下 GraphQL 端点发起请求:
https://www.instagram.com/graphql/query/?query_hash={hash}&variables={encoded_json}
其中 query_hash 是固定哈希值(对应“获取帖子评论”逻辑),variables 包含 shortcode 及分页/深度参数。我们通过 Selenium 模拟登录后访问该 URL,即可获得结构化 json 响应。
✅ 完整可运行代码(含错误处理与注释)
import json import time from urllib.parse import quote, urlparse from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from bs4 import BeautifulSoup def extract_shortcode(url: str) -> str: """从 Instagram 帖子 URL 提取 shortcode(如 C2SGS95NQH_)""" path = urlparse(url).path.strip('/') if path.startswith('p/'): return path.split('/')[1] raise ValueError("Invalid Instagram post URL format") def build_graphql_url(shortcode: str) -> str: """构建 GraphQL 请求 URL,启用全量评论加载""" variables = { "shortcode": shortcode, "child_comment_count": 10000, # 子回复上限(每条评论下的回复) "fetch_comment_count": 10000, # 首次加载的顶层评论数 "parent_comment_count": 10000, # 顶层评论总数(用于分页) "has_threaded_comments": True } query_hash = "b3055c01b4b222b8a47dc12b090e4e64" encoded_vars = quote(json.dumps(variables)) return f"https://www.instagram.com/graphql/query/?query_hash={query_hash}&variables={encoded_vars}" def setup_driver() -> webdriver.Chrome: """配置无头 Chrome 驱动(推荐关闭 headless 以便手动登录)""" options = Options() options.add_argument("--start-maximized") options.add_argument("--disable-blink-features=AutomationControlled") options.add_experimental_option("excludeSwitches", ["enable-automation"]) options.add_experimental_option('useAutomationExtension', False) driver = webdriver.Chrome(options=options) # 隐藏 WebDriver 特征(降低被识别为爬虫概率) driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', { 'source': 'Object.defineProperty(navigator, "webdriver", {get: () => undefined})' }) return driver def wait_for_manual_login(driver: webdriver.Chrome): """暂停等待用户完成 Instagram 登录(关键步骤)""" print("✅ 请在打开的浏览器中手动登录 Instagram 账号...") print("⚠️ 登录后,请确保页面跳转至目标帖子(如 https://www.instagram.com/p/...)") input("按回车键继续 → ") def fetch_comments_data(driver: webdriver.Chrome, graphql_url: str) -> dict: """访问 GraphQL URL 并解析返回的 JSON 数据""" try: driver.get(graphql_url) # 等待 标签加载(GraphQL 响应直接渲染为 pre 标签) WebDriverWait(driver, 20).until( EC.presence_of_element_located((By.TAG_NAME, "pre")) ) soup = BeautifulSoup(driver.page_source, "html.parser") pre_tag = soup.find("pre") if not pre_tag or not pre_tag.text.strip(): raise RuntimeError("GraphQL response not found in tag") return json.loads(pre_tag.text) except Exception as e: raise RuntimeError(f"Failed to fetch comments: {e}") def save_comments_to_file(data: dict, filename: str = "instagram_comments.json"): """保存原始响应数据到 JSON 文件(便于后续解析)""" with open(filename, "w", encoding="utf-8") as f: json.dump(data, f, indent=2, ensure_ascii=False) print(f"✅ 评论数据已保存至 {filename}") # —— 主流程 —— if __name__ == "__main__": POST_URL = "https://www.instagram.com/p/C2SGS95NQH_/" # 替换为你需要的帖子链接 try: shortcode = extract_shortcode(POST_URL) print(f"? 解析 shortcode: {shortcode}") driver = setup_driver() driver.get(POST_URL) wait_for_manual_login(driver) graphql_url = build_graphql_url(shortcode) print(f"? 正在请求 GraphQL 接口: {graphql_url[:80]}...") comments_data = fetch_comments_data(driver, graphql_url) save_comments_to_file(comments_data) # ✨ 示例:提取前 5 条顶层评论(含用户名、文本、时间、子回复数) edges = comments_data.get("data", {}).get("shortcode_media", {}).get("edge_media_to_parent_comment", {}).get("edges", []) print(f"n? 获取到 {len(edges)} 条顶层评论示例:") for i, edge in enumerate(edges[:5]): node = edge["node"] username = node["owner"]["username"] text = node["text"][:60] + "..." if len(node["text"]) > 60 else node["text"] created_at = node["created_at"] reply_count = node["edge_comment_to_child_comment"]["count"] print(f"[{i+1}] @{username} ({time.strftime('%Y-%m-%d', time.gmtime(created_at))}): {text} [回复数: {reply_count}]") except Exception as e: print(f"❌ 执行出错: {e}") finally: driver.quit()
⚠️ 关键注意事项
- 必须手动登录:Instagram 对未登录会话返回空评论,且登录状态需保持(session cookie 有效);
- 反爬策略:代码已加入 WebDriver 特征隐藏,但仍建议使用真实账号、避免高频请求(单帖间隔 ≥ 5 秒);
- 评论结构:返回 JSON 中 data.shortcode_media.edge_media_to_parent_comment.edges 为顶层评论列表;每个 node.edge_comment_to_child_comment.edges 为该评论下的嵌套回复;
- 合规提醒:仅限抓取公开帖子,遵守 Instagram Terms of Use 及 GDPR/CCPA 相关条款,禁止存储敏感用户信息或用于商业群发。
✅ 后续建议
- 如需持续采集,可封装为函数支持批量 URL 输入;
- 结合 instaloader 获取帖子元数据(发布时间、点赞数、作者信息),与评论数据关联分析;
- 对大规模评论做情感分析时,建议先清洗(过滤 emoji、URL、广告词)再送入 nlp 模型。
掌握此方法,你将能可靠获取任意公开 Instagram 帖子的全量、结构化评论数据,为舆情分析、用户行为研究提供坚实基础。
立即学习“Python免费学习笔记(深入)”;