0.前言
- 预警:①下文提到“宏迪”,即李云迪和王力宏。② 大部分内容第一次做,很多缺陷。
- 目的:快乐学习,所有数据和结果仅供参考。偶然知道了原来2012春晚之后拉开了一场大戏,当时我正忙于学业,没能吃上新鲜的瓜,现在来补习这个古早(现双塌方)的神奇CP,正好能快乐学习一天。
- 关键词:爬虫 情感分析 地图 正则表达式 中文分词 词云
- 语言:Python
- 参考:BiliBili up主 龙王山小青椒 “Python网络爬虫”系列视频
1.获取数据-爬虫
1.1 热门微博
- 爬取了微博话题#宏迪 下前100页的热门微博,但是由于微博的数据层级结构比以前复杂了,一时没能找到方便地获取长微博全文的方法,且没未确定是否获取了前100页的全部微博,最后得到的条目是600+。代码如下:
import requests
import pandas as pd
import json
import time
header = {'Content-Type':'application/json; charset=utf-8',
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'
} #请根据自己的浏览器修改
cookie = {'Cookie':'……登录自己的账号使用对应的cookie……'}
for i in range(100):
time.sleep(3) # 挺个一两秒就行,防止被禁访问
# 下面是搜索结果页
url_base = 'https://m.weibo.cn/api/container/getIndex?containerid=231522type%3D1%26t%3D10%26q%3D%23%E5%AE%8F%E8%BF%AA%23&isnewpage=1&luicode=10000011&lfid=100103type%3D38%26q%3D%E5%AE%8F%E8%BF%AA%26t%3D0&page_type=searchall'
url = url_base if i ==0 else url_base+'&page='+str(i+1)
print(url)
html = requests.get(url,headers=header,cookies=cookie)
for j in range(len(html.json()['data']['cards'])):
# 多了很多’mblog‘,而且不同页的层级也不同
# 所以可能并没能爬到前100页的所有微博
if 'mblog' in html.json()['data']['cards'][j].keys():
sig = html.json()['data']['cards'][j]['mblog']['isLongText']
print(sig)
text = html.json()['data']['cards'][j]['mblog']['text'].replace('
','')
if text.startswith('')[1]
data1 = [(html.json()['data']['cards'][j]['mblog']['user']['id'],
html.json()['data']['cards'][j]['mblog']['user']['screen_name'],
text,
html.json()['data']['cards'][j]['mblog']['reposts_count'],
html.json()['data']['cards'][j]['mblog']['comments_count'],
html.json()['data']['cards'][j]['mblog']['attitudes_count'],
html.json()['data']['cards'][j]['mblog']['created_at'],
html.json()['data']['cards'][j]['mblog']['source'],
)]
data2 = pd.DataFrame(data1)
# 注意模式是a+即不断添加
data2.to_csv('路径/weibo_content.csv',header=False,index=False,mode='a+')
else:
# 发现cards下还会有mblog
# 可能嵌套了很多层,可以修改循环把所有mblog找出来
# 这里鉴于爬取太多也挺花时间的,就没改了
for p in range(len(html.json()['data']['cards'][j]['card_group'])):
if 'mblog' in html.json()['data']['cards'][j]['card_group'][p].keys():
text = html.json()['data']['cards'][j]['card_group'][p]['mblog']['text'].replace('
','')
if text.startswith('')[1] # 后来想想这里不太合理,可能丢失了部分数据
data1 = [(html.json()['data']['cards'][j]['card_group'][p]['mblog']['user']['id'],
html.json()['data']['cards'][j]['card_group'][p]['mblog']['user']['screen_name'],
text,
html.json()['data']['cards'][j]['card_group'][p]['mblog']['reposts_count'],
html.json()['data']['cards'][j]['card_group'][p]['mblog']['comments_count'],
html.json()['data']['cards'][j]['card_group'][p]['mblog']['attitudes_count'],
html.json()['data']['cards'][j]['card_group'][p]['mblog']['created_at'],
html.json()['data']['cards'][j]['card_group'][p]['mblog']['source'],
)]
data2 = pd.DataFrame(data1)
data2.to_csv('路径/weibo_content.csv',header=False,index=False,mode='a+')
1.2 发帖人
- 爬取1.1里获得的热门微博发帖人在微博的公开信息
- 除了地理位置,还可以分析性别、年龄、学历等公开信息的情况
df_id= pd.read_csv('/路径/weibo_content.csv',
header=None,usecols=[0])
id = df_id.values.tolist()
df_id_sex_location_score = pd.DataFrame()
for i in range(len(id)):
url = f'http://weibo.cn/{id[i][0]}/info'
print(url)
try:
html = requests.get(url,headers=header,cookies=cookie)
sex = re.findall(r'
性别:(.*?)
',html.text)
location = re.findall(r'
地区:(.*?)
',html.text)
tmp = pd.DataFrame([(id[i][0],sex,location)])
df_id_sex_location_score = pd.concat([tmp,df_id_sex_location_score],axis=0)
except:
print('wrong')
time.sleep(1)
df_id_sex_location_score.columns=['id','sex','location']
df_id_sex_location_score.to_csv('路径/location.csv')
2.情感分析
2.1 计算情绪得分
- 用Python的 snownlp包,进行简易情绪分析
- 其返回值为正面情绪的概率,越接近1表示正面情绪,越接近0表示负面情绪。
from snownlp import SnowNLP
from snownlp import sentiment
score = []
for content in contents:
try:
s = SnowNLP(content)
score.append(s.sentiments)
except:
# 如果不能正确算分,比如有表情在内,则记为0.5
score.append(0.5)
data_score = pd.DataFrame(score,columns=['score'])
data_score.to_csv('路径/score.csv',header=False,index=False)
2.2 情绪分布地图
- 这一步使用1.2得到的发帖人地区信息和2.1得到的微博内容情绪得分
from pyecharts.charts import Map
from pyecharts import options as opts
import matplotlib.pyplot as plt
df_id_sex_location_score = pd.read_csv('路径/location.csv')
df = df_id_sex_location_score.copy()
# 得到数据信息精确到市一级,这里只保留了省份
df['location'] = [i.strip("'[]'" ).split(' ')[0] for i in df['location'] ]
# 将省份信息和情绪得分两列拼起来
df_formap = pd.concat([df,data_score],axis=1)
# 按省份分组
grouped= df_formap.groupby('location').describe()
# 去掉“其他”和“海外”的,本次只画了国内的
grouped.drop(['其他','海外'],axis=0,inplace=True)
#绘制地图
map = (
Map()
.add('',[list(i) for i in zip(grouped['score']['mean'].index,
grouped['score']['mean'].values)],
maptype='china',is_map_symbol_show=False
)
.set_global_opts(title_opts=opts.TitleOpts(title='吃瓜人分布'),
visualmap_opts = opts.VisualMapOpts(min_=0,max_=1)
)
)
map.render(path='路径/地图.html')
- 地图如下,其实应该把颜色改成diverging的,这样正面或负面更容易辨别。
- 从这个图里来看,发热门微博的可能情感都比较正面,可能这就是“幸存者偏差”吧,对“宏迪”恶感的人可能更少点进这个话题,而在这个话题下博得“热门微博”的可能性就更小了。
- 再有,北部、西南、南部 包括台湾的发帖人可能情绪比较激动?
想得到确切结论的话,还得更大的样本+长微博全文+多指标综合分析(如各地发帖数量、发帖人数量、发帖频率等),还可以找一些能分析表情包的方法。
3.词云
3.1微博内容预处理
2.1 微博内容数据预处理
这里用1.1得到的微博内容,删掉了一些符号,以及因为没找长微博原文所以会反复出现的“全文”二字。另外,对于cp党来说,一些关键的日期也很重要,所以没删除数字,但是后面分词写词典的时候没想起来加进去,遗憾,有时间的话再修改一下。
import re
contents = []
df = pd.read_csv('路径/weibo_content.csv',
header=None,usecols=[2]) # 只取出了微博内容,后面根据索引对就行
df.values.tolist()
for content in df.values.tolist():
content = content[0]
# p = re.compile(r'(.*)*(.* a>)?')
# data = p.sub(r'',content)
data = re.sub(r'()|( )|()|()|(全文)','',content).replace('','')
contents.append(data.replace('
',''))
3.2中文分词
这里用jieba,并根据我“打入内部”了解到的信息,写了一些“宏迪批”常提到的关键词,作为额外的词典,防止被分词。
from jieba import lcut_for_search
import jieba
import numpy as np
import matplotlib.pyplot as plt
from wordcloud import WordCloud
words = []
jieba.load_userdict('HDdic.txt')
for content in contents:
words.extend(lcut(content))
words = ' '.join(words)
3.3词云
# 专门找了个钢琴的图,把背景扣掉,只留下了钢琴,作为词云背景
# 读取背景图为数组
mask_pic = plt.imread('piona.png')
# 只留下钢琴及其内部的数据
mask_pic[mask_pic==0] = int(255)
# 设计词云
wdc = WordCloud(font_path='/System/Library/Fonts/PingFang.ttc',
width=800,height=600,
mask = mask_pic, # 背景图片
background_color='black', # 背景颜色
max_font_size=100, # 最大字号
max_words=150, # 显示最大字数
# 设置一些不显示的词
stopwords={'我','我们','你们','《','》',"全体",'人','上',
'了','注意','。',',','的','是','都',
'和','也','他','你','在','不','但是',
'被','吧','还是','吗','这','就','一个',
'说','好','有','对','到','这个','能',
'什么','就是','自己','呢','不是','又',
'会','会不会','着','后','得','从','还',
'把','没有','在','很','啊','多','让','那',
'再','去','看','过','给','要','觉得',
'两个','什么','谁','怎么','没','时候',
'因为','那么','这么','里','上','年',
'王','中','不会','他们','现在','事',
'一','为','但','那些','啥','大','个',
'来','太','下','起','月','日','开始',
'跟','而','最','她','更','做','那个','真是'
,'一些','写','站','只','完','还有','B',
'小','男','然后','很多','们','所以','比',
'号','用','发','这种','这样','出','这样',
'只有','带','一下','看','为了','找','头',
'才','啦','却','像','请','里面','的话','岁',
'它','这是','当'
}, # 词云图中不显示的词
collocations=False,#关键词不重复
contour_width=3,
colormap='Set3'
)
wdc.generate(words)
img_wdc = wdc.to_image()
wdc.to_file('路径/wordcloud.png')
可以平时积累一些常见的万金油stopwords存起来,就是那种经常用来组织语句但与主题不想关的字词。词云如下,我给这个图起名为“嗑宏迪真的缺德”(笑)。从这个图里可以看出:
- 王力宏,李云迪,他俩——宏迪
- cp粉认为他们是真的,嗑/磕得很上头,以致于很多人打了错别字
- 提到he的 比 提到 be的多
- 有个钢琴家
- 会要听什么的东西,比如歌吧,还提到音乐
- 有人下头了,有人觉得很缺德,还有人觉得恶心,有人提到了前妻,看来还是有一些对“宏迪”感到恶感的人进这个话题发微博的
- 比较重要的:爱,朋友,落叶归根,工体,玫瑰,感情,蝴蝶……
- 还有人 “哈哈哈”,“笑死”
这样一看,还是有一定信息量的,一些重要的关键词也有捕捉到。
- 最后,以上都是开玩笑的,只是为了学技术而已,对瓜一点都不感兴趣(笑)。