python实战项目示例 :揭秘微信朋友圈

通过python,连接到微信账号,收集好友性别、城市、个性签名等公开信息,使用 Python 进行数据统计与分析,得到你专属的朋友圈的分析报告!

1、准备工作

1.1 环境配置

编译环境:Windows10

编程语言:Python3.6

编译器IDE:Pycharm

浏览器工具:Chrome浏览器

1.2 wxpy库介绍

采用第三方库wxpy进行微信登录和信息获取。

具体细节文档参考https://wxpy.readthedocs.io/zh/latest/

wxpy的一些常见的场景

  • 控制路由器、智能家居等具有开放接口的玩意儿
  • 运行脚本时自动把日志发送到你的微信
  • 加群主为好友,自动拉进群中
  • 跨号或跨群转发消息
  • 自动陪人聊天
  • 逗人玩
  • ...

总而言之,可用来实现各种微信个人号的自动化操作

1.3 wxpy库安装

wxpy 支持 Python 3.4-3.6,以及 2.7 版本

将下方命令中的 “pip” 替换为 “pip3” 或 “pip2”,可确保安装到对应的 Python 版本中

  1. 从 PYPI 官方源下载安装 (在国内可能比较慢或不稳定):
pip install -U wxpy
  1. 从豆瓣 PYPI 镜像源下载安装 (推荐国内用户选用):
pip install -U wxpy -i "https://pypi.doubanio.com/simple/"

1.4 登录微信

使用bot()登录微信

# 导入模块
from wxpy import *
# 初始化机器人,扫码登陆
bot = Bot()
# 获取所有好友
my_friends = bot.friends()
print(type(my_friends))

输出结果

Getting uuid of QR code.
Downloading QR code.
Please scan the QR code to log in.

弹出二维码进行扫描,扫描后输出

Please press confirm on your phone.
Loading the contact, this may take a little while.
Login successfully as XXXX
<class 'wxpy.api.chats.chats.Chats'>

2、微信好友性别统计

2.1 数据统计

统计性别

# 使用一个字典统计好友男性和女性的数量
sex_dict = {'male': 0, 'female': 0}

for friend in my_friends:
    # 统计性别
    if friend.sex == 1:
        sex_dict['male'] += 1
    elif friend.sex == 2:
        sex_dict['female'] += 1

print(sex_dict)

输出结果

{'male': 194, 'female': 166}

2.2 数据展示

采用 ECharts饼图 进行数据展示,左边为JSON展示代码,右边为数据图

 修改JSON代码如下

option = {
    title : {
        text: '微信好友性别比例',
        subtext: '真实数据',
        x:'center'
    },
    tooltip : {
        trigger: 'item',
        formatter: "{a} <br/>{b} : {c} ({d}%)"
    },
    legend: {
        orient : 'vertical',
        x : 'left',
        data:['男性','女性']
    },
    toolbox: {
        show : true,
        feature : {
            mark : {show: true},
            dataView : {show: true, readOnly: false},
            magicType : {
                show: true, 
                type: ['pie', 'funnel'],
                option: {
                    funnel: {
                        x: '25%',
                        width: '50%',
                        funnelAlign: 'left',
                        max: 1548
                    }
                }
            },
            restore : {show: true},
            saveAsImage : {show: true}
        }
    },
    calculable : true,
    series : [
        {
            name:'访问来源',
            type:'pie',
            radius : '55%',
            center: ['50%', '60%'],
            data:[
                {value:194, name:'男性'},
                {value:166, name:'女性'}
            ]
        }
    ]
};        

点击“刷新” ,显示如下

3、微信好友城市统计

3.1 数据统计

统计城市,此处只统计全国范围内的城市,如果写了国外,则记为“其他”。

