Flask是Django之外用Python实现的另一优秀Web框架
今天用它
Flask以自由、灵活著称。在开发一些小应用的时候,使用Django会有“杀鸡用牛刀”的赶脚,而使用Flask就非常合适。本文将使用Flask开发一个微博用户画像的生成器,最后效果如下:
开发步骤如下:
- 抓取微博用户数据;
- 分析数据,生成用户画像;
- 网站实现,美化界面。
一、微博抓取
这里使用移动端的微博(m.weibo.cn),以为例。本教程使用chrome浏览器进行调试。
- 在“发现”中搜索“古力娜扎”,点击进入她的主页;
- 开始分析请求报文, 右击打开调试窗口,选择调试窗口的"网络(network)"标签;
- 选择"Preserve Log",刷新页面;
- 分析各条请求过程可以发现博文的数据是从https://m.weibo.cn/api/container/getIndex?XXX 类似的地址中得到的。其主要参数为type(固定值)、value(博主ID)、containerid(标识,请求中返回)、page(页码)
下面开始实现爬取博文的代码。
# 导入相关库<p>import requests</p>
from time import sleep
# 定义获取博主信息的函数<p># 参数uid为博主的id</p><p>defget_user_info(uid):</p><p> # 发送请求</p><p> result = requests.get('https://m.weibo.cn/api/container/getIndex?type=uid&value={}'</p><p> .format(uid))</p><p> json_data = result.json() # 获取繁华信息中json内容</p><p> userinfo = {</p><p> 'name': json_data['userInfo']['screen_name'], # 获取用户头像</p><p> 'description': json_data['userInfo']['description'], # 获取用户描述</p><p> 'follow_count': json_data['userInfo']['follow_count'], # 获取关注数</p><p> 'followers_count': json_data['userInfo']['followers_count'], # 获取粉丝数</p><p> 'profile_image_url': json_data['userInfo']['profile_image_url'], # 获取头像</p><p> 'verified_reason': json_data['userInfo']['verified_reason'], # 认证信息</p><p> 'containerid': json_data['tabsInfo']['tabs'][1]['containerid'] # 此字段在获取博文中需要</p><p> }</p><p> # 获取性别,微博中m表示男性,f表示女性</p><p> if json_data['userInfo']['gender'] == 'm':</p><p> gender = '男'</p><p> elif json_data['userInfo']['gender'] == 'f':</p><p> gender = '女'</p><p> else:</p><p> gender = '未知'</p><p> userinfo['gender'] = gender</p>
return userinfo
# 获取古力娜扎信息
userinfo = get_user_info('1350995007')
# 信息如下
userinfo
{'containerid': '1076031350995007',<p>'description': '工作请联系:nazhagongzuo@163.com',</p><p>'follow_count': 529,</p><p>'followers_count': 12042995,</p><p>'name': '我是娜扎',</p><p>'profile_image_url': 'https://tvax2.sinaimg.cn/crop.0.0.1242.1242.180/50868c3fly8fevjzsp2j4j20yi0yi419.jpg',</p>
'verified_reason': '演员,代表作《择天记》'}
In [33]:
# 循环获取所有博文<p>defget_all_post(uid,containerid):</p><p> # 从第一页开始</p><p> page = 0</p><p> # 这个用来存放博文列表</p><p> posts = []</p><p> while True:</p><p> # 请求博文列表</p><p> result = requests.get('https://m.weibo.cn/api/container/getIndex?type=uid&value={}&containerid={}&page={}'</p><p> .format(uid, containerid, page))</p><p> json_data = result.json()</p><p> # 当博文获取完毕,退出循环</p><p> if not json_data['cards']:</p><p> break</p><p> # 循环将新的博文加入列表</p><p> for i in json_data['cards']:</p><p> posts.append(i['mblog']['text'])</p><p> # 停顿半秒,避免被反爬虫</p><p> sleep(0.5)</p><p> # 跳转至下一页</p><p> page += 1</p><p> # 返回所有博文</p>
return posts
posts = get_all_post('1350995007', '1076031350995007')
# 查看博文条数
len(posts)
1279
# 显示前3个
posts[:3]
至此,用户的数据已准备就绪,接下来开始生成用户画像。
二、生成用户画像
1.提取关键词
这里从博文列表中提取出关键字,分析出博主的发表的热词
import jieba.analyse<p>from html2text import html2text</p><p>content = 'n'.join([html2text(i) for i in posts])</p><p># 这里使用jieba的textrank提取出1000个关键词及其比重</p><p>result = jieba.analyse.textrank(content, topK=1000, withWeight=True)</p><p># 生成关键词比重字典</p><p>keywords = dict()</p><p>for i in result:</p>
keywords[i[0]] = i[1]
2.生成词云图
from PIL import Image, ImageSequence<p>import numpy as np</p><p>import matplotlib.pyplot as plt</p><p>from wordcloud import WordCloud, ImageColorGenerator</p><p># 初始化图片</p><p>image = Image.open('./static/images/personas.png')</p><p>graph = np.array(image)</p><p># 生成云图,这里需要注意的是WordCloud默认不支持中文,所以这里需要加载中文黑体字库</p><p>wc = WordCloud(font_path='./fonts/simhei.ttf',</p><p> background_color='white', max_words=300, mask=graph)</p><p>wc.generate_from_frequencies(keywords)</p>
image_color = ImageColorGenerator(graph)
# 显示图片<p>plt.imshow(wc)</p><p>plt.imshow(wc.recolor(color_func=image_color))</p><p>plt.axis("off") # 关闭图像坐标系</p>
plt.show()
三、实现Flask应用
开发Flask不像Django那么复杂,小应用几个文件就可以完成。步骤如下:
1. 安装
使用pip安装flask,命令如下:
$ pip install flask
2.实现应用逻辑
简单来说,一个Flask应用就是一个Flask类,由route函数控制它的url请求。代码实现如下:
# app.py<p>from flask import Flask</p><p>import requests</p><p>from PIL import Image, ImageSequence</p><p>import numpy as np</p><p>import matplotlib.pyplot as plt</p><p>from wordcloud import WordCloud, ImageColorGenerator</p><p>import jieba.analyse</p><p>from html2text import html2text</p><p>from time import sleep</p><p>from collections import OrderedDict</p><p>from flask import render_template, request</p><p># 创建一个Flask应用</p><p>app = Flask(__name__)</p><p>##################################</p><p># 微博相关函数 #</p><p># 定义获取博主信息的函数</p><p># 参数uid为博主的id</p><p>def get_user_info(uid):</p><p> # 发送请求</p><p> result = requests.get('https://m.weibo.cn/api/container/getIndex?type=uid&value={}'</p><p> .format(uid))</p><p> json_data = result.json() # 获取繁华信息中json内容</p><p> # 获取性别,微博中m表示男性,f表示女性</p><p> if json_data['userInfo']['gender'] == 'm':</p><p> gender = '男'</p><p> elif json_data['userInfo']['gender'] == 'f':</p><p> gender = '女'</p><p> else:</p><p> gender = '未知'</p><p> userinfo = OrderedDict()</p><p> userinfo['昵称'] = json_data['userInfo']['screen_name'] # 获取用户头像</p><p> userinfo['性别'] = gender # 性别</p><p> userinfo['关注数'] = json_data['userInfo']['follow_count'] # 获取关注数</p><p> userinfo['粉丝数'] = json_data['userInfo']['followers_count'] # 获取粉丝数</p><p> userinfo['认证信息'] = json_data['userInfo']['verified_reason'] # 获取粉丝数</p><p> userinfo['描述'] = json_data['userInfo']['description'] # 获取粉丝数</p><p> data = {</p><p> 'profile_image_url': json_data['userInfo']['profile_image_url'], # 获取头像</p><p> 'containerid': json_data['tabsInfo']['tabs'][1]['containerid'], # 此字段在获取博文中需要</p><p> 'userinfo': '<br>'.join(['{}:{}'.format(k, v) for (k,v) in userinfo.items()])</p><p> }</p><p> return data</p><p># 循环获取所有博文</p><p>def get_all_post(uid, containerid):</p><p> # 从第一页开始</p><p> page = 0</p><p> # 这个用来存放博文列表</p><p> posts = []</p><p> while True:</p><p> # 请求博文列表</p><p> result = requests.get('https://m.weibo.cn/api/container/getIndex?type=uid&value={}&containerid={}&page={}'</p><p> .format(uid, containerid, page))</p><p> json_data = result.json()</p><p> # 当博文获取完毕,退出循环</p><p> if not json_data['cards']:</p><p> break</p><p> # 循环将新的博文加入列表</p><p> for i in json_data['cards']:</p><p> posts.append(i['mblog']['text'])</p><p> # 停顿半秒,避免被反爬虫</p><p> sleep(0.5)</p><p> # 跳转至下一页</p><p> page += 1</p><p> # 返回所有博文</p><p> return posts</p><p>##############################</p><p>## 云图相关函数</p><p># 生成云图</p><p>def generate_personas(uid, data_list):</p><p> content = '<br>'.join([html2text(i) for i in data_list])</p><p> # 这里使用jieba的textrank提取出1000个关键词及其比重</p><p> result = jieba.analyse.textrank(content, topK=1000, withWeight=True)</p><p> # 生成关键词比重字典</p><p> keywords = dict()</p><p> for i in result:</p><p> keywords[i[0]] = i[1]</p><p> # 初始化图片</p><p> image = Image.open('./static/images/personas.png')</p><p> graph = np.array(image)</p><p> # 生成云图,这里需要注意的是WordCloud默认不支持中文,所以这里需要加载中文黑体字库</p><p> wc = WordCloud(font_path='./static/fonts/simhei.ttf',</p><p> background_color='white', max_words=300, mask=graph)</p><p> wc.generate_from_frequencies(keywords)</p><p> image_color = ImageColorGenerator(graph)</p><p> plt.imshow(wc)</p><p> plt.imshow(wc.recolor(color_func=image_color))</p><p> plt.axis("off") # 关闭图像坐标系</p><p> dest_img = './static/personas/{}.png'.format(uid)</p><p> plt.savefig(dest_img)</p><p> return dest_img</p><p>#######################################</p><p># 定义路由</p><p># 指定根路径请求的响应函数</p><p>@app.route('/', methods=['GET', 'POST'])</p><p>def index():</p><p> # 初始化模版数据为空</p><p> userinfo = {}</p><p> # 如果是一个Post请求,并且有微博用户id,则获取微博数据并生成相应云图</p><p> # request.method的值为请求方法</p><p> # request.form既为提交的表单</p><p> if request.method == 'POST' and request.form.get('uid'):</p><p> uid = request.form.get('uid')</p><p> userinfo = get_user_info(uid)</p><p> posts = get_all_post(uid, userinfo['containerid'])</p><p> dest_img = generate_personas(uid, posts)</p><p> userinfo['personas'] = dest_img</p><p> return render_template('index.html', **userinfo)</p><p>if __name__ == '__main__':</p><p> app.run()</p>
以上就是全部的代码,简单吧?当然,单文件结构只适合小的应用,随着功能和代码量的增加,还是需要把代码分离中不同的文件结构中以便开发和维护。最后,还差一个页面的模版文件。
3.模版开发
模版需要有一个输入的表单和用户信息展示,基于Jinja2模版引擎。熟悉Django模版的应该可以很快上手,流程也和Django类型,在项目根目录下建一个名为templates的文件夹并新建一个名为index.html的文件,代码如下:
<!DOCTYPE html><p><html></p><p><head></p><p> <meta charset="UTF-8"></p><p> <link rel="stylesheet" type="text/css" href="./static/css/style.css"></p><p> <title>Flask之微博单用户画像生成器</title></p><p></head></p><p><body></p><p><!--提交微博id的表单--></p><p><div></p><p> <form action="/" method="post" ></p><p> <input type="text" name="uid" placeholder="微博用户ID"></p><p> <input type="submit" value="生成画像"></p><p> </form></p><p></div></p><p><!--下面是用户的展示信息--></p><p><!--使用了模版的if语法,如果有才展示这块内容--></p><p></body></p><p></html></p>
这样,应用实现完毕,项目结构如下:
$ tree .<p>weibo_personas</p><p>├── app.py</p><p>├── static</p><p>│ ├── css</p><p>│ │ └── style.css</p><p>│ ├── fonts</p><p>│ │ └── simhei.ttf</p><p>│ └── images</p><p>│ └── personas.png</p><p>└── templates</p>
└── index.html
进入项目文件夹,启动项目:
$ python app.py
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
然后浏览器打开http://127.0.0.1:5000 地址就可以看到本教程最上面的效果。
以上只是一个初步实现,还有很多需要完善的地方。比如,如果发布的博文比较多时,获取时间比较长,可以考虑加个缓存,存储已获取的用户,避免重复请求,前端也可以加个loading效果。本教程展示的只是单用户,后面也可以批量获取用户信息,生成一个群体的用户画像