任何人无需登录即可查看的品牌和企业公开 Facebook 主页,包含大量有价值的公开信息:主页名称、公开帖子的可见文本,以及帖子显示的汇总数量。对于竞争研究、品牌监控或内容基准测试,这个公开的数据面是值得以程序化方式读取的。本指南向你展示如何使用 Python 爬取公开 Facebook 主页,在 PyCharm 内构建和运行,并且真正有效。

先明确说明:这里的所有内容仅限于公开 Facebook 主页。这意味着公开的主页名称、公开帖子的可见文本,以及任何访客无需登录即可看到的公开汇总数量。不涵盖私人群组、成员列表、个人资料、需要登录才能访问的内容、与具名个人关联的评论,或任何人的个人数据。Facebook 及其母公司 Meta 在服务条款中严格限制自动化访问,因此在将此代码指向任何实际目标之前,请先阅读靠近末尾的法律部分。对于生产或商业用途,正确的工具是官方 Facebook Graph API,而不是爬虫。

你将构建什么

一个用 Python 编写、在 PyCharm 中运行的小脚本,接收一个公开 Facebook 主页 URL,通过带有 JavaScript token 的Crawling API 获取完整渲染的页面,并解析出少量公开字段:

  • 公开主页名称,即主页上显示的品牌或企业名称。
  • 公开帖子文本,即主页上公开帖子的可见正文。
  • 公开数量,即公开主页显示的汇总数字,例如帖子的点赞或分享数量,仅作为数字处理。
  • 公开帖子 URL,即每条公开帖子的永久链接。

请注意有意省略的内容:没有群组成员列表,没有评论者身份,没有个人资料,没有个人联系方式。这些是个人数据,此处有意将其排除在外。本教程的旧版本以 Facebook 群组为目标;本次重写严格限于公开主页,这是可辩护的数据面。

为什么普通请求在 Facebook 上会失败

用裸 HTTP 客户端请求一个公开 Facebook 主页 URL,你会得到一个技术上成功但实际上毫无用处的响应。响应体是一个 JavaScript 外壳:真实内容只有在页面的脚本在浏览器中运行并从内部端点获取数据后才会出现。Facebook 多年来一直是公开网络上最难爬取的目标之一,原因正在于此。

除渲染问题外,Facebook 还会快速标记自动化流量。数据中心 IP 段、缺少浏览器行为以及重复的请求模式,在任何有趣的内容加载之前就会被挑战或速率限制。因此,一个可运行的爬虫在同一个请求中需要两样东西:一个真正渲染页面的浏览器,以及一个平台认为是普通访客的 IP 地址。你可以自己用无头浏览器和一组轮换住宅代理来构建这些,但保持那个技术栈健康才是大部分工作所在。Crawling API 将两者合并为一次调用:你发送带有 JavaScript token 的 URL,它在受信任的住宅 IP 后面渲染页面,并返回你可以解析的完成 HTML。如果你想要更深入的背景知识,请参阅我们关于如何爬取 JavaScript 网站的指南。

为什么需要 JS token

Crawlbase 提供两种 token 类型。普通 token 获取静态 HTML;JavaScript(JS)token 先在真实浏览器中渲染页面。Facebook 是客户端渲染的,所以这里需要 JS token。普通 token 返回与普通请求相同的外壳,没有任何有用的内容可以解析。

前置条件

首先需要准备几样东西,每样都不需要太长时间。

基本 Python 技能。你应该能够运行脚本并用 pip 安装包。如果你是 HTML 解析新手,我们关于如何用 Python 爬取网站的入门指南涵盖了提取部分。

Python 3.8 或更高版本。python --version 确认。如果你没有,从 python.org 安装。旧版教程使用 Python 2 和 urllib2;两者均已停止维护,因此本次重写面向现代 Python 3。

PyCharm。从 JetBrains 下载免费社区版并安装。PyCharm 是我们用来创建项目、安装包和运行脚本的 IDE。

Crawlbase 账户和 JS token。注册,打开你的控制面板,从账户文档页面复制你的 JavaScript(JS)token。将其视为密码:它对你的请求进行身份验证,因此不要将其放入版本控制。

在 PyCharm 中设置项目

这是演练的 PyCharm 部分。打开 PyCharm 并选择新建项目。将其命名为类似 facebook-page-scraper 的名称,让 PyCharm 为你创建虚拟环境(默认设置),确认解释器是 Python 3.8 或更高版本。点击创建。然后在项目面板中右键单击项目,选择新建,Python 文件,并命名为 scraper.py