# 使用一个字典统计各省好友数量
province_dict = {'北京': 0, '上海': 0, '天津': 0, '重庆': 0,
    '河北': 0, '山西': 0, '吉林': 0, '辽宁': 0, '黑龙江': 0,
    '陕西': 0, '甘肃': 0, '青海': 0, '山东': 0, '福建': 0,
    '浙江': 0, '台湾': 0, '河南': 0, '湖北': 0, '湖南': 0,
    '江西': 0, '江苏': 0, '安徽': 0, '广东': 0, '海南': 0,
    '四川': 0, '贵州': 0, '云南': 0,
    '内蒙古': 0, '新疆': 0, '宁夏': 0, '广西': 0, '西藏': 0,
    '香港': 0, '澳门': 0, '其他': 0 }

# 统计省份
for friend in my_friends:
    if friend.province in province_dict.keys():
        province_dict[friend.province] += 1
    if friend.province not in province_dict.keys():
        province_dict['其他'] += 1
    

# 为了方便数据的呈现,生成JSON Array格式数据
data = []
for key, value in province_dict.items():
    data.append({'name': key, 'value': value})

print(data)

输出结果

[{'name': '北京', 'value': 11}, {'name': '上海', 'value': 19}, {'name': '天津', 'value': 1}, {'name': '重庆', 'value': 5}, {'name': '河北', 'value': 4}, {'name': '山西', 'value': 1}, {'name': '吉林', 'value': 4}, {'name': '辽宁', 'value': 3}, {'name': '黑龙江', 'value': 4}, {'name': '陕西', 'value': 5}, {'name': '甘肃', 'value': 1}, {'name': '青海', 'value': 0}, {'name': '山东', 'value': 9}, {'name': '福建', 'value': 6}, {'name': '浙江', 'value': 128}, {'name': '台湾', 'value': 1}, {'name': '河南', 'value': 6}, {'name': '湖北', 'value': 7}, {'name': '湖南', 'value': 3}, {'name': '江西', 'value': 5}, {'name': '江苏', 'value': 15}, {'name': '安徽', 'value': 40}, {'name': '广东', 'value': 10}, {'name': '海南', 'value': 1}, {'name': '四川', 'value': 2}, {'name': '贵州', 'value': 0}, {'name': '云南', 'value': 0}, {'name': '内蒙古', 'value': 0}, {'name': '新疆', 'value': 0}, {'name': '宁夏', 'value': 0}, {'name': '广西', 'value': 1}, {'name': '西藏', 'value': 0}, {'name': '香港', 'value': 0}, {'name': '澳门', 'value': 0}, {'name': '其他', 'value': 112}]

3.2 数据展示

本想采用ECharts地图 来进行好友分布的数据呈现,结果网站在维护,无法打开,故通过下载echarts相关组件和js文件进行展示。

echarts官网下载  http://echarts.baidu.com/download.html

echarts源代码压缩包、jquery.js、echarts.min.js、china.js下载
链接:https://pan.baidu.com/s/1EIN4j0Avjut46Ljku1_SMQ 密码:9dtm
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=EDGE">
    <title>ECharts</title>
    <!--<link rel="stylesheet" type="text/css" href="css/main.css"/>-->
    <script src="jquery.js"></script>
    <script src="echarts.min.js"></script>
    <script src="china.js"></script>
    <style>#china-map {width:1000px; height: 1000px;margin: auto;}</style>
