☰
微博转发社交网络分析:用Python和NetworkX绘制信息传播图谱
2026/9/29 21:00:43 网站建设 项目流程

简介:面向毕业设计、课程设计及初级项目开发者的新浪微博转发社交网络分析项目,基于Python实现,解决微博转发数据的采集与可视化问题。项目涵盖模拟登录、网页解析、转发数据提取与图形化展示的完整流程,核心模块分工明确:模拟登录负责会话保持,解析模块提取转发数据,绘图模块分别生成网络关系图与时间分布图,可直接运行并参照扩展。资源包共16个文件,以6个Python源码文件为主,附带1个Markdown项目文档、1份CSV转发数据、2张可视化结果图,以及若干TXT说明与编译缓存文件,整体体积仅486KB,轻量易上手。目前已有39人学习浏览,适合需要快速搭建社交网络分析原型或参考完整项目结构的开发者。

1. 微博转发社交网络分析:从一条微博看信息如何流动

一条微博被转发的次数再多,如果只看那个数字,你很难说清这波传播到底是怎么起来的:是谁最先撬动的?哪些节点把信息顶到了更大的圈层?转发关系天生就是一张有向网络,节点是用户,边是“谁转发了谁”。用Python把这条链抽出来,再借助NetworkX做中心性分析、社区划分和可视化,就能把“爆款微博”拆解成一张可解释的传播图谱。这个主题经常被选作毕业设计或课程设计,是因为它从前端爬虫到后端图算法,再到可视化展示,一条链路完整,任何一层都能检验开发能力。这篇文章按照实际可运行的思路来拆解:数据从哪来、怎么清洗、图怎么建、指标怎么算、可视化怎么做,最后连项目文档的结构也一并给你。

2. 数据获取与环境准备:用Python抓取微博转发列表

2.1 为什么先解决数据来源而不是直接算图

社交网络分析的核心是“关系”,而新浪微博的公开数据里,最容易拿到的关系就是转发关系。普通时间线数据需要反复清洗才能判断用户之间的交互,转发数据却天然带着“from A to B”的属性,一条转发记录里既有转发者,也有被转发的上级微博,甚至可以通过retweeted_status字段找到多级转发链。所以不少项目都愿意从转发数据入手,这也是这个题目能在一周内做出原型的原因。

抓取方案上,开放平台API对普通个人开发者不友好,既要申请又要配额,所以常见做法是用移动端网页接口做模拟请求。动手前有两个前提要确认:一是Python环境能正常装包,通常先完成python安装,再在vscode python环境配置里把解释器指到当前项目目录;二是准备好目标微博的mid值。所谓mid,就是微博页面URL里那串纯数字或字符串标识,后续所有请求参数都以它为基础。

2.2 用移动端接口抓转发列表

我在处理这个步骤时,用的接口是m.weibo.cn的container/getIndex,参数type固定为repost,mid填目标微博id,page控制翻页。这个接口不需要额外签名,只要带一个正常的User-Agent就能返回JSON,算是新手上路最稳的路径。

import time import requests def fetch_reposts(mid, page=1): """获取指定微博某一页的转发记录""" url = "https://m.weibo.cn/api/container/getIndex" params = { "type": "repost", "mid": mid, "page": page, "max_id": "", } headers = { "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15", "Referer": f"https://m.weibo.cn/detail/{mid}", } resp = requests.get(url, params=params, headers=headers, timeout=10) if resp.status_code != 200: print(f"页面请求失败,HTTP状态码:{resp.status_code}") return [] data = resp.json() reposts = [] for card in data.get("data", {}).get("cards", []): if card.get("card_type") == 9: reposts.append(card["mblog"]) return reposts if __name__ == "__main__": mid = "这里是你要分析的微博mid" first = fetch_reposts(mid, 1) print("第一页转发记录数:", len(first)) if first: print("示例用户名:", first[0]["user"]["screen_name"])

这段代码的关键在于resp.json()返回的结构里,真正的微博数据嵌在cards数组中,并且card_type为9的card才包含mblog对象。拿到mblog之后,user字段里有用户id和昵称,retweeted_status字段里是被转发的上一级微博,这个字段在构建转发链时至关重要。如果请求被限制,常见的处理方式是在循环翻页之间加time.sleep(2),并且把User-Agent换成真实浏览器里复制出来的完整字符串。

2.3 把原始JSON整理成关系表和CSV

直接操作嵌套JSON会让后续代码越来越难维护,所以我一般会先把每条转发拉平成一行记录,生成一个DataFrame再存成CSV。这里要特别处理一个坑:有的转发记录里retweeted_status为空,说明转发源头是原始微博;不为空则说明这条转发是针对另一条转发产生的,对应的是转发链上的中间层。

