Nginx 用 deny 禁止某些IP访问

在nginx配置conf(一般在 /etc/nginx 目录 )中加入

include /etc/nginx/blackips.conf;

新建 blackips.conf

#屏蔽单个IP的命令是
deny 192.168.1.1
#封整个段即从123.0.0.1到123.255.255.254的命令
deny 123.0.0.0/8
#封IP段即从123.45.0.1到123.45.255.254的命令
deny 124.45.0.0/16
#封IP段即从123.45.6.1到123.45.6.254的命令是
deny 123.45.6.0/24


也就是说,如果最后的斜杠后的数值:
8:匹配后三位最大值的
16:匹配后两位最大值的
24:匹配后一位最大值的

用 nginx -t 命令检查配置是否成功

[root@xxx ~]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

最后 nginx -s reload 重启nginx 让配置生效

Laravel将采集WBE页面Table转换为CSV文件保存

一,准备环境 simple_html_dom

composer 找了很多源都没找到,我采用的是自己下载。

下载地址:https://sourceforge.net/projects/simplehtmldom/

解压后的文件

将 simple_html_dom.php 上传到 根目录/app/LIb/

并修改 composer.json 找到 autoload 引入类库

    "autoload": {
        "psr-4": {
            "App\\": "app/"
        },
        "classmap": [
            "database/seeds",
            "database/factories",
            "app/Lib/simple_html_dom.php"  //增加
        ]
    },

准备工作完成。

二,使用

//$content 为 包含 table 的页面源码   
 private function get_table($content ){
		//echo "Start table2csv".PHP_EOL;

		$html = new simple_html_dom();
		@$html->load($content);
		$re = [];
		$csv = "";
		
		$fp = fopen ( '示例.csv' , 'w' );

		foreach($html->find('tr') as $element)
		{
			$th = array();
			foreach( $element->find('th') as $row)  
			{
				$th [] = $row->plaintext;
			}
			if(!empty($th)){
				$re[] = $th;
				fputcsv ( $fp , $th );
			}
			$td = array();
			foreach( $element->find('td') as $row)  
			{
				$td [] = $row->plaintext;
			}
			
			if(!empty($td)){
				$re[] = $td;
				fputcsv ( $fp , $td);

			}
		}
		fclose( $fp );
		return $re;
		//echo "end table2csv".PHP_EOL;
    }

保存文件效果

某校考研录取成绩

Laravel Admin ModelTree 展示问题的解决方案

ModelTree 树形目录配置

问题描述

在grid 中引入ModelTree改造过的selectOptions()时,会出现 无法识别的bug。

勉强可以使用,但是展示效果非常差,占据页面很大宽度。

展示问题1
展示问题2

解决方案

实际上 ModelClass::selectOptions(); 返回结果就是一个Array,循环replace 了  即可。

$option_arr = ModelClass::selectOptions();
		
foreach($option_arr as $key=>$value){
		
    $option_arr[$key] =  str_replace(" ", ".",$value); //替换掉  
}

$grid->column('pid', __('分类'))->editable('select',$option_arr)->sortable();
修改完的页面。

基本上完美解决了展示问题。

Laravel Admin 树形目录管理

laravel  5.8.38    laravel admin  1.7.16  本文创建于 2020.8.17

完成后长这样

1.创建数据库

CREATE TABLE `art_paper` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(45) DEFAULT NULL,
  `fid` int(11) DEFAULT NULL,
  `index` int(11) DEFAULT NULL,
  `paperid` int(11) DEFAULT NULL,
  `creattime` datetime DEFAULT NULL,
  `state` int(11) DEFAULT '1',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=70 DEFAULT CHARSET=utf8;

2.生成model

php artisan code:models –table=art_paper

3.修改Models文件

namespace App\Models;

use Carbon\Carbon;
use Encore\Admin\Traits\ModelTree;  //add
use Illuminate\Database\Eloquent\Model;