</head>
<body>
<div id="china-map"></div>
<script>
    var myChart = echarts.init(document.getElementById('china-map'));
    var option = {
        title : {
            text: '微信好友全国分布图',
            subtext: '真实数据',
            x:'center'
        },
        tooltip : {//提示框组件。
            trigger: 'item'//数据项图形触发,主要在散点图,饼图等无类目轴的图表中使用。
        },
        legend: {
            orient: 'horizontal',//图例的排列方向
            x:'left',//图例的位置
            data:['好友数量']
        },
        dataRange: {
            min: 0,
            max: 150,
            x: 'left',
            y: 'bottom',
            text:['高','低'],           // 文本,默认为数值文本
            calculable : true
        },
        //左侧小导航图标
        visualMap: {//颜色的设置  dataRange
            show : true,
            x: 'left',
            y: 'center',
            splitList: [
                {start: 0, end: 5},
                {start: 6, end: 10},
                {start: 11, end: 15},
                {start: 16, end: 20},
                {start: 21, end: 40, label: '21 到 40(自定义label)'},
                {start: 41, label: '5(自定义特殊颜色)', color: 'black'},
            ],
//            min: 0,
//            max: 2500,
//            calculable : true,//颜色呈条状
            text:['高','低'],// 文本,默认为数值文本
            <!--color: ['#E0022B', '#E09107', '#A3E00B']-->
            color: ['#5475f5', '#9feaa5', '#85daef','#74e2ca', '#e6ac53', '#9fb5ea']
        },
        toolbox: {//工具栏
            show: true,
            orient : 'vertical',//工具栏 icon 的布局朝向
            x: 'right',
            y: 'center',
            feature : {//各工具配置项。
                mark : {show: true},
                dataView : {show: true, readOnly: false},//数据视图工具,可以展现当前图表所用的数据,编辑后可以动态更新。
                restore : {show: true},//配置项还原。
                saveAsImage : {show: true}//保存为图片。
            }
        },
        roamController: {//控制地图的上下左右放大缩小 图上没有显示
            show: true,
            x: 'right',
            mapTypeControl: {
                'china': true
            }
        },
        //配置属性
        series : [
            {
                name: '好友数量',
                type: 'map',
                mapType: 'china',
                roam: false,//是否开启鼠标缩放和平移漫游
                //地图区域的多边形 图形样式;
                itemStyle:{
                    normal:{label:{show:true}},//是图形在默认状态下的样式;//是否显示标签
		            emphasis:{label:{show:true}}//是图形在高亮状态下的样式,比如在鼠标悬浮或者图例联动高亮时
                },
                top:"3%",//组件距离容器的距离
                data:[
                    {'name': '北京', 'value': 11},
                    {'name': '上海', 'value': 19},
                    {'name': '天津', 'value': 1},
                    {'name': '重庆', 'value': 5},
                    {'name': '河北', 'value': 4},
                    {'name': '山西', 'value': 1},
                    {'name': '吉林', 'value': 4},
                    {'name': '辽宁', 'value': 3},
                    {'name': '黑龙江', 'value': 4},
                    {'name': '陕西', 'value': 5},
                    {'name': '甘肃', 'value': 1},
                    {'name': '青海', 'value': 0},
                    {'name': '山东', 'value': 9},
                    {'name': '福建', 'value': 6},
                    {'name': '浙江', 'value': 128},
                    {'name': '台湾', 'value': 1},
                    {'name': '河南', 'value': 6},
                    {'name': '湖北', 'value': 7},
                    {'name': '湖南', 'value': 3},
                    {'name': '江西', 'value': 5},
                    {'name': '江苏', 'value': 15},
                    {'name': '安徽', 'value': 40},
                    {'name': '广东', 'value': 10},
                    {'name': '海南', 'value': 1},
                    {'name': '四川', 'value': 2},
                    {'name': '贵州', 'value': 0},
                    {'name': '云南', 'value': 0},
                    {'name': '内蒙古', 'value': 0},
                    {'name': '新疆', 'value': 0},
                    {'name': '宁夏', 'value': 0},
                    {'name': '广西', 'value': 1},
                    {'name': '西藏', 'value': 0},
                    {'name': '香港', 'value': 0},
                    {'name': '澳门', 'value': 0},
                ]
            }
        ]
    };
    myChart.setOption(option);
    myChart.on('mouseover', function (params) {
        var dataIndex = params.dataIndex;
        console.log(params);
    });
</script>
</body>
</html>

执行代码后,显示结果

4、微信好友个性签名统计

4.1 数据统计

统计个性签名,并存至本地'signatures.txt'

import re

def write_txt_file(path, txt):
    '''
    写入txt文本
    '''
    with open(path, 'a', encoding='gb18030', newline='') as f:
        f.write(txt)