PyCharm 在窗口底部内置了一个终端,已激活到你项目的虚拟环境。打开它并安装爬虫需要的两个库。

bash
python --version

pip install crawlbase beautifulsoup4

两个依赖项完成工作:crawlbase 是 Crawling API 的官方客户端,beautifulsoup4 解析返回的 HTML,以便你可以按选择器提取单个字段。如果你更喜欢 GUI,PyCharm 的Python 包工具窗口可以安装相同的包而无需使用终端。

步骤 1:获取渲染后的页面

从获取完成的页面开始。在 scraper.py 中,导入 CrawlingAPI,用你的 JS token 初始化它,并请求公开 Facebook 主页 URL。在解析之前检查状态码,使失败保持显眼而非静默。

python
from crawlbase import CrawlingAPI

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"})

def crawl(page_url):
    options = {"ajax_wait": "true", "page_wait": 5000}
    response = api.get(page_url, options)
    if response["status_code"] == 200:
        return response["body"].decode("utf-8")
    print(f"Request failed: {response['status_code']}")
    return None

if __name__ == "__main__":
    page_url = "https://www.facebook.com/MetaForBusiness"
    html = crawl(page_url)
    print(html[:500] if html else "No HTML returned")

在 PyCharm 内运行:右键单击编辑器并选择运行"scraper",或按编辑区旁边的绿色运行箭头。两个等待选项对于客户端渲染目标很重要。ajax_wait 告诉 API 等待异步内容完成加载,page_wait 在加载后等待固定毫秒数,使延迟渲染的元素在页面被捕获前出现。五秒是合理的起点;如果字段返回为空,则增加它。示例使用公开企业主页正是因为它是公开且非个人的。你应该在运行面板中看到真实的页面标记,这在你编写任何选择器之前就确认了渲染正常工作。

Crawlbase Facebook Scraper

Facebook 需要在受信任 IP 后面的渲染页面,一次调用完成。Crawling API 接收 JS token,在真实浏览器中运行页面,在服务端轮换住宅 IP,并将完成的 HTML 交给你,让你无需自己运行无头浏览器集群和代理池。先在免费层级将其指向公开企业主页试试。

步骤 2:用 BeautifulSoup 解析公开字段

拿到渲染后的 HTML,将其加载到 BeautifulSoup 中并提取公开字段。Facebook 在页面的 <meta> 标签中暴露了大量稳定的元数据,这比追踪频繁改变名称的深层嵌套 CSS 类可靠得多。公开主页名称和描述存在于标准 Open Graph meta 标签中;可见的公开帖子文本和帖子永久链接存在于渲染后的 DOM 中。

python
from bs4 import BeautifulSoup

def meta(soup, prop):
    el = soup.find("meta", attrs={"property": prop})
    return el["content"] if el and el.has_attr("content") else None

def scrape_page(html):
    soup = BeautifulSoup(html, "html.parser")

    page_name = meta(soup, "og:title")
    summary = meta(soup, "og:description")

    post_urls = []
    for a in soup.select("a[href*='/posts/']"):
        href = a["href"].split("?")[0]
        if href.startswith("/"):
            href = f"https://www.facebook.com{href}"
        if href not in post_urls:
            post_urls.append(href)

    return {
        "page_name": page_name,
        "summary": summary,
        "post_urls": post_urls,
    }

og:title 标签携带公开主页名称,og:description 通常携带简短的公开摘要字符串。帖子链接从 href 包含 /posts/ 的锚点中收集,去除查询字符串,规范化为绝对 URL,并去重,因为同一永久链接可能在渲染后的 DOM 中出现多次。这里的所有内容都是公开的主页级别信号,而非任何个人的资料。

选择器会变化

Facebook 会在不通知的情况下更改其标记和类名,这就是为什么此代码依赖 Open Graph meta 标签和稳定的 /posts/ URL 格式,而不是脆弱的嵌套类。当某个字段返回 None 时,在浏览器开发者工具中重新检查实时页面并更新选择器。周期性维护对任何生产爬虫来说都是正常的,而不是出了问题的信号。

步骤 3:从帖子中提取公开文本和数量

公开帖子页面携带相同类型的 Open Graph 元数据,加上渲染 DOM 中可见的公开帖子文本。你可以从中提取公开帖子文本以及页面显示的任何公开数量,例如点赞或分享数。将这些数字作为纯汇总来读取,而不是枚举其背后人员的方式。

python
import re
from bs4 import BeautifulSoup

