"""FB Comments Crawler — Gradio Space UI. Crawl 100% of comments (with pagination + replies) from a Facebook post you have permission to read (see README), and download CSV/JSON with commenter UID. The OAuth login step (getting a Page access token) must be done on your local machine because Facebook redirects the browser to localhost. This Space accepts the token you already have and does the crawling. """ import json import os import tempfile from pathlib import Path import gradio as gr import pandas as pd from fb_comments import ( CommentCrawler, CommentCrawlerError, RateLimitError, parse_post_identifier, resolve_post_id, ) # --------------------------------------------------------------------------- # # Crawl logic # --------------------------------------------------------------------------- # def crawl(token: str, url_or_id: str, include_replies: bool): """Run the crawl; return (csv_path, json_path, df, status_text).""" token = (token or "").strip() url_or_id = (url_or_id or "").strip() if not token: return None, None, None, "❌ Token requi. Get a Page access token first (see tab Help)." if not url_or_id: return None, None, None, "❌ Post URL/id requi." try: post_id = parse_post_identifier(url_or_id) except Exception as e: return None, None, None, f"❌ Invalid post URL/id: {e}" cc = CommentCrawler(token) try: post_id = resolve_post_id(post_id, cc) except Exception: pass try: comments = list(cc.comments(post_id, include_replies=bool(include_replies))) except RateLimitError as e: return None, None, None, f"❌ Rate limited: {e}" except CommentCrawlerError as e: return None, None, None, f"❌ API error: {e} — verify token is a Page token of a page you manage." except Exception as e: return None, None, None, f"❌ Unexpected: {e}" if not comments: return None, None, None, "No comments (or post not readable with this token)." rows = [] for c in comments: author = c.get("from") or {} rows.append({ "comment_id": c.get("id"), "parent_id": (c.get("parent") or {}).get("id"), "name": author.get("name"), "uid": author.get("id"), "message": c.get("message") or "", "created_time": c.get("created_time"), "like_count": c.get("like_count"), }) df = pd.DataFrame(rows) out_dir = tempfile.gettempdir() base = f"comments_{len(rows)}" csv_path = str(Path(out_dir) / f"{base}.csv") json_path = str(Path(out_dir) / f"{base}.json") df.to_csv(csv_path, index=False, encoding="utf-8-sig") with open(json_path, "w", encoding="utf-8") as fh: json.dump(rows, fh, ensure_ascii=False, indent=2) return csv_path, json_path, df, f"OK: {len(rows)} comments (top-level + replies) collected." # --------------------------------------------------------------------------- # # Help text # --------------------------------------------------------------------------- # HELP = """ ### How to get a Page access token 1. Create a FB app at developers.facebook.com/apps → **Facebook Login**, keep **Development** mode. 2. Add `http://localhost:8080/callback` to **Valid OAuth Redirect URIs** + enable Web OAuth. 3. Add your account as **Admin** under Roles. (No App Review needed for pages you own.) 4. On your own machine run: ``` pip install . # install fb-comments-crawler fbcomments login --app-id --app-secret --port 8080 ``` → open the printed URL, log in as page admin → token saved to `token.json`. 5. Paste the `page_access_token` value into the box here. > You can only read comments on posts of **Pages you manage** (permission > `pages_read_engagement`). Posts of other pages return error #283. The **uid** column holds the **PSID** (Page-Scoped user ID) — the modern stable FB user identifier. """ # --------------------------------------------------------------------------- # # Gradio UI # --------------------------------------------------------------------------- # with gr.Blocks(title="FB Comments Crawler") as demo: gr.Markdown("# FB Comments Crawler\nPull 100% of comments (with UID) from a FB post you manage, download CSV/JSON.") with gr.Tab("Crawl"): token = gr.Textbox(label="Page access token", type="password", placeholder="EAAG... paste token from token.json") url = gr.Textbox(label="Post URL or id", placeholder="https://www.facebook.com/MyPage/posts/pfbid0...") replies = gr.Checkbox(label="Include reply comments", value=True) btn = gr.Button("Crawl", variant="primary") status = gr.Markdown("Ready.") df_out = gr.Dataframe(label="Comments preview") csv_file = gr.File(label="Download CSV") json_file = gr.File(label="Download JSON") btn.click(fn=crawl, inputs=[token, url, replies], outputs=[csv_file, json_file, df_out, status]) with gr.Tab("Help"): gr.Markdown(HELP) demo.queue() if __name__ == "__main__": demo.launch()