# 统计签名
for friend in my_friends:
    # 对数据进行清洗,将标点符号等对词频统计造成影响的因素剔除
    pattern = re.compile(r'[一-龥]+')
    filterdata = re.findall(pattern, friend.signature)
    write_txt_file('signatures.txt', ' '.join(filterdata))

4.2 数据展示

4.2.1 安装库

对个性签名文档做文本处理,需要安装以下第三方库文件

pip install jieba
pip install pandas
pip install numpy
pip install scipy
pip install wordcloud

4.2.2 读取文本

def read_txt_file(path):
    '''
    读取txt文本
    '''
    with open(path, 'r', encoding='gb18030', newline='') as f:
        return f.read()

4.2.3 加载停用词

百度“停用词表”,网上资源有很多可以下载,此处由于个性签名的文本较小,故只剔除了部分常见停用词,如的、了、啦、于、都、一、而、就、之等。

content = read_txt_file(txt_filename)
segment = jieba.lcut(content)
words_df=pd.DataFrame({'segment':segment})

stopwords=pd.read_csv("stopwords.txt",index_col=False,quoting=3,sep=" ",names=['stopword'],encoding='utf-8')
words_df=words_df[~words_df.segment.isin(stopwords.stopword)]

4.2.4 词频统计

import numpy

words_stat = words_df.groupby(by=['segment'])['segment'].agg({"计数":numpy.size})
    words_stat = words_stat.reset_index().sort_values(by=["计数"],ascending=False)

4.2.5 词云展示

from scipy.misc import imread
from wordcloud import WordCloud, ImageColorGenerator


# 设置词云属性
color_mask = imread('python.jpg')
wordcloud = WordCloud(font_path="simhei.ttf",   # 设置字体可以显示中文
                background_color="white",       # 背景颜色
                max_words=100,                  # 词云显示的最大词数
                mask=color_mask,                # 设置背景图片
                max_font_size=150,              # 字体最大值
                random_state=42,
                width=1000, height=860, margin=2,# 设置图片默认的大小,但是如果使用背景图片的话,                                                   # 那么保存的图片大小将会按照其大小保存,margin为词语边缘距离
                )

# 生成词云, 可以用generate输入全部文本,也可以我们计算好词频后使用generate_from_frequencies函数
word_frequence = {x[0]:x[1]for x in words_stat.head(100).values}
print(word_frequence)
word_frequence_dict = {}
for key in word_frequence:
    word_frequence_dict[key] = word_frequence[key]

wordcloud.generate_from_frequencies(word_frequence_dict)
# 从背景图片生成颜色值  
image_colors = ImageColorGenerator(color_mask) 
# 重新上色
wordcloud.recolor(color_func=image_colors)
# 保存图片
wordcloud.to_file('output.jpg')
plt.imshow(wordcloud)
plt.axis("off")
plt.show()

下图左为背景图'background.jpg',右为词云图'output.png'

      

5、总结与展望

通过wxpy获取微信好友的性别、地区、个性签名等信息分析朋友圈好友基本属性,也可通过wxpy的其他功能进行微信分析,具体拓展可以根据兴趣继续实现https://wxpy.readthedocs.io/zh/latest/

6、完整代码

#-*- coding: utf-8 -*-
import re
import jieba
import numpy
import pandas as pd
import matplotlib.pyplot as plt
from scipy.misc import imread
from wordcloud import WordCloud, ImageColorGenerator
from wxpy import *


def write_txt_file(path, txt):
    '''
    写入txt文本
    '''
    with open(path, 'a', encoding='gb18030', newline='') as f:
        f.write(txt)

def read_txt_file(path):
    '''
    读取txt文本
    '''
    with open(path, 'r', encoding='gb18030', newline='') as f:
        return f.read()

def login():
    # 初始化机器人,扫码登陆
    bot = Bot()

    # 获取所有好友
    my_friends = bot.friends()

    print(type(my_friends))
    return my_friends