def scrape_post(html):
    soup = BeautifulSoup(html, "html.parser")

    desc = soup.find("meta", attrs={"property": "og:description"})
    post_text = desc["content"] if desc and desc.has_attr("content") else None

    counts = {}
    for label in ("reaction", "share"):
        node = soup.find("div", attrs={"aria-label": re.compile(label, re.I)})
        if node:
            digits = re.sub(r"[^\d]", "", node.get_text())
            counts[label] = int(digits) if digits else None

    return {
        "post_text": post_text,
        "counts": counts,
    }

这只提取公开的、非个人的字段:可见的帖子文本和页面显示的汇总数量。它不读取单条评论、评论者账号或谁点赞了帖子。aria-label 和选择器形状会随时间变化,所以当某个字段停止解析时重新检查它们。这种克制是有意为之的,也是让工作保持可辩护的原因。数量是数字;其背后的人不是你应当采集的。

步骤 4:整合在一起

现在将获取和解析连接成一个可运行的脚本,读取公开主页,然后访问其前几条公开帖子。将以下内容粘贴覆盖 scraper.py 并从 PyCharm 运行。

python
import re
import json
import time
from crawlbase import CrawlingAPI
from bs4 import BeautifulSoup

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"})

def crawl(page_url):
    options = {"ajax_wait": "true", "page_wait": 5000}
    response = api.get(page_url, options)
    if response["status_code"] == 200:
        return response["body"].decode("utf-8")
    print(f"Request failed: {response['status_code']}")
    return None

def main():
    page_url = "https://www.facebook.com/MetaForBusiness"
    html = crawl(page_url)
    if not html:
        return

    page = scrape_page(html)
    records = []
    for post_url in page["post_urls"][:5]:
        post_html = crawl(post_url)
        if post_html:
            record = scrape_post(post_html)
            record["url"] = post_url
            records.append(record)
        time.sleep(3)

    output = {"page_name": page["page_name"], "posts": records}
    print(json.dumps(output, indent=2, ensure_ascii=False))

if __name__ == "__main__":
    main()

请求之间的 time.sleep(3) 不是装饰。控制节奏是决定一次运行是否保持健康的最重要因素,我们稍后会回到这个话题。切片 [:5] 使演示保持小规模;只有当你的节奏和使用量负责任时才增加它。保持之前步骤中的 scrape_pagescrape_post 函数在同一文件中,这样脚本才能端到端运行。

输出是什么样的

在 PyCharm 中运行完整脚本,你会得到一条干净的公开字段记录,可以写入 JSON、CSV 或数据库。

json
{
  "page_name": "Meta for Business",
  "posts": [
    {
      "post_text": "New tools to help businesses reach customers.",
      "counts": { "reaction": 1820, "share": 214 },
      "url": "https://www.facebook.com/MetaForBusiness/posts/example123"
    }
  ]
}

扩展规模与保持不被封锁

要读取多个主页,在公开主页 URL 列表上循环抓取,并在过程中将每个结果写入磁盘,这样中途失败不会丢失所有内容。即使渲染由 Crawling API 处理,Facebook 也会监视爬虫形态的流量。一些习惯有助于保持长时间运行的健康,适用于任何困难的、防御性强的目标。

  • 控制请求节奏。在紧密循环中轰炸页面是被限速的最快方式。像上面的 time.sleep 那样添加真实延迟,克制并发的冲动。
  • 依靠轮换。住宅 IP 池将请求分散到多个真实用户地址,使单个地址不会触发速率限制。Crawling API 为你处理这些;如果你自己搭建技术栈,这是需要做好的部分。
  • 读取状态码。开始返回挑战或错误的运行是在告诉你当前速率或 IP 层级已不够。应该退让,而非更加激进。
  • 保持低使用量和目标多样化。公开数据研究不需要抓取一个主页的完整历史。采样你需要的内容然后停止。

更广泛的攻略请参阅我们关于如何在爬取网站时不被封锁的指南。如果托管渲染加轮换调用对你的使用量还不够,并且你想要自动解析的页面输出,我们关于用 Crawling API 掌握 Facebook 数据提取的深度文章涵盖了结构化输出方案。

爬取 Facebook 合法吗?

这是你在编写生产代码之前需要阅读的部分。Facebook 由 Meta 所有,Meta 的服务条款严格限制自动化访问和数据采集。无论你的工具多么谨慎,自动化爬取都可能违反这些条款,以上代码都无法改变这一点。它只是让技术部分得以运行。阅读 Meta 和 Facebook 的服务条款以及 Facebook 的 robots.txt,尊重平台的速率限制,并将这些都视为你采集内容的边界。

