爬取微博|情感分析|中文词云

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的,这样正面或负面更容易辨别。
    • 从这个图里来看,发热门微博的可能情感都比较正面,可能这就是“幸存者偏差”吧,对“宏迪”恶感的人可能更少点进这个话题,而在这个话题下博得“热门微博”的可能性就更小了。
    • 再有,北部、西南、南部 包括台湾的发帖人可能情绪比较激动?

想得到确切结论的话,还得更大的样本+长微博全文+多指标综合分析(如各地发帖数量、发帖人数量、发帖频率等),还可以找一些能分析表情包的方法。


吃瓜人情感地图.png

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'(.*)*(.*)?')
    # 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的多
  • 有个钢琴家
  • 会要听什么的东西,比如歌吧,还提到音乐
  • 有人下头了,有人觉得很缺德,还有人觉得恶心,有人提到了前妻,看来还是有一些对“宏迪”感到恶感的人进这个话题发微博的
  • 比较重要的:爱,朋友,落叶归根,工体,玫瑰,感情,蝴蝶……
  • 还有人 “哈哈哈”,“笑死”

这样一看,还是有一定信息量的,一些重要的关键词也有捕捉到。


磕宏迪真的缺德.png
  • 最后,以上都是开玩笑的,只是为了学技术而已,对瓜一点都不感兴趣(笑)。

你可能感兴趣的:(爬取微博|情感分析|中文词云)