import pandas as pd def reposts_to_frame(reposts): """将API返回的转发列表转成结构化表格""" rows = [] for r in reposts: user = r.get("user", {}) row = { "repost_id": r.get("id"), "user_id": user.get("id"), "user_name": user.get("screen_name"), "created_at": r.get("created_at"), } retweeted = r.get("retweeted_status") if retweeted and retweeted.get("user"): row["parent_user_id"] = retweeted["user"].get("id") row["parent_user_name"] = retweeted["user"].get("screen_name") else: row["parent_user_id"] = "root" row["parent_user_name"] = "root" rows.append(row) return pd.DataFrame(rows) df = reposts_to_frame(first) df.to_csv("weibo_reposts.csv", index=False, encoding="utf-8-sig") print("保存成功,共", len(df), "行")

这里把没有retweeted_status的记录的父节点统一置为root,root代表原始微博作者。CSV编码用utf-8-sig而不是utf-8,是因为Windows上的Excel打开后者会出现中文乱码。created_at字段从API拿到的是类似“x分钟前”的相对时间,如果论文里需要画传播时间曲线,建议在抓取时另外记录一个完整的服务器时间戳字段。

字段名类型说明
repost_idstr转发微博自身的ID
user_idint转发者用户ID
user_namestr转发者昵称
parent_user_idstr被转发的上一层用户ID,无则root
parent_user_namestr被转发的上一层昵称,无则root
created_atstr转发时间相对表达

分页抓取时要留意接口返回的has_more字段,它为1时表示还有下一页。真实项目中,我会把fetch_reposts放进一个while循环,每抓一页就追加一次CSV,并记录当前页数,避免断点后从头再来。数据量不需要贪多,几千条转发足够完成一个毕业设计级别的分析,关键是字段完整和关系清晰。

3. 构建转发关系图:用NetworkX算关键节点和传播层

3.1 从CSV到有向图:两种转发链建模方式

把数据读入后,构建图有两种常见做法。第一种是让所有转发者节点都指向root,适合观察信息的一级扩散范围;第二种是利用parent_user_id字段保留真实路径,让转发者指向它的上一层转发者,这样能还原多级传播链。课程设计通常建议用第二种,因为更能体现“社交网络分析”的深度。

import networkx as nx import pandas as pd df = pd.read_csv("weibo_reposts.csv", dtype={"user_id": str, "parent_user_id": str}) G = nx.DiGraph() for _, row in df.iterrows(): current_node = row["user_name"] + "#" + row["user_id"] parent_node = row["parent_user_name"] + "#" + row["parent_user_id"] G.add_edge(current_node, parent_node) print("节点数:", G.number_of_nodes()) print("边数:", G.number_of_edges()) print("是否有自环:", any(n == m for n, m in G.edges()))

用用户名加井号加用户id是为了避免不同用户重名导致节点被合并。add_edge会自动创建不存在的节点,所以不需要先add_node。自环检测很重要,极少数数据会出现用户转发自己构造的异常记录,这类边在后续计算PageRank时会干扰结果。如果发现自环,直接调用G.remove_edge(u, v)删掉,再重新计算图规模。

3.2 度、介数、PageRank:选择适合转发网络的指标

NetworkX提供了成套的中心性算法,选对指标比调参更重要。转发网络里最常用的是四个:入度衡量直接影响力,出度衡量参与转发意愿,介数中心性衡量信息流通的桥梁作用,PageRank衡量重要程度。由于图规模可能上万,介数中心性需要限制采样数量,否则计算时间会指数级上升。

in_degree = dict(G.in_degree()) out_degree = dict(G.out_degree()) betweenness = nx.betweenness_centrality(G, k=200, normalized=True) pagerank = nx.pagerank(G, alpha=0.85)

betweenness_centrality的k参数设为200,表示随机采样200个源节点来估算介数,这是大图加速的常用做法。实际项目中如果节点数小于2000,可以直接去掉k参数做精确计算。pagerank的alpha默认是0.85,含义是随机游走过程中有15%的概率跳到一个非关联节点,在社交网络场景下通常不用改,除非你的转发图极度稀疏,比如平均度小于1时,再考虑调低到0.75。

把指标合并成DataFrame并排序:

scores = pd.DataFrame({ "in_degree": in_degree, "out_degree": out_degree, "betweenness": betweenness, "pagerank": pagerank, }) scores = scores.fillna(0).sort_values("betweenness", ascending=False) print(scores.head(10))