class ArtPaper extends Model
{
	use ModelTree;  //add
	protected $table = 'art_paper';
	public $timestamps = false;

/**  add  **/
	public function __construct(array $attributes = [])
    {
        parent::__construct($attributes);

        $this->setParentColumn('fid');
        $this->setOrderColumn('index');
        $this->setTitleColumn('name');
    }
/**  add end **/

4.创建管理页面

php artisan admin:make ArtpaperController --model App\\Models\\ArtPaper

按提示加好路由
Add the following route to app/Admin/routes.php:
$router->resource('art-papers', ArtpaperController::class);

5.修改 Controller 文件

增加
use Encore\Admin\Tree;
use Encore\Admin\Layout\{Column, Row, Content};
use Encore\Admin\Widgets\Box;

//注意这里要新建index方法,在 grid 里写会报错    
 public function index(Content $content){
     	

        return $content
            ->header('逻辑800题目录')
            ->row(function (Row $row){
            	$row->column(6, $this->treeView()->render());
            	$row->column(6, function (Column $column){
                    $form = new \Encore\Admin\Widgets\Form();
                    $form->action(admin_url('logic-papers'));
                    $form->select('fid', __('父目录'))->options(LogicPaper::selectOptions());
                    $form->text('name', __('名称'))->required();
                    $form->number('index', __('排序'))->default(99)->help('越小越靠前');
                    $form->hidden('_token')->default(csrf_token());
                    $column->append((new Box(__('新建'), $form))->style('success'));
                });
            });
            //->body($tree);

    
    } 
    protected function treeView()
    {
    	 $tree = new Tree(new LogicPaper);
    	 return $tree;
        // return  CategoriesModel::tree(function (Tree $tree){
//             $tree->disableCreate(); // 关闭新增按钮
//             $tree->branch(function ($branch) {
//                 return "<strong>{$branch['cate_name']}</strong>"; // 标题添加strong标签
//             });
//         });
    }

laravel-admin 笔记

自动创建数据库

php artisan make:model User -crm
同时创建

修改  ./database/migrations/  下数据文件

