尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Python社交网络分析:微博转发关系图与关键节点挖掘

Python社交网络分析:微博转发关系图与关键节点挖掘 简介这份资源面向社交网络分析与数据挖掘的学习者以新浪微博转发数据为案例演示如何用Python完成从模拟登录、网页解析到网络图与时间图绘制的完整分析流程适合具备Python基础、希望上手真实社交网络项目实践的学生与开发者。压缩包共16个文件约485KB以6个py脚本为核心辅以3个pyc编译文件、3个txt说明、2张png结果图、1个csv数据文件和1个md文档涵盖登录、编码、抓取、绘图等模块结构紧凑便于按需查阅。目前已有771人学习下载。读者可借助data.csv中的转发关系数据结合network_graph.py与time_graph.py复现网络结构图与时间演化图理解节点关系、传播路径与时间分布的分析思路同时参考模拟登录与网页解析模块掌握数据采集的关键环节为社交网络分析类课程设计或毕业项目提供可运行的参考方案。1. 新浪微博转发网络分析从一条热搜到一张可计算的关系图一条微博被转发了 3 万次这 3 万次转发里藏着什么是几个大 V 在带节奏还是普通用户层层扩散是同一批账号反复搬运还是跨圈层穿透这些问题靠肉眼翻评论区是翻不出来的但用 Python 把转发关系抽成一张有向图答案就变成可计算、可排序、可验证的数字。新浪微博转发社交网络分析做的就是这件事把「谁转了谁」变成节点和边再用图算法找出关键传播者、社群结构和扩散路径。它适合三类人——做舆情监测的运营、写人工智能大作业的学生、以及想拿真实社交数据练手 Python 数据分析的工程师。整条链路不复杂抓数据、建图、算指标、可视化一台普通笔记本就能跑通。2. 数据从哪来微博转发链的抓取与清洗2.1 转发关系的本质是一棵有向树微博的转发机制决定了它的数据结构。每条原创微博是根节点直接转发原创的是第一层转发「转发微博」的是第二层以此类推。每次转发记录里通常包含转发者 ID、被转发微博 ID、被转发用户 ID、转发时间、转发文本。把这些字段抽出来就得到一条有向边转发者 → 被转发者。所有边合起来就是一张以原创者为根的传播树。这里有个容易搞混的点微博的repost接口返回的转发列表很多时候只给到「转发者」和「原微博」不直接给「被转发的那条中间微博」。也就是说你拿到的是转发者和根节点之间的关系而不是严格的父子关系。这会导致建出来的图是一张「星型多层」的混合结构而不是精确的树。做传播路径分析时这个差异必须心里有数否则算出来的层级深度会偏浅。常见做法是如果只做关键节点识别和社群划分用「转发者 → 原创者」的二分关系就够了如果要还原精确传播链得额外抓每条转发微博的pid父微博 ID再自底向上拼接。前者成本低后者更准但抓取量翻倍。2.2 用 Python 抓取转发数据的骨架代码下面这段代码是抓取转发列表的最小骨架用的是微博开放平台风格的接口结构。实际运行时你需要替换成自己申请到的凭证并且遵守平台的访问频率限制。import requests import time import pandas as pd # 替换为你自己的访问凭证不要硬编码在公开仓库里 ACCESS_TOKEN your_token_here BASE_URL https://api.weibo.com/2/statuses/repost_timeline.json def fetch_reposts(weibo_id, max_pages50): 抓取指定微博的转发列表返回 DataFrame records [] page 1 while page max_pages: params { access_token: ACCESS_TOKEN, id: weibo_id, count: 200, # 单页最大条数平台通常限制在 200 page: page } resp requests.get(BASE_URL, paramsparams, timeout10) if resp.status_code ! 200: print(f第 {page} 页请求失败状态码 {resp.status_code}) break data resp.json() items data.get(reposts, []) if not items: break for it in items: records.append({ repost_uid: it[user][id], # 转发者 repost_name: it[user][screen_name], origin_uid: it.get(retweeted_status, {}).get(user, {}).get(id), origin_mid: weibo_id, repost_time: it[created_at], repost_text: it.get(text, ) }) page 1 time.sleep(1.2) # 控制频率避免触发限流 return pd.DataFrame(records) df fetch_reposts(你的微博ID) df.to_csv(reposts_raw.csv, indexFalse) print(f共抓取 {len(df)} 条转发记录)逻辑说明循环翻页每页取 200 条遇到空列表就停。time.sleep(1.2)是血泪经验频率太快会被限流甚至临时封禁。参数上count不要超过平台上限max_pages根据目标微博的实际转发量设3 万转发大约 150 页。2.3 清洗阶段必须处理的四类脏数据抓下来的原始数据不能直接建图至少要做四件事。第一去重同一用户可能多次转发同一条微博做节点分析时通常只保留首次转发做传播强度分析时才保留全部。第二剔除空 ID有些转发者账号已注销repost_uid为空这类边要丢掉。第三时间格式统一微博返回的时间是Wed Jan 01 12:00:00 0800 2025这种格式用pd.to_datetime转换时要注意时区。第四文本清洗转发文本里带//某某:的嵌套内容做文本分析前要剥离否则词频统计会被用户名污染。# 清洗示例 df df.dropna(subset[repost_uid, origin_uid]) df df.drop_duplicates(subset[repost_uid], keepfirst) df[repost_time] pd.to_datetime(df[repost_time], utcTrue) df[repost_time] df[repost_time].dt.tz_convert(Asia/Shanghai) import re def clean_text(t): t re.sub(r//[^:][:], , t) # 去掉嵌套转发标记 t re.sub(r[\w\u4e00-\u9fa5-], , t) # 去掉 提及 return t.strip() df[clean_text] df[repost_text].apply(clean_text) print(df.shape)这一步做完数据才算能进图。很多人跳过清洗直接nx.from_pandas_edgelist结果图里一堆孤立节点和自环后面算中心性全是噪声。3. 用 NetworkX 建图节点、边和三种中心性指标3.1 从边表到有向图的最小代码有了干净的边表建图只要几行。关键决策是用有向图还是无向图。转发关系天然有方向用DiGraph更合适但如果你只关心「谁和谁有连接」而不关心方向Graph算社群发现会更快。import networkx as nx G nx.DiGraph() for _, row in df.iterrows(): G.add_edge(row[repost_uid], row[origin_uid]) print(f节点数: {G.number_of_nodes()}) print(f边数: {G.number_of_edges()}) print(f平均入度: {sum(d for _, d in G.in_degree()) / G.number_of_nodes():.2f})逻辑说明add_edge会自动创建不存在的节点所以不用单独add_node。in_degree对转发网络特别有意义——入度高说明这个账号被很多人转发是传播中的关键接收方出度高说明这个账号频繁转发别人是活跃扩散者。参数上要注意如果转发量超过 10 万iterrows会慢换成G.add_edges_from(zip(df[repost_uid], df[origin_uid]))能快一个数量级。3.2 度中心性、介数中心性、特征向量中心性怎么选三个指标回答三个不同问题别混用。指标回答的问题计算复杂度适用场景度中心性谁被转发最多O(E)快速找头部账号介数中心性谁在传播路径上最关键O(VE)找桥梁节点、跨圈层推手特征向量中心性谁连接的人本身也重要O(VE) 迭代找圈层核心deg_c nx.degree_centrality(G) bet_c nx.betweenness_centrality(G, k500) # k 为采样数大图必须采样 eig_c nx.eigenvector_centrality(G, max_iter500) top_deg sorted(deg_c.items(), keylambda x: x[1], reverseTrue)[:10] top_bet sorted(bet_c.items(), keylambda x: x[1], reverseTrue)[:10] print(度中心性 Top10:, top_deg) print(介数中心性 Top10:, top_bet)参数说明betweenness_centrality的k参数是采样节点数不设的话在几万节点的图上会跑到天荒地老。k500是精度和速度的折中实测和全量计算的 Top20 重合度在 85% 以上。eigenvector_centrality的max_iter设小了会不收敛报PowerIterationFailedConvergence调到 500 到 1000 之间比较稳。一个反直觉的结论度中心性最高的账号介数中心性往往不是最高。被转发最多的大 V 通常是「终点」而介数高的是那些把不同圈层连起来的中间账号粉丝可能只有几千但传播穿透力更强。3.3 社群发现Louvain 和标签传播的取舍转发网络里天然存在社群——同一事件下不同立场、不同圈层的用户会形成转发聚集。用python-louvain或networkx自带的louvain_communities可以自动划分。from networkx.algorithms.community import louvain_communities communities louvain_communities(G.to_undirected(), seed42) print(f划分出 {len(communities)} 个社群) for i, c in enumerate(sorted(communities, keylen, reverseTrue)[:5]): print(f社群 {i}: {len(c)} 个节点)逻辑说明Louvain 基于模块度优化不需要预设社群数量适合探索性分析。seed固定后结果可复现。如果图特别大超过 50 万边换成标签传播label_propagation_communities速度快但结果稳定性差每次跑可能不一样。注意社群发现前通常转成无向图因为 Louvain 的模块度定义基于无向边。转的时候G.to_undirected()会把双向边合并这是预期行为。4. 可视化与指标验证把图变成能看懂的结论4.1 用 Pyecharts 画可交互的转发网络Matplotlib 画大图会糊成一团Pyecharts 的Graph支持交互缩放和拖拽更适合展示。下面是最小可用配置。from pyecharts import options as opts from pyecharts.charts import Graph # 只取度最高的 200 个节点避免浏览器卡死 top_nodes sorted(G.degree, keylambda x: x[1], reverseTrue)[:200] node_set {n for n, _ in top_nodes} sub_edges [(u, v) for u, v in G.edges if u in node_set and v in node_set] nodes [{name: str(n), symbolSize: min(5 G.degree(n) * 0.5, 40)} for n, _ in top_nodes] links [{source: str(u), target: str(v)} for u, v in sub_edges] g ( Graph() .add(, nodes, links, repulsion8000, layoutforce) .set_global_opts(title_optsopts.TitleOpts(title微博转发网络)) ) g.render(repost_network.html)参数说明repulsion是斥力系数越大节点越分散8000 适合 200 节点规模。symbolSize按度数缩放让关键节点视觉上更突出。layoutforce是力导向布局计算量随节点数平方增长超过 500 节点建议换circular或先做子图采样。4.2 怎么验证你算出来的关键节点是对的中心性算完不能直接信要做交叉验证。我一般用三个办法。第一把 Top20 关键节点拿去和实际转发文本里的高频 账号对比重合度高说明指标有效。第二看这些节点的时间分布——如果关键节点集中在事件爆发的前 2 小时说明它们是早期推手如果集中在后期可能是跟风大 V。第三做移除实验把 Top10 节点从图里删掉看最大连通子图缩小多少缩小越多说明它们越关键。import networkx as nx def robustness_check(G, top_nodes): 移除关键节点后看最大连通子图的变化 G_copy G.copy() G_copy.remove_nodes_from(top_nodes) largest max(nx.connected_components(G_copy.to_undirected()), keylen) return len(largest) / G.number_of_nodes() ratio robustness_check(G, [n for n, _ in top_deg[:10]]) print(f移除 Top10 后最大连通子图占比: {ratio:.2%})如果这个比例低于 30%说明网络高度依赖少数节点传播是「中心化」的如果高于 70%说明网络很分散删掉几个大 V 影响不大。5. 避坑与排查转发网络分析里最容易翻车的五件事5.1 抓取时被限流返回空列表但不报错现象翻到第 20 页突然返回空reposts程序以为抓完了实际是被限流。 原因微博接口对高频请求会返回空数据而不是 403这是最坑的地方。 解决在循环里加判断——如果连续两页为空且总页数远小于预期打印警告并time.sleep(60)后重试而不是直接break。5.2 建图后节点数远大于预期现象一条 3 万转发的微博建出来的图有 8 万个节点。 原因origin_uid字段在某些转发记录里指向的是中间转发者而非原创者导致把中间节点也当成了根。 解决统一用原创微博的user.id作为所有边的终点或者在清洗阶段过滤掉origin_uid不等于原创者 ID 的记录。5.3 介数中心性跑了一小时还没出结果现象betweenness_centrality在 5 万节点的图上卡死。 原因精确介数中心性是 O(VE)5 万节点 20 万边就是 100 亿次操作。 解决必须设k参数做采样k500通常够用或者先用度中心性筛出前 5000 个节点只在子图上算精确介数。5.4 Louvain 每次跑出来的社群数量不一样现象同样的图两次运行社群数差了好几个。 原因Louvain 有随机初始化不设seed结果不可复现。 解决louvain_communities(G, seed42)固定随机种子。如果还想要更稳定用greedy_modularity_communities它是确定性的但速度慢一些。5.5 Pyecharts 渲染的图打开就卡死现象render出来的 HTML 一打开浏览器就无响应。 原因节点数超过 1000 时力导向布局的 JS 计算量爆炸。 解决渲染前做子图采样只保留度最高的 200 到 300 个节点或者改用layoutcircular牺牲美观换流畅。6. 进阶技巧用时间切片看传播的「引爆点」静态图只能告诉你谁重要动态图才能告诉你什么时候开始爆。我习惯把转发记录按 10 分钟切片每个切片建一张子图算节点数和边数画成时间序列。引爆点通常表现为节点数突然从几十跳到几千的那一个切片。import pandas as pd import networkx as nx df[time_bin] df[repost_time].dt.floor(10min) timeline [] for t, group in df.groupby(time_bin): sub_g nx.DiGraph() sub_g.add_edges_from(zip(group[repost_uid], group[origin_uid])) timeline.append({ time: t, nodes: sub_g.number_of_nodes(), edges: sub_g.number_of_edges(), density: nx.density(sub_g) }) tl pd.DataFrame(timeline).sort_values(time) tl[node_growth] tl[nodes].pct_change() print(tl.nlargest(5, node_growth)[[time, nodes, node_growth]])逻辑说明floor(10min)把时间对齐到 10 分钟桶pct_change算环比增长率增长最快的桶就是引爆点。参数上切片粒度根据事件持续时间调——持续 2 小时的事件用 5 分钟持续 3 天的用 1 小时。拿到引爆点后再去看那个时间窗口里新出现的关键节点往往就是真正的「点火者」而不是事后统计里粉丝最多的大 V。这个技巧我在多次舆情复盘里用过比单纯看总转发量有用得多。最后一个习惯每次分析完把原始数据、清洗脚本、图对象和结论指标打包存一份标注抓取时间和参数。社交网络数据时效性强同一事件隔一周再抓转发结构可能完全变了。没有这份存档你后面想复现或对比都无从下手。希望帮到你。本文还有配套的精品资源点击获取
返回列表