空值主要来自那些没有出现在PageRank结果里的节点,fillna(0)是为了让表格整体可对比。排序时建议以betweenness为主键,因为介数高的节点通常是连接不同粉丝群的桥梁,这类节点是分析报告里最值得写的对象。

指标NetworkX函数在转发场景中的含义
入度G.in_degree()被多少条转发间接引用
出度G.out_degree()主动转发了多少条内容
介数中心性nx.betweenness_centrality()信息经过该节点的桥梁程度
PageRanknx.pagerank()受其他高权重节点认可的程度

3.3 社区划分与传播层级分布

中心性回答的是“谁重要”,社区划分回答的是“信息在哪些圈层里流动”。转发网络通常有很强的聚类效应,一个话题先在垂直圈层内传播,再由桥节点带入另一个圈层。社区检测在Python里常被称作层次聚类,除了scikit-learn的AgglomerativeClustering,更贴合网络场景的是python-louvain库,这个库直接封装了Louvain算法。

import community as community_louvain G_undirected = G.to_undirected() partition = community_louvain.best_partition(G_undirected, random_state=42) community_count = max(partition.values()) + 1 print("检测到社区数量:", community_count)

这里必须转成无向图,因为Louvain算法是基于无向图定义模块度的。random_state固定为42是为了保证结果可复现,写进论文时尤其需要,否则每次运行社区划分结果不同,很难解释“上次和这次为什么不一样”。传播深度方面,可以计算从root出发到每个节点的最短路径长度,再统计层级分布,它反映信息最远穿透了几层。

from collections import Counter source = [n for n in G.nodes() if n.endswith("#root")][0] lengths = nx.single_source_shortest_path_length(G, source) layer_counter = Counter(lengths.values()) print("传播层级分布:", sorted(layer_counter.items()))

注意,有向图中如果root无法到达部分节点,single_source_shortest_path_length会自动忽略不可达节点。层级分布可以直观看出信息是在第一层就衰减,还是经过多次转发持续升温。如果发现大量节点停在第一层,说明原始微博的粉丝影响力有限,后续分析应当把重点放在高介数节点上,看看它们是如何把信息扩散到下一层的。

4. 可视化与结果验证:画出转发传播路径

4.1 用Matplotlib调整布局参数,让网络图不糊成一团

网络图最容易出现的问题是一堆节点挤在中间。解决办法不是换布局,而是调整布局参数和节点尺寸。spring_layout会模拟物理斥力,k控制节点间距,iterations控制迭代次数,值越大越稳定但越慢。我的经验是k取0.6左右,iterations设100,对几百个节点的图已经能看清主要结构。

import matplotlib.pyplot as plt pos = nx.spring_layout(G, k=0.6, iterations=100) plt.figure(figsize=(14, 10)) node_sizes = [100 + 50 * G.in_degree(n) for n in G.nodes()] node_colors = ["lightcoral" if n.endswith("#root") else "lightblue" for n in G.nodes()] nx.draw_networkx_edges(G, pos, alpha=0.2, arrows=False) nx.draw_networkx_nodes(G, pos, node_size=node_sizes, node_color=node_colors) nx.draw_networkx_labels(G, pos, labels={n: n.split("#")[0] for n in G.nodes()}, font_size=6) plt.axis("off") plt.savefig("repost_network.png", dpi=300)

这里直接对draw_networkx_edges设置arrows=False,因为转发关系有方向性,但箭头在静态图里会遮挡标签,而且视觉上会显得很乱。节点颜色用root区分源节点。标签只显示用户名,去掉#后面的id部分,既保留了可读性,又不会把图撑爆。如果图太大,可以把labels参数移除,否则文字会互相覆盖。保存时dpi=300是标准论文图片要求,插入Word后也够清晰。

当节点数超过300时,matplotlib的矢量绘制会非常慢,保存的PNG也会很大。这时候常见做法是从全图中提取度排名前50的节点组成子图,再看局部结构:

top_nodes = sorted(G.nodes(), key=G.in_degree, reverse=True)[:50] subgraph = G.subgraph(top_nodes) pos_sub = nx.spring_layout(subgraph, k=0.8, seed=2024) print("子图节点数:", subgraph.number_of_nodes())
参数推荐值作用
spring_layout.k0.5 ~ 0.8节点间斥力距离,越大越松散
spring_layout.iterations100迭代次数,越大布局越稳定
draw_networkx_nodes.node_size100 ~ 1000入度大的节点突出显示
savefig.dpi300满足论文插图清晰度

4.2 用Pyecharts生成可交互HTML图

