品牌想要了解人们对它们、它们的产品以及竞争对手的看法,而这类讨论大多发生在公开场合:论坛、评论网站、社区板块和公开的社交媒体资料。收集这些内容是社交聆听的基础,但做起来比看上去要难。每个平台的页面结构都不同,很多平台用 JavaScript 渲染内容,而且你收集的几乎所有内容都涉及到真实的人。
本指南介绍了当今的团队如何使用网页抓取 API 收集公开社交数据:应该使用哪些来源,何时官方 API 是更好的选择,如何将差异巨大的平台整合成一份统一的数据集,以及如何从设计之初就把个人数据排除在外。
先明确你想了解什么
社交数据项目出问题,往往是因为先收集一切,再决定怎么用。应该先确定你要回答的问题:
- **数量:**品牌被提及的频率如何,是否在变化?
- **话题:**人们在谈论什么,涉及哪些产品或问题?
- **对比:**该品牌的提及量与竞争对手相比如何?
- **事件:**某个投诉或仿冒品信息是否正在扩散?
每个问题只需要少数几个字段:帖子的链接、时间、文本和来源。很少有项目需要关于发帖人的更多信息。
选择采集路线
| 来源 | 最佳路线 |
|---|---|
| 有官方 API 且能满足需求的平台 | 在条款范围内使用官方 API |
| 公开论坛、社区板块和评论网站 | 采集公开页面 |
| 用 JavaScript 渲染的页面 | 带渲染功能的网页抓取 API |
| 任何需要登录才能访问的内容 | 不要采集 |
在官方 API 存在且符合你的数量和预算需求时,它通常是最可靠、也最明确被允许的路线。对于没有官方 API 的公开页面,网页抓取 API 可以处理让社交网站变得棘手的部分:渲染 JavaScript、等待内容加载,以及按指定国家呈现的方式获取页面。提取规则会以 JSON 的形式返回你需要的字段,而不是原始 HTML。
始终停留在每个网站的公开一侧。需要登录才能访问的内容、私密群组和私信都是禁区,一方面是因为访问这些内容通常意味着要接受禁止采集的条款,另一方面是因为相关人员期望自己的隐私得到保护。
为所有平台统一一套结构
每个来源对字段的命名都不一样:论坛有永久链接和正文,评论网站有链接和评论文本,日期的格式和时区也各不相同。如果分别分析它们,对比就会出问题。在采集过程中,把每个来源都映射到同一个小型结构中:
- **source:**帖子来自哪个来源;
- **url:**规范化后的地址,确保同一篇帖子只被计算一次;
- **published_at:**UTC 时间;
- **text:**帖子的文本,空白字符已标准化;
- **author_key:**用一个化名键代替作者的名字。
下面这个模块完成了这种映射,去除了重复的 URL(跟踪参数和片段经常会让一篇帖子看起来像几篇),并统计每个品牌每天的提及次数:
import hashlib
from collections import Counter
from datetime import datetime, timezone
from urllib.parse import urlsplit, urlunsplit
# Where each source keeps the fields we need, after extraction rules have run.
FIELD_MAPS = {
"forum": {"url": "permalink", "text": "body", "author": "username", "time": "created"},
"review": {"url": "link", "text": "review_text", "author": "reviewer", "time": "date"},
}
def canonical(url):
"""Drop query strings and fragments so the same post counts once."""
parts = urlsplit(url)
return urlunsplit((parts.scheme, parts.netloc.lower(), parts.path.rstrip("/"), "", ""))
def normalise(source, raw, salt):
"""One post in a common shape, with the author pseudonymised and no other personal data."""
m = FIELD_MAPS[source]
when = datetime.fromisoformat(raw[m["time"]].replace("Z", "+00:00")).astimezone(timezone.utc)
return {
"source": source,
"url": canonical(raw[m["url"]]),
"published_at": when.isoformat(timespec="seconds"),
"text": " ".join(raw[m["text"]].split()),
"author_key": hashlib.sha256((salt + raw[m["author"]]).encode()).hexdigest()[:16],
}
def daily_mentions(posts, brands):
"""Count posts per day that mention each brand, counting each URL once."""
seen, counts = set(), Counter()
for post in posts:
if post["url"] in seen:
continue
seen.add(post["url"])
day = post["published_at"][:10]
for brand in brands:
if brand.lower() in post["text"].lower():
counts[(day, brand)] += 1
return dict(sorted(counts.items()))
应用到三条示例记录上,其中一条是带有跟踪参数的重复项:
import os
from social import daily_mentions, normalise
salt = os.environ.get("MENTIONS_SALT", "rotate-me-per-project")
raw = [
("forum", {"permalink": "https://forum.example/t/88?ref=home", "body": "Acme kettle died after a week",
"username": "kitchen_fan", "created": "2026-10-20T08:15:00Z"}),
("forum", {"permalink": "https://forum.example/t/88", "body": "Acme kettle died after a week",
"username": "kitchen_fan", "created": "2026-10-20T08:15:00Z"}),
("review", {"link": "https://reviews.example/p/42", "review_text": "Better than my old Brandly one",
"reviewer": "J. D.", "date": "2026-10-20T19:40:00+02:00"}),
]
posts = [normalise(source, item, salt) for source, item in raw]
print(daily_mentions(posts, ["Acme", "Brandly"]))
{('2026-10-20', 'Acme'): 1, ('2026-10-20', 'Brandly'): 1}
重复项只计算一次,评论的本地时间被转换为 UTC,也没有任何用户名出现在输出结果中。
从设计之初排除个人数据
在欧洲和许多其他地方,公开发布的帖子依然属于个人数据,无论帖子多么公开,数据保护规则都照样适用。最安全的做法是只保留问题所需的最少个人信息:
- **对作者进行化名处理。**加盐哈希值可以让你统计不同发帖人的数量,或发现某个账号反复发帖,而无需存储用户名。每个项目使用不同的盐值,并对其保密。
- **不要采集用户资料。**个人的粉丝数、简介、照片和所在地,对于品牌相关问题来说很少是必要的。
- **保留期要短。**聚合统计和话题数据可以保留;原始文本在分析完成后通常可以删除。
- **以聚合方式报告。**发布趋势和示例,但不要识别出具体的个人。
我们关于抓取社交平台进行情感分析的指南涵盖了数据清洗完成后的分析环节,我们关于在线追踪品牌提及的指南则介绍了如何构建查询集以及处理地区差异。
温和地采集
社交和社区网站是为其用户运营的。采集的速度应当与普通读者浏览的速度相当,优先使用列出最新帖子的页面,而不是爬取整个归档,并且只要有来源提出要求,就应停止从该来源采集。品牌监测通常每天对相关来源做一次抓取就足够了。
常见问题
采集社交媒体数据合法吗?
采集公开帖子通常是合法的,但平台条款、你获取数据的方式以及数据保护法都同时适用。在合适的地方使用官方 API,远离需要登录才能访问的内容,并且只保留目的所需的个人数据。针对你的具体情况,请寻求法律意见。
我应该使用平台的 API 还是网页抓取 API?
如果官方 API 在其条款范围内能满足你所需的数据和数量,就使用官方 API。对于没有合适 API 的公开页面,尤其是用 JavaScript 渲染的页面,则使用网页抓取 API。
如何避免从社交媒体采集个人数据?
只采集问题所需的字段,用加盐的化名键替换用户名,完全跳过用户资料,原始文本只保留必要的时长,并以聚合方式报告结果。
应该多久采集一次社交提及?
对于大多数品牌监测来说,每天一次就足够了。只有在产品发布或发生事件期间才需要更频繁地采集,并且要聚焦于你的受众实际发帖的来源。
结论
社交数据之所以有价值,是因为它反映了真实人群在公开场合的表达,而这恰恰也是它需要谨慎对待的原因。先确定要回答的问题,在合适的地方使用官方 API,对公开页面使用网页抓取 API,把每个来源都映射到同一个小型结构中,并从一开始就把个人数据设计排除在外。这样得到的数据集既能回答品牌相关的问题,又不会收集超出所需的内容。