def show_sex_ratio(friends):
    # 使用一个字典统计好友男性和女性的数量
    sex_dict = {'male': 0, 'female': 0}

    for friend in friends:
        # 统计性别
        if friend.sex == 1:
            sex_dict['male'] += 1
        elif friend.sex == 2:
            sex_dict['female'] += 1

    print(sex_dict)

def show_area_distribution(friends):
    # 使用一个字典统计各省好友数量
    province_dict = {'北京': 0, '上海': 0, '天津': 0, '重庆': 0,
        '河北': 0, '山西': 0, '吉林': 0, '辽宁': 0, '黑龙江': 0,
        '陕西': 0, '甘肃': 0, '青海': 0, '山东': 0, '福建': 0,
        '浙江': 0, '台湾': 0, '河南': 0, '湖北': 0, '湖南': 0,
        '江西': 0, '江苏': 0, '安徽': 0, '广东': 0, '海南': 0,
        '四川': 0, '贵州': 0, '云南': 0,
        '内蒙古': 0, '新疆': 0, '宁夏': 0, '广西': 0, '西藏': 0,
        '香港': 0, '澳门': 0, '其他': 0 }

    # 统计省份
    for friend in friends:
        if friend.province in province_dict.keys():
            province_dict[friend.province] += 1
        if friend.province not in province_dict.keys():
            province_dict['其他'] += 1

    # 为了方便数据的呈现,生成JSON Array格式数据
    data = []
    for key, value in province_dict.items():
        data.append({'name': key, 'value': value})

    print(data)

def show_signature(friends):
    # 统计签名
    for friend in friends:
        # 对数据进行清洗,将标点符号等对词频统计造成影响的因素剔除
        pattern = re.compile(r'[一-龥]+')
        filterdata = re.findall(pattern, friend.signature)
        write_txt_file('signatures.txt', '\n'.join(filterdata))

    # 读取文件
    content = read_txt_file('signatures.txt')
    segment = jieba.lcut(content)
    words_df = pd.DataFrame({'segment':segment})

    # 读取stopwords
    stopwords = pd.read_csv("stopwords.txt",index_col=False,quoting=3,sep=" ",names=['stopword'],encoding='utf-8')
    words_df = words_df[~words_df.segment.isin(stopwords.stopword)]
    print(words_df)

    words_stat = words_df.groupby(by=['segment'])['segment'].agg({"计数":numpy.size})
    words_stat = words_stat.reset_index().sort_values(by=["计数"],ascending=False)

    # 设置词云属性
    color_mask = imread('python.jpg')
    wordcloud = WordCloud(font_path="simhei.ttf",   # 设置字体可以显示中文
                    background_color="white",       # 背景颜色
                    max_words=100,                  # 词云显示的最大词数
                    mask=color_mask,                # 设置背景图片
                    max_font_size=150,              # 字体最大值
                    random_state=42,
                    width=1000, height=860, margin=2,# 设置图片默认的大小,但是如果使用背景图片的话,
                                                     # 那么保存的图片大小将会按照其大小保存,margin为词语边缘距离
                    )

    # 生成词云, 可以用generate输入全部文本,也可以我们计算好词频后使用generate_from_frequencies函数
    word_frequence = {x[0]:x[1]for x in words_stat.head(100).values}
    print(word_frequence)
    word_frequence_dict = {}
    for key in word_frequence:
        word_frequence_dict[key] = word_frequence[key]

    wordcloud.generate_from_frequencies(word_frequence_dict)
    # 从背景图片生成颜色值
    image_colors = ImageColorGenerator(color_mask)
    # 重新上色
    wordcloud.recolor(color_func=image_colors)
    # 保存图片
    wordcloud.to_file('output.jpg')
    plt.imshow(wordcloud)
    plt.axis("off")
    plt.show()

def main():
    friends = login()
    show_sex_ratio(friends)
    show_area_distribution(friends)
    show_signature(friends)

if __name__ == '__main__':
    main()

猜你喜欢

转载自blog.csdn.net/m511655654/article/details/82752396