   public function up()
    {
        Schema::create('columns', function(Blueprint $table)
        {
            $table->increments('id');
            $table->string('name')->nullable();
            $table->string('url')->nullable();
            $table->text('intr');
            $table->string('otherauthor');
            $table->integer('copyinfo');
            $table->timestamps();
        });
    }

执行  php artisan migrate

自动生成Admin管理页面

php artisan admin:make UserController –model App\\Models\\User

根据已存在的表创建model

php artisan code:models –table=test

创建指定链接的某个表

php artisan code:models –connection=mysql001 –table=test

微信小程序实践 – 陌生人地图

共享位置信息的小程序。

3.21 实现动态加载 markers

3.22 增加login & 通过服务器获取 openid

3.23 从服务器获取最新位置并更新 marker 位置

坑:

很多 function 里,需要 bind(this)。

map 的 translateMarker 方法,在动态加载 marker 时,需要等大概10秒以上才能成功找到markerId ,因为这个问题被坑了好久。


微信小程序代码:https://github.com/endpang/maps.cc

服务端代码

get.php

<?php

$redis = new Redis();
$redis->connect('127.0.0.1', 6688);
$redis->select(4);
/**/
file_put_contents("xx.log","[get]".json_encode($_REQUEST).PHP_EOL,FILE_APPEND);
$_REQUEST["id"] = 1;
$_REQUEST["latitude"] = round($_REQUEST["latitude"],4);
$_REQUEST["longitude"] = round($_REQUEST["longitude"],4);
$result = [
    'status' => 1,
    'count' => 1,
    'friend' => [$_REQUEST],
];
file_put_contents("xx.log","[response]".json_encode($result).PHP_EOL,FILE_APPEND);
//*/
echo json_encode($result);

test.php

<?php

$redis = new Redis();
$redis->connect('127.0.0.1', 6688);
$redis->select(4);
/**
$result = [
    'status' => 1,
    'userInfo' => "test",
];
//*/
$file = file_get_contents("https://api.weixin.qq.com/sns/jscode2session?appid=xxx&secret=xxx&js_code=".$_REQUEST['code']."&grant_type=authorization_code");
$user_array = json_decode($file,true);
//file_put_contents("xx.log",$file);
if(!empty($user_array["openid"])){
    $result["status"] = 1;
    $result["userInfo"]["openid"] = $user_array["openid"];
    $redis->set("user_".$user_array["openid"],$file);
}
echo json_encode($result);

PHP微信机器人

github https://github.com/HanSon/vbot
https://github.com/HanSon/my-vbot

修改文件 Example.php

$this->config =  $default_config;//array_merge($default_config, $this->config);

修改了一个文件,以实现收到文字回复笔画的功能
MessageHandler.php

如需主动发起消息请安装swoole,并修改config文件。

pecl install swoole

<?php

namespace Hanson\MyVbot;

use Hanson\MyVbot\Handlers\Contact\ColleagueGroup;
use Hanson\MyVbot\Handlers\Contact\ExperienceGroup;
use Hanson\MyVbot\Handlers\Contact\FeedbackGroup;
use Hanson\MyVbot\Handlers\Contact\Hanson;
use Hanson\MyVbot\Handlers\Type\RecallType;
use Hanson\MyVbot\Handlers\Type\TextType;
use Hanson\Vbot\Contact\Friends;
use Hanson\Vbot\Contact\Groups;
use Hanson\Vbot\Contact\Members;

use Hanson\Vbot\Message\Emoticon;
use Hanson\Vbot\Message\Text;
use Illuminate\Support\Collection;

class MessageHandler
{
    public static function messageHandler(Collection $message)
    {
        /** @var Friends $friends */
        $friends = vbot('friends');

        /** @var Members $members */
        $members = vbot('members');

        /** @var Groups $groups */
        $groups = vbot('groups');

        Hanson::messageHandler($message, $friends, $groups);
        ColleagueGroup::messageHandler($message, $friends, $groups);
        FeedbackGroup::messageHandler($message, $friends, $groups);
        ExperienceGroup::messageHandler($message, $friends, $groups);

        TextType::messageHandler($message, $friends, $groups);
        RecallType::messageHandler($message);

        if ($message['type'] === 'new_friend') {
            Text::send($message['from']['UserName'], '客官,等你很久了!感谢跟 vbot 交朋友,如果可以帮我点个star,谢谢了!https://github.com/HanSon/vbot');
            $groups->addMember($groups->getUsernameByNickname('Vbot 体验群'), $message['from']['UserName']);
            Text::send($message['from']['UserName'], '现在拉你进去vbot的测试群,进去后为了避免轰炸记得设置免骚扰哦!如果被不小心踢出群,跟我说声“拉我”我就会拉你进群的了。');
        }

        if ($message['type'] === 'emoticon' && random_int(0, 1)) {
            Emoticon::sendRandom($message['from']['UserName']);
        }

        // @todo
        if ($message['type'] === 'official') {
            vbot('console')->log('收到公众号消息:'.$message['title'].$message['description'].
                $message['app'].$message['url']);
        }

        if ($message['type'] === 'request_friend') {
            vbot('console')->log('收到好友申请:'.$message['info']['Content'].$message['avatar']);
            if (in_array($message['info']['Content'], ['echo', 'print_r', 'var_dump', 'print'])) {
                $friends->approve($message);
            }
        }
        //print_r($message);
        $re = 0;
        if($message["fromType"] == "Friend"){
            $nick = $message['from']['NickName'];
            $re = 1;
        }

        if($message["fromType"] == "Group"){
            $nick = $message['sender']['NickName'];
            if(@$message['isAt']){
                $re = 1;
            }
        }
        if($re ==1 ){

            $zi = mb_substr($message["message"],0,1,'utf-8');
            $uni = self::unicode_encode($zi);


            $var = trim($uni);
            $len = strlen($var)-1;
            $las = $var{$len};
            $url = "http://www.shufaji.com/datafile/bd/gif/".$las."/".$uni.".gif";
            //Text::send($message['from']['UserName'], "@".$nick." ".$url);
            if(!is_file(__DIR__."/img/".$uni.'.gif')){

                $img = @file_get_contents($url);

                if(!empty($img)){
                    file_put_contents(__DIR__."/img/".$uni.'.gif',$img);
                    Emoticon::send($message['from']['UserName'], __DIR__."/img/".$uni.".gif");

                }else{
                    Text::send($message['from']['UserName'], "@".$nick." 找不到这个字的笔顺".$url);
                }
            }else{
                Emoticon::send($message['from']['UserName'], __DIR__."/img/".$uni.".gif");
            }
        }


    }
    private static function unicode_encode($name)
    {
        $name = iconv('UTF-8', 'UCS-2', $name);
        $len = strlen($name);
        $str = '';
        for ($i = 0; $i < $len - 1; $i = $i + 2)
        {
            $c = $name[$i];
            $c2 = $name[$i + 1];
            if (ord($c) > 0)
            {    // 两个字节的文字
                $s1 = base_convert(ord($c), 10, 16);
                $s2 = base_convert(ord($c2), 10, 16);

                if(ord($c) < 16){
                    $s1 = "0".$s1;
                }
                if(ord($c2) < 16){
                    $s2 = "0".$s2;
                }
                $str .= $s1 . $s2;
            }
            else
            {
                $str .= $c2;
            }

        }
        return $str;
    }
}

itchat 调试完毕后,开始折腾聊天的server

https://ask.julyedu.com/question/7410

首先准备好  torch 环境,然后安装 nn,rnn,async

sudo ~/torch/install/bin/luarocks install nn
sudo ~/torch/install/bin/luarocks install rnn
sudo ~/torch/install/bin/luarocks install async penlight cutorch cunn

下载程序和语料

git clone --recursive https://github.com/rustcbf/chatbot-zh-torch7 #代码
git clone --recursive https://github.com/rustcbf/dgk_lost_conv #语料
git clone --recursive https://github.com/chenb67/neuralconvo #以上两个在此源码进行改进,可作为参考

将 dgk_lost_conv 里的  xiaohuangji50w_fenciA.zip 解压放到外层目录

th train.lua –cuda –dataset 5000 –hiddenSize 100

报错

-- Epoch 1 / 30

/root/torch/install/bin/luajit: ./seq2seq.lua:50: attempt to call field 'recursiveCopy' (a nil value)
stack traceback:
	./seq2seq.lua:50: in function 'forwardConnect'
	./seq2seq.lua:67: in function 'train'
	train.lua:90: in main chunk
	[C]: in function 'dofile'
	/root/torch/install/lib/luarocks/rocks/trepl/scm-1/bin/th:150: in main chunk
	[C]: at 0x00405d50

修改 seq2seq.lua 如下 (50 – 70 行间)

function Seq2Seq:forwardConnect(inputSeqLen)
  self.decoderLSTM.userPrevOutput =
    --nn.rnn.recursiveCopy(self.decoderLSTM.userPrevOutput, self.encoderLSTM.outputs[inputSeqLen])
    nn.utils.recursiveCopy(self.decoderLSTM.userPrevOutput, self.encoderLSTM.outputs[inputSeqLen])
  self.decoderLSTM.userPrevCell =
    nn.utils.recursiveCopy(self.decoderLSTM.userPrevCell, self.encoderLSTM.cells[inputSeqLen])
end

--[[ Backward coupling: Copy decoder gradients to encoder LSTM ]]--
function Seq2Seq:backwardConnect()
  if(self.encoderLSTM.userNextGradCell ~= nil) then
    self.encoderLSTM.userNextGradCell =
      nn.utils.recursiveCopy(self.encoderLSTM.userNextGradCell, self.decoderLSTM.userGradPrevCell)
  end
  if(self.encoderLSTM.gradPrevOutput ~= nil) then
    self.encoderLSTM.gradPrevOutput =
      nn.utils.recursiveCopy(self.encoderLSTM.gradPrevOutput, self.decoderLSTM.userGradPrevOutput)
  end
end

训练之,1080ti 一轮大概 两个多小时。。。 30轮估计需要70小时。妇女节后见了。

eval.lua 的时候报错,不明所以,先放弃这个了,试试别的。

/root/torch/install/bin/luajit: /root/torch/install/share/lua/5.1/nn/Container.lua:67:
In 3 module of nn.Sequential:
/root/torch/install/share/lua/5.1/torch/Tensor.lua:466: Wrong size for view. Input size: 100. Output size: 6561
stack traceback:
 [C]: in function 'error'
 /root/torch/install/share/lua/5.1/torch/Tensor.lua:466: in function 'view'
 /root/torch/install/share/lua/5.1/rnn/utils.lua:191: in function 'recursiveZeroMask'
 /root/torch/install/share/lua/5.1/rnn/MaskZero.lua:37: in function 'updateOutput'
 /root/torch/install/share/lua/5.1/rnn/Recursor.lua:13: in function '_updateOutput'
 /root/torch/install/share/lua/5.1/rnn/AbstractRecurrent.lua:50: in function 'updateOutput'
 /root/torch/install/share/lua/5.1/rnn/Sequencer.lua:53: in function </root/torch/install/share/lua/5.1/rnn/Sequencer.lua:34>
 [C]: in function 'xpcall'
 /root/torch/install/share/lua/5.1/nn/Container.lua:63: in function 'rethrowErrors'
 /root/torch/install/share/lua/5.1/nn/Sequential.lua:44: in function 'forward'
 ./seq2seq.lua:115: in function 'eval'
 eval.lua:90: in function 'say'
 eval.lua:105: in main chunk
 [C]: in function 'dofile'
 /root/torch/install/lib/luarocks/rocks/trepl/scm-1/bin/th:150: in main chunk
 [C]: at 0x00405d50

WARNING: If you see a stack trace below, it doesn't point to the place where this error occurred. Please use only the one above.
stack traceback:
 [C]: in function 'error'
 /root/torch/install/share/lua/5.1/nn/Container.lua:67: in function 'rethrowErrors'
 /root/torch/install/share/lua/5.1/nn/Sequential.lua:44: in function 'forward'
 ./seq2seq.lua:115: in function 'eval'
 eval.lua:90: in function 'say'
 eval.lua:105: in main chunk
 [C]: in function 'dofile'
 /root/torch/install/lib/luarocks/rocks/trepl/scm-1/bin/th:150: in main chunk
 [C]: at 0x00405d50

换一个试试 ,tensorflow 的,而且还比较新

git:   https://github.com/qhduan/just_another_seq2seq

包含还有一个英译汉的功能。

把之前下载的预料库中的 dgk_shooter_min.conv.zip 解压缩放到 chatbot 目录里

python3 train.py 训练之。

20轮,一轮大概30-40分钟(1070 显卡)

训练完之后执行  Python3 test.py 测试。

注意,tensorflow 在 1.4.1 下。

并修改 test.py  否则 sequence_to_sequence   会报

NotFoundError (see above for traceback): Key decoder/multi_rnn_cell/cell_0/Attention_Wrapper/lstm_cell/bias not found in checkpoint
    test(
        bidirectional=False,
        cell_type='lstm',
        depth=2,
        attention_type='Bahdanau',
        use_residual=False,
        use_dropout=False,
        time_major=False,
        hidden_units=512
    )

聊天效果,chatbot_ad 的还行

训练chatbot_ad 的时候,readme 少了一步  train_tfidf.py  。

Input Chat Sentence:我生病了
rl: ['我', '睡', '着', '了', '</s>', '</s>', '</s>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:我想我爱你
rl: ['我', '不', '知', '道', '你', '在', '说', '什', '么', '</s>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:你多大了?
rl: ['你', '看', '上', '去', '不', '错', '</s>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:你好吗?
rl: ['很', '高', '兴', '见', '到', '你', '</s>', '</s>', '<unk>', '<unk>']
Input Chat Sentence:什么时间了
rl: ['你', '要', '去', '哪', '儿', '</s>', '</s>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:去北京
rl: ['维', '克', '多', ',', '过', '来', '</s>', '</s>', '</s>', '</s>', '<unk>', '<unk>']
Input Chat Sentence:去哪?
rl: ['我', '们', '得', '走', '了', '</s>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:走
rl: ['我', '们', '得', '走', '了', '</s>', '</s>', '<unk>']
Input Chat Sentence:走了
rl: ['你', '们', '都', '走', '了', '</s>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:去哪
rl: ['我', '也', '不', '知', '道', '</s>', '</s>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:干啥
rl: ['你', '在', '干', '啥', '啊', '</s>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:他是谁?
rl: ['不', '知', '道', '为', '什', '么', '</s>', '</s>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:你是谁?
rl: ['我', '是', '麦', '克', '墨', '菲', '医', '生', '</s>', '<unk>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:你哎我 吗?
rl: ['我', '有', '话', '跟', '你', '说', '</s>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:你爱我 吗?
rl: ['什', '么', '东', '西', '?', '</s>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:你爱我吗?
rl: ['我', '爱', '你', ',', '宝', '贝', '</s>', '<unk>', '<unk>', '<unk>', '<unk>']
Input Chat Sentence:

chatbot_ad  用 bottle 改造了一个 url api接口用于和 itchat 对接。代码如下。

# -*- coding: utf-8 -*-
"""
对SequenceToSequence模型进行基本的参数组合测试
"""

import sys
import random
import pickle

import numpy as np
import tensorflow as tf
import bottle

sys.path.append('..')

from data_utils import batch_flow
from sequence_to_sequence import SequenceToSequence
from word_sequence import WordSequence # pylint: disable=unused-variable
random.seed(0)
np.random.seed(0)
tf.set_random_seed(0)
_, _, ws = pickle.load(open('chatbot.pkl', 'rb'))
config = tf.ConfigProto(
        device_count={'CPU': 1, 'GPU': 0},
        allow_soft_placement=True,
        log_device_placement=False
    )
save_path_rl = './s2ss_chatbot_ad.ckpt'
graph_rl = tf.Graph()

with graph_rl.as_default():
        model_rl = SequenceToSequence(
            input_vocab_size=len(ws),
            target_vocab_size=len(ws),
            batch_size=1,
            mode='decode',
            beam_width=12,
            bidirectional=False,
            cell_type='lstm',
            depth=1,
            attention_type='Bahdanau',
            use_residual=False,
            use_dropout=False,
            parallel_iterations=1,
            time_major=False,
            hidden_units=1024,
            share_embedding=True
        )
        init = tf.global_variables_initializer()
        sess_rl = tf.Session(config=config)
        sess_rl.run(init)
        model_rl.load(sess_rl, save_path_rl)


@bottle.route('/login/<w>', method='GET')
def do_login(w):
    user_text = w
    x_test = list(user_text.lower())
    x_test = [x_test]
    bar = batch_flow([x_test], [ws], 1)
    x, xl = next(bar)
    pred_rl = model_rl.predict(
            sess_rl,
            np.array(x),
            np.array(xl)
        ) 
    #word = bottle.request.forms.get("word")
    str2 = ''.join(str(i) for i in ws.inverse_transform(pred_rl[0]))
    return str2


bottle.run(host='0.0.0.0', port=8080)                                          #表示本机,接口是8080

注意不要聊的太猛,容易被腾讯封了。

[2018-03-12 02:34:54][INFO] please scan the qrCode with wechat.
[2018-03-12 02:35:01][INFO] please confirm login in wechat.
Array
(
    [ret] => 1203
    [message] => 当前登录环境异常。为了你的帐号安全,暂时不能登录web微信。你可以通过Windows微信、Mac微信或者手机客户端微信登录。
)
[2018-03-12 02:35:03] vbot.ERROR: Undefined index: skey [] []
PHP Fatal error:  Uncaught ErrorException: Undefined index: skey in /Users/zhiweipang/my-vbot/vendor/hanson/vbot/src/Core/Server.php:194

用 Python 分析自己的微信好友

#coding:utf-8 
import itchat
import re
from snownlp import SnowNLP
import jieba
import jieba.analyse
import numpy as np 
from collections import Counter
import matplotlib
from PIL import Image
from wordcloud import WordCloud, STOPWORDS
#matplotlib.use('qt4agg')  
from matplotlib.font_manager import *  
import matplotlib.pyplot as plt
itchat.auto_login(hotReload = True)
friends = itchat.get_friends(update = True)
#print(friends)

matplotlib.rcParams['axes.unicode_minus']=False

def analyseSex(friends):
    sexs = list(map(lambda x:x['Sex'],friends[1:]))
    counts = list(map(lambda x:x[1],Counter(sexs).items()))
    labels = ['Unknow','Male','Female']
    colors = ['red','yellowgreen','lightskyblue']
    plt.figure(figsize=(8,5), dpi=80)
    plt.axes(aspect=1)
    plt.pie(counts, #性别统计结果
            labels=labels, #性别展示标签
            colors=colors, #饼图区域配色
            labeldistance = 1.1, #标签距离圆点距离
            autopct = '%3.1f%%', #饼图区域文本格式
            shadow = False, #饼图是否显示阴影
            startangle = 90, #饼图起始角度
            pctdistance = 0.6 #饼图区域文本距离圆点距离
    )
    myfont = FontProperties(fname='/Library/Fonts/Microsoft/Microsoft Yahei.ttf')
    plt.legend(loc='upper right',)
    plt.title(u'%s的微信好友性别组成' % friends[0]['NickName'],fontproperties=myfont)
    plt.show()

def analyseSignature(friends):
    signatures = ''
    emotions = []
    pattern = re.compile("1f\d.+")
    for friend in friends:
        signature = friend['Signature']
        if(signature != None):
            signature = signature.strip().replace('span', '').replace('class', '').replace('emoji', '')
            signature = re.sub(r'1f(\d.+)','',signature)
            if(len(signature)>0):
                nlp = SnowNLP(signature)
                emotions.append(nlp.sentiments)
                signatures += ' '.join(jieba.analyse.extract_tags(signature,5))
    with open('signatures.txt','wt',encoding='utf-8') as file:
         file.write(signatures)

    # Sinature WordCloud
    back_coloring = np.array(Image.open('true.jpeg')) #随便找张图
    wordcloud = WordCloud(
        font_path='/Library/Fonts/Microsoft/Microsoft Yahei.ttf',  #字体文件
        background_color="white",
        max_words=1200,
        mask=back_coloring, 
        max_font_size=75,
        random_state=45,
        width=960, 
        height=720, 
        margin=15
    )

    wordcloud.generate(signatures)
    plt.imshow(wordcloud)
    plt.axis("off")
    plt.show()
    wordcloud.to_file('signatures.jpg')

    # Signature Emotional Judgment
    count_good = len(list(filter(lambda x:x>0.66,emotions)))
    count_normal = len(list(filter(lambda x:x>=0.33 and x<=0.66,emotions)))
    count_bad = len(list(filter(lambda x:x<0.33,emotions)))
    labels = [u'负面消极',u'中性',u'正面积极']
    values = (count_bad,count_normal,count_good)
    plt.rcParams['font.sans-serif'] = ['simHei'] 
    plt.rcParams['axes.unicode_minus'] = False
    plt.xlabel(u'情感判断')
    plt.ylabel(u'频数')
    plt.xticks(range(3),labels)
    plt.legend(loc='upper right',)
    plt.bar(range(3), values, color = 'rgb')
    plt.title(u'%s的微信好友签名信息情感分析' % friends[0]['NickName'])
    plt.show()
def analyseLocation(friends):   #生成一个所在城市 csv
    headers = ['NickName','Province','City']
    with open('location.csv','w',encoding='utf-8',newline='',) as csvFile:
        writer = csv.DictWriter(csvFile, headers)
        writer.writeheader()
        for friend in friends[1:]:
           row = {}
           row['NickName'] = friend['NickName']
           row['Province'] = friend['Province']
           row['City'] = friend['City']
           writer.writerow(row)
analyseLocation(friends)
analyseSignature(friends) 
#analyseSex(friends)

会弹出一个二维码用微信扫码登录。

等一会就能看到好友分布图了