答辩时静态PNG不够直观,评审更想看到能拖拽、能点开节点看到用户名的交互效果。Pyecharts的Graph类型很适合做这件事,它接受nodes和links两类数据,和NetworkX的图结构几乎一一对应。

from pyecharts import options as opts from pyecharts.charts import Graph nodes = [ {"name": n, "symbolSize": 8 + 6 * G.in_degree(n), "value": n.split("#")[0]} for n in G.nodes() ] links = [{"source": u, "target": v} for u, v in G.edges()] graph = Graph(init_opts=opts.InitOpts(width="1200px", height="800px")) graph.add( series_name="转发网络", nodes=nodes, links=links, repulsion=8000, edge_length=120, linestyle_opts=opts.LineStyleOpts(curve=0.2, opacity=0.4), ) graph.render("repost_network.html") print("已生成HTML")

symbolSize根据入度缩放,节点越大代表被转发越多。repulsion是节点之间的斥力系数,节点太多就调大,不然会叠在一起。edge_length控制边的长度,配合curve做曲线可以让交叉边更清晰。links里的source和target必须和nodes里的name完全一致,这是最常见的报错原因,所以我在构建links时直接复用G.edges()的输出,避免手写字符串出错。

这个HTML文件直接用浏览器打开,支持鼠标拖拽和缩放,答辩演示时比静态图更有说服力。如果想保留社区划分结果,可以给不同社区的节点设置不同颜色,做法是把partition字典映射成颜色列表,再传给itemStyle字段,但要注意Pyecharts的itemStyle是单节点属性,需要逐个节点生成配置。

4.3 用转发层级柱状图验证计算是否正确

可视化不只是看效果,更是验证计算是否正确的手段。我在实际项目里会随手画一个“传播层级—节点数”的柱状图,如果root能到达的深度很浅,或者出现大量不可达节点,说明数据清洗阶段有边方向搞错了。

import matplotlib.pyplot as plt level_counts = {} for node, level in lengths.items(): level_counts[level] = level_counts.get(level, 0) + 1 plt.bar(list(level_counts.keys()), list(level_counts.values())) plt.xlabel("传播层级") plt.ylabel("节点数") plt.title("转发层级分布") plt.savefig("layer_distribution.png", dpi=200)

柱状图特别适合放在项目文档的“数据概览”部分,几行代码就能直观展示转发衰减趋势。另一个更严谨的验证方法是:挑一个你认识的意见领袖节点,如果它的PageRank排名异常高,而它的入度并不高,通常说明图里存在伪造转发关系或者parent字段错位,需要回头检查数据预处理阶段是不是把直接转发误判成了二级转发。

5. 从代码到文档:把项目做成能直接答辩的交付物

5.1 一个能一键运行的目录结构

交付级项目必备的是模块分离,不能让所有代码堆在一个文件里。我一般会按职责拆成几个脚本,每个脚本只做一件事,这样答辩时单独运行visualize.py也不会触发重新抓数据。

weibo_sna/ ├── requirements.txt ├── config.py ├── crawler.py ├── graph_builder.py ├── analyze.py ├── visualize.py ├── data/ │ └── weibo_reposts.csv ├── output/ │ ├── repost_network.png │ └── repost_network.html └── docs/ ├── 01_需求说明.md ├── 02_数据与算法设计.md └── 03_测试与结论.md

config.py里放mid和请求头,crawler.py只负责抓取,graph_builder.py读取CSV并构建NetworkX图,analyze.py计算指标,visualize.py生成图片和HTML。requirements.txt里写networkx、pandas、requests、matplotlib、pyecharts、python-louvain这几个核心依赖,不带版本号让环境更宽松。

5.2 项目文档写什么,以及答辩演示技巧

项目文档不一定要写到几十页,但需求分析、数据说明、算法设计、测试结论四块必不可少。需求分析写“做这个系统为了解决什么问题”,数据说明写抓了多少条转发、字段怎么定义、清洗规则是什么,算法设计写为什么选用PageRank而不是HITS,测试结论给出关键指标表。测试结论部分建议用一个表格,让评审一眼看到结果:

指标数值说明
转发总数2841抓取时间范围内
root一级转发占比63%大部分信息直接来自源微博
高介数节点数7介数中心性大于0.1的节点
最大社区节点数1320Louvain划分结果

答辩时,先把output目录下的repost_network.html拖进浏览器,展示交互式网络图,再切回命令行运行python analyze.py输出指标表格,最后用层级柱状图说明传播衰减规律。这个顺序比从头开始爬数据要快,也更能让评审看到你已经把抓取、建模、分析、可视化整条链路走通。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询