破解 Twitter 社交图数据:从理论到实践
1. 项目背景与目标
在处理 Twitter 数据时,我们面临着严格的速率限制。为了避免触及这些限制并遵守 Twitter 的服务条款,我们将构建一个从 Twitter 提取数据的系统,且在整个过程中不直接访问 Twitter 的 API。
我们的项目从构建局部网络(即自我网络,ego - network)开始,采用类似计算 Erdős 数的方式进行雪球式扩展。我们的分析目标包括:探索社区检测方法,将社交网络划分为紧密的子群体,以了解特定用户所属的不同社交群体;利用 Twitter 社交图的结构构建“关注推荐”引擎。
2. 网络基础概念
在深入研究 Twitter 社交图之前,我们需要明确网络的基本概念。从数学角度看,网络(或图)是由节点和边组成的集合。这些集合本身没有特定的上下文,仅用于表示边连接节点的某种世界。
下面是不同类型网络的介绍:
| 网络类型 | 特点 | 示例 |
| ---- | ---- | ---- |
| 无向网络 | 边没有方向,节点间的连接意味着相互关系 | 如图 11 - 2 顶部面板所示,Dick 和 Harry 共享连接,可相互交换信息、物品等 |
| 有向网络 | 边有箭头表示方向,体现单向关系 | 图 11 - 2 中间面板中,Dick 和 Drew 与 John 有连接,但 John 仅与 Harry 有连接 |
| 带标签边的有向网络 | 在有向网络基础上,边添加了标签,可表示“喜欢”“不喜欢”关系或访问级别等 | 图 11 - 2 最后面板,边标签可以是更复杂的权重,指示关系的强度或类型 |
不同的社交网络使用不同类型的图,例如 Facebook 是大规模的无向社交图,“加好友”需要双方同意,所有边都意味着相互的友谊,形成相对封闭、局部网络结构密集的网络;而 Twitter 是大型有向图,关注关系无需相互同意,允许图中存在较大的不对称性,名人、新闻媒体等成为主要的推文传播点。
3. 选择数据来源
Twitter 提供两种 API 访问方式:未认证访问每小时允许 150 次请求,OAuth 认证访问每小时限制为 350 次请求。这两种限制都无法在合理时间内满足我们的数据抓取需求,且我们不希望构建需要认证的 Twitter 应用。
因此,我们选择使用 Google 的 SocialGraph API(SGA)来获取 Twitter 社交图数据。SGA 于 2008 年推出,旨在创建跨所有社交网络站点的单一在线身份。通过输入以下 URL 并替换 EMAIL_ADDRESS,可查看 Google 存储的个人社交图数据:
https://socialgraph.googleapis.com/lookup?q=@EMAIL_ADDRESS&fme=1&pretty=1
SGA 会爬取包括 Twitter 在内的多个社交网络站点,存储公共社交图数据。其主要优势是每天允许 50,000 次查询,足以构建绝大多数用户的社交图。
4. 使用 SGA 查询数据
以 Drew 的 Twitter 页面为例,使用以下 API 查询:
https://socialgraph.googleapis.com/lookup?q=http://twitter.com/drewconway&edo=1&edi=1&pretty=1
该查询包含三个重要参数:
-
pretty
:仅在浏览器查看时有用,返回格式化的 JSON。在解析 JSON 构建网络时可忽略此参数。
-
edo
:表示“外向边”,即 Twitter 好友。
-
edi
:表示“内向边”,即 Twitter 粉丝。
查询返回的 JSON 中,我们主要关注
nodes
对象,它包含 Twitter 用户的描述信息以及入站和出站关系。
nodes_referenced
对象包含被查询节点关注的用户(出度),
nodes_referenced_by
对象包含关注被查询节点的用户(入度)。
以下是示例 JSON 结构:
{
"canonical_mapping": {
"http://twitter.com/drewconway": "http://twitter.com/drewconway"
},
"nodes": {
"http://twitter.com/drewconway": {
"attributes": {
"exists": "1",
"bio": "Hopeful academic, data nerd, average hacker, student of conflict.",
"profile": "http://twitter.com/drewconway",
"rss": "http://twitter.com/statuses/user_timeline/drewconway.rss",
"atom": "http://twitter.com/statuses/user_timeline/drewconway.atom",
"url": "http://twitter.com/drewconway"
},
"nodes_referenced": {
...
},
"nodes_referenced_by": {
...
}
}
}
需要注意的是,SGA 爬虫仅扫描 Twitter 用户的公共链接,私人 Twitter 账户的数据不会包含在内。而且,由于 SGA 定期爬取活动链接,其缓存数据可能不反映最新的 Twitter 关系。
5. 构建网络的 R 代码实现
为了使用 SGA 数据构建 Twitter 网络,我们需要使用 R 语言和相关包。以下是具体步骤:
5.1 加载必要的 R 包
library(RCurl)
library(RJSONIO)
library(igraph)
5.2 编写核心函数
-
twitter.network函数 :查询 SGA 获取指定种子用户的 JSON 数据,并解析为自我网络。
twitter.network <- function(user) {
api.url <- paste("https://socialgraph.googleapis.com/lookup?q=http://twitter.com/",
user, "&edo=1&edi=1", sep="")
api.get <- getURL(api.url)
while(grepl("Service Unavailable. Please try again later.", api.get)) {
api.get <- getURL(api.url)
}
api.json <- fromJSON(api.get)
return(build.ego(api.json))
}
-
find.twitter函数 :从节点向量中筛选出真正的 Twitter 用户节点。
find.twitter <- function(node.vector) {
twitter.nodes <- node.vector[grepl("http://twitter.com/", node.vector, fixed=TRUE)]
if(length(twitter.nodes) > 0) {
twitter.users <- strsplit(twitter.nodes, "/")
user.vec <- sapply(1:length(twitter.users),
function(i) (ifelse(twitter.users[[i]][4]=="account",
NA, twitter.users[[i]][4])))
return(user.vec[which(!is.na(user.vec))])
} else {
return(character(0))
}
}
-
build.ego函数 :根据解析的 JSON 数据构建自我网络的边列表。
build.ego <- function(json) {
ego <- find.twitter(names(json$nodes))
nodes.out <- names(json$nodes[[1]]$nodes_referenced)
if(length(nodes.out) > 0) {
twitter.friends <- find.twitter(nodes.out)
if(length(twitter.friends) > 0) {
friends <- cbind(ego, twitter.friends)
} else {
friends <- c(integer(0), integer(0))
}
} else {
friends <- c(integer(0), integer(0))
}
nodes.in <- names(json$nodes[[1]]$nodes_referenced_by)
if(length(nodes.in) > 0) {
twitter.followers <- find.twitter(nodes.in)
if(length(twitter.followers) > 0) {
followers <- cbind(twitter.followers, ego)
} else {
followers <- c(integer(0), integer(0))
}
} else {
followers <- c(integer(0), integer(0))
}
ego.el <- rbind(friends, followers)
return(ego.el)
}
-
get.seeds函数 :从边列表中找出非种子节点的唯一节点集合。
get.seeds <- function(snowball.el, seed) {
new.seeds <- unique(c(snowball.el[,1],snowball.el[,2]))
return(new.seeds[which(new.seeds!=seed)])
}
-
twitter.snowball函数 :实现雪球采样,构建完整的网络。
twitter.snowball <- function(seed, k=2) {
snowball.el <- twitter.network(seed)
new.seeds <- get.seeds(snowball.el, seed)
rounds <- 1
all.nodes <- seed
while(rounds < k) {
next.seeds <- c()
for(user in new.seeds) {
if(!user %in% all.nodes) {
user.el <- twitter.network(user)
if(dim(user.el)[2] > 0) {
snowball.el <- rbind(snowball.el, user.el)
next.seeds <- c(next.seeds, get.seeds(user.el, user))
all.nodes <- c(all.nodes, user)
}
}
}
new.seeds <- unique(next.seeds)
new.seeds <- new.seeds[!which(new.seeds %in% all.nodes)]
rounds <- rounds + 1
}
snowball.el <- snowball.el[!duplicated(snowball.el),]
return(graph.edgelist(snowball.el))
}
以下是雪球采样的流程图:
graph TD;
A[开始] --> B[获取种子用户的自我网络];
B --> C[找出新种子节点];
C --> D{是否达到采样轮数};
D -- 否 --> E[遍历新种子节点];
E --> F{节点是否已访问};
F -- 否 --> G[获取节点的自我网络];
G --> H[更新边列表和新种子节点列表];
H --> I[更新已访问节点列表];
I --> E;
F -- 是 --> E;
D -- 是 --> J[去除边列表中的重复行];
J --> K[转换为 igraph 图对象];
K --> L[结束];
通过以上步骤,我们可以构建 Twitter 网络。接下来,我们将构建补充脚本,以便从给定的种子用户快速生成网络结构,并对这些图进行基本的结构分析,以发现其中的局部社区结构。
破解 Twitter 社交图数据:从理论到实践
6. 补充脚本构建与网络分析
在完成核心网络构建函数的编写后,我们可以进一步构建补充脚本,用于快速生成网络结构并进行基本的结构分析,以发现 Twitter 网络中的局部社区结构。
6.1 快速生成网络结构
利用之前编写的
twitter.snowball
函数,我们可以轻松地从指定的种子用户开始构建网络。以下是一个简单的示例,展示如何使用该函数:
# 选择一个种子用户
seed_user <- "drewconway"
# 进行两轮雪球采样
twitter_network <- twitter.snowball(seed_user, k = 2)
在这个示例中,我们选择了
drewconway
作为种子用户,并进行了两轮雪球采样,最终得到了一个
igraph
图对象
twitter_network
。
6.2 基本结构分析
得到网络对象后,我们可以使用
igraph
包提供的各种函数进行基本的结构分析。以下是一些常见的分析方法:
-
节点数量
:查看网络中包含的节点数量。
num_nodes <- vcount(twitter_network)
print(paste("网络中的节点数量:", num_nodes))
- 边的数量 :查看网络中边的数量。
num_edges <- ecount(twitter_network)
print(paste("网络中的边数量:", num_edges))
- 度分布 :分析节点的度分布,了解节点的连接情况。
degree_distribution <- degree(twitter_network)
hist(degree_distribution, main = "节点度分布", xlab = "节点度", ylab = "节点数量")
-
社区检测
:使用
igraph包中的社区检测算法,将网络划分为不同的社区。
# 使用 Louvain 算法进行社区检测
communities <- cluster_louvain(twitter_network)
# 查看社区数量
num_communities <- length(communities)
print(paste("检测到的社区数量:", num_communities))
以下是一个简单的表格,总结了上述分析方法及其作用:
| 分析方法 | 作用 |
| ---- | ---- |
| 节点数量 | 了解网络的规模 |
| 边的数量 | 了解网络的连接密度 |
| 度分布 | 分析节点的连接情况 |
| 社区检测 | 发现网络中的局部社区结构 |
7. 注意事项与拓展思考
在使用上述方法构建和分析 Twitter 网络时,有一些注意事项需要我们关注:
-
数据准确性
:由于 SGA 的数据可能存在不准确性,如缓存数据不反映最新的 Twitter 关系,我们在分析结果时需要谨慎对待。可以结合其他数据源或进行多次采样来提高数据的准确性。
-
速率限制
:虽然 SGA 每天允许 50,000 次查询,但对于一些大规模的网络构建,仍然可能会受到速率限制的影响。在实际应用中,我们可以合理安排查询时间,避免短时间内进行大量查询。
-
内存管理
:在处理大规模网络时,内存管理尤为重要。我们可以采用分批处理、及时释放不再使用的对象等方法来优化内存使用。
此外,我们还可以对现有方法进行拓展和改进:
-
多轮采样
:可以增加雪球采样的轮数,以获取更广泛的网络结构,但需要注意速率限制和内存使用。
-
结合其他数据源
:除了 SGA,还可以结合 Twitter 的官方 API 或其他第三方数据源,以获取更全面的 Twitter 数据。
-
更复杂的分析方法
:可以使用更复杂的社区检测算法、中心性分析等方法,深入挖掘 Twitter 网络的结构和特征。
以下是一个拓展思考的流程图,展示了如何对现有方法进行拓展和改进:
graph TD;
A[现有方法] --> B[增加采样轮数];
A --> C[结合其他数据源];
A --> D[使用更复杂的分析方法];
B --> E[获取更广泛的网络结构];
C --> F[获取更全面的数据];
D --> G[深入挖掘网络特征];
E --> H[优化速率限制和内存使用];
F --> H;
G --> H;
H --> I[改进后的方法];
8. 总结
通过本文的介绍,我们详细阐述了如何在不直接访问 Twitter API 的情况下,利用 Google 的 SocialGraph API 构建 Twitter 网络,并进行基本的结构分析。具体步骤如下:
1. 明确项目背景和目标,了解网络的基本概念,包括无向网络、有向网络和带标签边的有向网络。
2. 选择合适的数据来源,即 Google 的 SocialGraph API,并了解其查询参数和返回数据的结构。
3. 使用 R 语言和相关包编写核心函数,包括
twitter.network
、
find.twitter
、
build.ego
、
get.seeds
和
twitter.snowball
函数,实现网络的构建和雪球采样。
4. 构建补充脚本,快速生成网络结构并进行基本的结构分析,如节点数量、边的数量、度分布和社区检测等。
5. 注意数据准确性、速率限制和内存管理等问题,并对现有方法进行拓展和改进。
通过这些步骤,我们可以深入了解 Twitter 网络的结构和特征,为进一步的社交网络分析和应用提供基础。希望本文对您在处理 Twitter 数据和构建社交网络方面有所帮助。
超级会员免费看
1099

被折叠的 条评论
为什么被折叠?