值得坚守的诚实而严格的规则。只从公开主页采集公开数据:公开主页名称、公开帖子的可见文本、公开汇总数量,以及任何访客无需登录即可看到的公开帖子 URL。绝不爬取私人群组、群组成员列表、个人资料、需要登录才能访问的内容、私信、与具名个人关联的评论,或任何人的个人数据。本教程的旧版本将 Facebook 群组作为目标;这正是本次重写所避免的,因为群组内容和成员数据属于个人数据,且经常处于隐私保护之后。绝不绕过身份验证、以编程方式求解登录挑战,或使用他人凭据访问内容。这些都是明确的界限,本指南在设计上始终处于这些界限的公开一侧。

涉及任何个人数据时,隐私法律适用。在欧盟的 GDPR 和加利福尼亚州的 CCPA 下,你需要合法依据来处理个人数据,并且必须遵从删除和退出请求。最安全的做法是完全避免个人数据,只保留主页级别的公开汇总,这正是上面的脚本所做的。对于任何实际或商业用途,正确的工具是官方 Facebook Graph API。它专为对你拥有或管理的主页进行有授权的访问而构建,提供有保证的结构,并让你保持在 Meta 条款范围内。本文是一篇严格限于公开主页的技术演练,不是对大规模个人数据采集的认可,也不涵盖登录后的任何内容。如果你的项目需要的不仅仅是少量公开字段的样本,Graph API 或正式数据协议才是正确的路径,而非更聪明的爬虫。

回顾

核心要点

  • 仅限公开主页。本演练针对公开 Facebook 主页,绝不涉及私人群组、成员数据或个人资料。
  • Facebook 是客户端渲染且有机器人防御的。普通请求返回空外壳,因此你必须在解析之前渲染页面。
  • PyCharm 处理设置。创建项目和虚拟环境,安装 crawlbase 和 beautifulsoup4,并从 IDE 运行脚本。
  • 渲染和受信任 IP 属于同一次调用。带有 JS token 的 Crawling API 两者都做;ajax_waitpage_wait 控制等待内容的时长。
  • 控制节奏、轮换,并优先使用 Graph API。保持低使用量,依靠住宅轮换,注意 GDPR 和 CCPA,对任何实际或商业用途使用官方 Facebook Graph API。

常见问题

为什么普通请求从 Facebook 返回不了数据?

因为 Facebook 用 JavaScript 在客户端渲染其页面和帖子内容。初始 HTML 是一个外壳,只有在页面的脚本在浏览器中运行后才会填充,因此原始 HTTP 请求返回几乎为空的正文。要获取真实的公开数据,你必须先渲染页面,这正是 Crawling API 的 JS token 为你处理的。

Facebook 需要普通 token 还是 JS token?

需要 JS token。普通 token 获取静态 HTML,在 Facebook 上与普通请求返回的是相同的空外壳。JS token 在将 HTML 交回之前先在真实浏览器中渲染页面,因此当 BeautifulSoup 解析时公开字段已经存在。

可以安全爬取哪些 Facebook 数据?

只有来自公开主页的公开数据:公开主页名称、公开帖子的可见文本、作为数字的公开汇总数量,以及公开帖子 URL。私人群组、群组成员列表、个人资料、需要登录才能访问的内容、私信,以及个别评论者的身份均超出范围且不被允许。这些是个人数据,采集它们违反 Meta 的条款,在许多地方也违反隐私法律。

为什么本教程涵盖主页而非群组?

因为公开主页是可辩护的数据面。群组内容、成员列表,以及个别成员的帖子属于个人数据,且经常处于隐私保护之后,即使标记为公开的群组也是如此。品牌和企业主页的设计是让任何人无需登录即可查看,因此读取其公开主页名称、帖子文本和汇总数量处于公开边界之内。

我应该使用官方 Facebook Graph API 还是爬取网站?

对于任何实际、持续或商业用途,使用官方 Facebook Graph API。这是有授权的路径,提供有保证的结构,并让你保持在 Meta 条款范围内。在没有 API 访问权限的情况下,用此处的方法爬取少量公开主页字段的样本,适合轻量的公开数据研究,前提是你尊重条款、robots.txt 和速率限制。

如何避免在爬取 Facebook 时被封锁?

保持低 per-IP 请求速率,在请求之间添加真实延迟,多样化目标而非抓取一个主页的完整历史,并通过轮换住宅 IP 路由,使单个地址不会触发速率限制。Crawling API 为你管理轮换和受信任 IP 池。注意状态码,一旦开始看到挑战就立即退让。

开始构建

大规模爬取任何站点,无需与基础设施对抗。

Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。

自助开通 · 无需销售通话 · 提供企业级爬取量