
简介面向希望用Python开展社交媒体数据分析的开发者这份资料聚焦微信好友场景演示从数据提取、清洗到可视化与社交网络洞察的完整流程。压缩包共5个文件1个Python脚本负责调用微信接口或第三方库获取好友信息并基于Pandas完成缺失值处理、异常值过滤和字段统一1个whl组件包为pyecharts可视化依赖3个HTML文件分别呈现城市、性别、省份分布图表便于对照脚本输出校验结果。资源仅2.79MB轻量易学当前已有71人学习下载。除脚本与成品页面外学习者还能从中理解NetworkX社交网络图构建、Louvain社区发现以及结合聊天记录的情感分析思路适合具备基础Python语法、想通过真实项目掌握数据分析与可视化工具链的读者快速上手。1. 微信好友分析到底分析什么从RAR包文件反推数据流“微信好友分析.rar”是我在整理老项目时常看到的一个压缩包里面只有一个 Python 脚本和三个 HTML 文件外加一个 pyecharts 的 wheel 安装包。文件不多但它恰好串起了一条完整的数据分析流水线登录微信获取好友数据按性别、省份、城市三个维度聚合最后用 pyecharts 生成可交互的 HTML 图表。对刚入门的数据分析师来说这是一个低门槛、高反馈的练手样本对有经验的开发者它则是一个需要迁移到新版 echarts 接口和老 Python 版本兼容问题的遗留项目。相比单纯看教程直接对着这份 RAR 里的脚本和产物反推实现能更快搞明白微信社交数据里哪些字段值得挖、哪些坑必须绕开。2. 数据采集与字段清洗把微信好友转成可统计的 DataFrame2.1 好友信息从哪里来itchat 登录与好友列表微信官方并没有直接开放“好友资料”查询接口当年最常见的做法是借用itchat这个 Python 库走 Web 微信协议完成登录和数据拉取。虽然现在 Web 微信通道已经被大量限制但分析逻辑仍然值得复用脚本先登录再拉取完整好友列表后续所有统计都基于这次拉取结果。# 简化后的采集段实际项目里应加 try/finally 处理扫码超时 import itchat itchat.auto_login(hotReloadTrue, enableCmdQR2) friends itchat.get_friends(updateTrue)这里有两个参数值得注意hotReloadTrue会把登录状态缓存到本地文件下次运行不需要重新扫码enableCmdQR2让二维码以 ASCII 形式打印到终端适合在无图形界面的服务器上跑。get_friends(updateTrue)返回的是一个 JSON 列表列表的[0]是当前登录账号自己的信息从friends[1:]开始才是真正的好友数据。很多统计结果多出一个人就是因为在聚合时忘了把[0]去掉。2.2 字段筛选与文本噪声处理好友列表里一个字段就是一个属性包括NickName、Sex、Province、City、Signature、RemarkName等。实际操作中我不会全部保留只选取分析要用的列避免 DataFrame 过大且难以排查异常值。import pandas as pd df pd.DataFrame(friends[1:]) cols [NickName, Sex, Province, City, Signature, RemarkName] df df[cols].copy() df[City] df[City].str.replace(市, , regexFalse) df df.replace({Province: {北京: 北京市, 天津: 天津市, 河北: 河北省}}, regexFalse)str.replace(市, , regexFalse)用来把“北京市”里的“市”去掉为后续重新匹配省份全称做准备regexFalse表示按普通字符串匹配而不是正则表达式替换。微信资料中City字段经常是空的或者只填到“朝阳区”这样的区级地名清洗阶段要统一成“城市名“或“未知”。文本噪声主要集中在Signature签名里表情符号、控制字符、零宽字符都会让后续词云或情感分析输出乱码。我一般会用一个正则函数把所有不可见字符去掉。import re def clean_text(s): if not isinstance(s, str): return s re.sub(r[\x00-\x1f\x7f], , s) s re.sub(r[\ufe0f\u200d], , s) return s.strip() df[Signature] df[Signature].map(clean_text)第一行正则剔除了 ASCII 控制符第二行删掉常见的变体选择符和零宽连接符。这两个字符在微信签名里高频出现外部复制来的文本尤其多不过滤的话最终抓出来的“频率最高词”很可能是一堆看不出含义的方块字。2.3 性别和地理字段的映射微信Sex字段取值是数字0 表示未知1 表示男性2 表示女性。为了让图表图例可读需要映射成中文。省份字段则更麻烦旧版 pyecharts 的Map组件依赖的是“北京市”“广东省”“广西壮族自治区”这类官方全称如果直接使用微信返回的“广东”“广西”地图匹配不上最终结果会是“默认展示中国地图但各省数值全为 0”。字段原始取值转换规则Sex0 / 1 / 2未知 / 男 / 女Province北京 / 广东 / 广西北京市 / 广东省 / 广西壮族自治区City朝阳区 / None朝阳区 / 未知代码实现如下sex_mapping {0: 未知, 1: 男, 2: 女} df[SexName] df[Sex].map(sex_mapping) province_fix { 北京: 北京市, 天津: 天津市, 上海: 上海市, 重庆: 重庆市, 广西: 广西壮族自治区, 内蒙古: 内蒙古自治区, 新疆: 新疆维吾尔自治区, 西藏: 西藏自治区, 宁夏: 宁夏回族自治区, 香港: 香港特别行政区, 澳门: 澳门特别行政区 } df[ProvinceFixed] df[Province].fillna(未知).map(lambda x: province_fix.get(x, x))fillna(未知)先把缺失值统一成字符串map再对已知的省名做补全。这一段做完df里就有了SexName和ProvinceFixed两个可统计的列后面三张图的聚合操作全部基于它们。3. 三张 HTML 图用 pyecharts 完成性别、省份与城市可视化3.1 pyecharts 0.1.9.4 的 API 与当前版本差异这个 RAR 里带的 wheel 是pyecharts-0.1.9.4属于老一代接口所有图表类都从pyecharts顶层导入图表对象用add()方法添加数据。现在的 pyecharts 是 1.x/2.x 时代包结构改成了from pyecharts.charts import Bar样式参数也从title_pos变成了set_global_opts()。如果你想按本文复现请不要盲目 pip 安装最新版否则脚本里所有add()参数都会报错。能力旧版 0.1.9.4新版 1.x导入方式from pyecharts import Piefrom pyecharts.charts import Pie参数入口add()add()set_global_opts()标题设置titlexx通过opts.TitleOpts地图Map.add(..., maptypechina)Map.add(..., maptypechina)仍需注册地图3.2 性别分布Pie 图分析 RAR 里的analysisSex.html文件最稳妥的方案就是用 Pie 图展示男、女、未知三类好友的数量占比。聚合统计放在图表绘制前完成这样后续add()只需要接收列表类型不必传 DataFrame。sex_count df[SexName].value_counts() from pyecharts import Pie sex_pie Pie(微信好友性别分布, title_poscenter, width800, height500) sex_pie.add( 性别, sex_count.index.tolist(), sex_count.values.tolist(), is_label_showTrue, legend_orientvertical, legend_posleft ) sex_pie.render(analysisSex.html)sex_count.index.tolist()是标签列表sex_count.values.tolist()是对应数值列表。is_label_showTrue会在扇区上直接显示具体人数legend_orientvertical让图例纵向排列避免把饼图区域挤得太扁。3.3 省份地图Map 图省份地图能直观看出好友的地理分布。核心是前面已经处理过的ProvinceFixed列聚合后按旧版 Map 组件要求的名称格式传入。prov_count df[ProvinceFixed].value_counts() prov_names prov_count.index.tolist() prov_values prov_count.values.tolist() from pyecharts import Map province_map Map(微信好友省份分布, width1200, height600) province_map.add( 好友数, prov_names, prov_values, maptypechina, is_visualmapTrue, visual_range[0, max(prov_values)], is_label_showTrue ) province_map.render(analysisProvince.html)is_visualmapTrue会打开地图右侧的渐变颜色条数值越大颜色越深。visual_range手动指定色条上下界如果不写某些只有个位数好友的省份会与 0 值显示为同一个颜色看起来像“空白省份”。maptypechina是旧版内置地图名称新版则需要单独引入并注册china.js地图。3.4 城市分布Bar 图城市字段的可用性比省份差很多。很多用户没填城市或者填成了“市辖区”这种无法归属到具体城市的文本。常见的做法是生成柱状图时只取value_counts()的前 20 位避免一堆“未知”把真实城市名淹没。city_count df[City].replace(, 未知).value_counts().head(20) from pyecharts import Bar city_bar Bar(微信好友城市 Top20, title_poscenter, width1000, height600) city_bar.add( 城市, city_count.index.tolist(), city_count.values.tolist(), is_convertTrue, is_label_showTrue, xaxis_rotate45 ) city_bar.render(analysisCity.html)is_convertTrue的作用是把纵向柱状图转换成横向条形图。微信里常见的城市名如“海淀区”“朝阳区”可能很长横向排列时文字不会被旋转遮挡xaxis_rotate45是对非横向模式的一个兜底当图表没有被转换时X 轴标签倾斜 45 度防止重叠。这里的输出文件名和 RAR 里的analysisCity.html保持一致方便后续直接对比。4. RAR 内 wheel 文件的使用与运行排错4.1 解压 RAR 后先核对文件结构先把压缩包解压到独立目录不要直接放在桌面或者有中文空格的路径下避免旧版 Python 库和脚本路径解析出问题。unzip 微信好友分析.rar -d wechat_analysis cd wechat_analysis ls -la我注意到这个包内的文件非常精简文件说明analysis_friends.py主脚本负责数据采集、聚合、绘图analysisCity.html城市分布柱状图输出analysisProvince.html省份分布地图输出analysisSex.html性别分布饼图输出pyecharts-0.1.9.4-py2.py3-none-any.whl固定版本依赖py2.py3-none-any.whl这个文件名意味着它是跨 Python 2/3 的纯 Python wheel理论上兼容范围很广但实际运行却不一定。4.2 安装旧版 pyecharts wheel 并运行 analysis_friends.py由于项目依赖的是旧版 pyecharts直接用 Python 3.10 以上安装很可能会在导入阶段报ImportError: cannot import name Iterable from collections的错误。更稳妥的方式是先创建一个 Python 3.6 虚拟环境再安装指定 wheel。python3.6 -m venv .venv source .venv/bin/activate pip install pyecharts-0.1.9.4-py2.py3-none-any.whl python analysis_friends.pypython3.6如果不在系统 PATH 里需要先安装对应版本解释器。安装过程中 pip 会自动拉取 pyecharts 的依赖如jinja2和lml这些依赖包至今仍在 PyPI 上所以不会出现“找不到包”的离线问题。脚本运行后终端会打印扫码信息用与微信绑定的手机扫码确认后同目录下会生成三个 HTML 文件。4.3 常见异常对照表与处理异常现象大概率原因解决办法ImportError: cannot import name Iterable from collectionsPython 3.9 起collections.Iterable迁移到collections.abc修改 pyecharts 源码中的 import 或换 Python 3.6浏览器打开 HTML 后地图空白页面引用的远程 echarts/map JS 加载失败下载对应 JS替换为本地路径登录时报“当前微信账号无法使用网页版登录”Web 微信通道被限制脚本分析思路保留改用 vCard/通讯录导出后再分析运行时出现乱码城市名CSV/HTML 编码不是 UTF-8在pd.read_csv或DataFrame.to_csv中指定encodingutf-8处理Iterable兼容性时我会直接用 sed 把源码里两处from collections import Iterable改成from collections.abc import Iterable因为 0.1.9.4 源码中只有两处用到这个导入。你不必重新打包 wheel在已解压的 site-packages 目录里直接改再重跑脚本即可。4.4 离线 HTML 依赖替换旧版 pyecharts 生成的 HTML 文件默认引用远程 JS 地址比如cdn.jsdelivr.net或echarts.baidu.com。如果运行机器离线生成的 HTML 页面会只有一个空白壳。最直接的修正方法是在线时把echarts.min.js和china.js下载到本地再把 HTML 里的远程地址替换成相对路径。# 用本地文件替换远程 echarts 引用注意不同版本地址略有差异 sed -i s#https://cdn.jsdelivr.net/npm/echarts4[^]*#./echarts.min.js#g analysis*.html sed -i s#https://cdn.jsdelivr.net/npm/echarts4/map/js/china[^]*#./china.js#g analysis*.htmlsed -i的#作为分隔符因为原 URL 里含有/直接使用/做分隔符会破坏命令结构。第一条把远程 echarts 主库指向本地echarts.min.js第二条把中国地图数据指向本地china.js。替换完成后重新打开 HTML所有图形应当都能正常渲染。5. 把分析扩展到词云与签名验证二次开发的两个增量点5.1 用已有 Signature 字段跑词云原有的analysis_friends.py并没有处理Signature字段但这恰好是微信好友画像里最有人味的部分。既然我们已经拿到了清洗后的 DataFrame完全可以在不重新抓取好友的情况下再生成一张签名词云。import jieba from collections import Counter signature_text .join(df[Signature].fillna().tolist()) words jieba.cut(signature_text) keywords [w for w in words if len(w.strip()) 1] word_counter Counter(keywords).most_common(50)len(w.strip()) 1这个过滤条件会丢弃单字和无意义空格避免最终词云被“的”“了”“是”之类的字刷屏。得到word_counter后你可以直接输出成 CSV也可以继续交给wordcloud库生成 PNG。我用这个方式给一个 400 人好友的账号做过测试出现频率最高的前三位词基本能准确反映那位好友的职业和生活状态。5.2 用 pandas 做交叉验证确认三张图数字闭合三张图表在生成前各自做了value_counts()但三者之间并没有自动校验。当你把代码改到一半时很常见的情况是性别饼图的总数比好友总数少 2省份地图又少 1而你还不知道是哪里的清洗逻辑把数据丢了。所以我会在绘图结束后加一段交叉验证代码。sex_total df[SexName].value_counts().sum() prov_total df[ProvinceFixed].value_counts().sum() city_top_total df[City].replace(, 未知).value_counts().head(20).sum() assert sex_total len(df), f性别汇总缺失: {sex_total} vs {len(df)} assert prov_total len(df), f省份汇总缺失: {prov_total} vs {len(df)} assert city_top_total len(df), 城市 Top20 数量异常大于总好友数性别和省份的汇总必须等于len(df)因为每一个好友都会有一个性别值最差情况下也会归入“未知”。城市 Top20 则不必等于总数因为有很多城市名在“其他”范围内但如果city_top_total远小于len(df) / 2说明城市字段缺失严重地图的整体参考价值就已经很低了。用这种方式验证过之后再打开三个 HTML 文件数据之间的可信度才有保证。df.to_csv(friend_snapshot.csv, indexFalse, encodingutf-8-sig)导出一份清洗后的快照 CSV既方便你下次用其他工具二次分析也能和新的抓取结果做增量对比。本文还有配套的精品资源点击获取