1. 程式人生 > >用Python爬了菊姐2W條微博評論,竟發現“菊粉”都是這樣的人!(附程式碼)

用Python爬了菊姐2W條微博評論,竟發現“菊粉”都是這樣的人!(附程式碼)

關於菊姐為什麼會火,網上有很多文章,我就不再贅述了。今天我們就來做一份菊粉陶淵明的使用者畫像,看看那些 Pick 菊姐的人都有什麼特質?

先來看看百度指數,通過百度指數我們看出,菊姐的搜尋熱度在 5 月 30 開始出現頂峰,5 月 31 開始回落。

王菊百度指數

再來看看微信指數,與百度指數趨勢基本一致,但起伏感要稍強於百度指數。

王菊微信指數

我們再來看看菊姐的需求圖譜,即與菊姐相關的話題都有哪些。

王菊需求圖譜

相關詞主要有:創造101,王菊,王菊舊照,王菊年齡,看來大家很關心菊姐的個人資訊哈,尤其是舊照。

王菊新舊照

看看菊姐以前的照片,再看看現在的,我只想問一句,菊姐這些年都經歷了什麼?

瞭解了這些以後,我們切換到今天的主題,菊粉到底都是哪些人?於是我用 Python 爬了菊姐的兩萬條微博評論,並做了分析!

資料抓取

爬取資料來源於王菊最新微博評論資料,本次資料採集的思路主要是:

先獲取王菊最新幾條(關於創造101話題)的微博評論 url

然後依次去遍歷每條 url 下面的留言以及 user_id

在拿到 user_id 以後再去依次遍歷每個使用者的基本資訊

具體實現程式碼如下:

#匯入所需庫
import json
import time
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib
import squarify
from matplotlib.patches import Polygon
from mpl_toolkits.basemap import Basemap
from matplotlib.collections import PatchCollection

#獲取每條微博評論的url引數
def get_comment_parameter():
    url = 'https://m.weibo.cn/api/container/getIndex?uid=1773294041&luicode=10000011&lfid=100103type%3D1%26q%3D%E7%8E%8B%E8%8F%8A&\
           featurecode=20000320&type=uid&value=1773294041&containerid=1076031773294041'

    c_r = requests.get(url)

    for i in range(2,11):
        c_parameter = (json.loads(c_r.text)["data"]["cards"][i]["mblog"]["id"])
        comment_parameter.append(c_parameter)
    return comment_parameter


if __name__ == "__main__":

    comment_parameter = []#用來存放微博url引數
    comment_url = []#用來存放微博url
    user_id = []#用來存放user_id
    comment = []#用來存放評論
    containerid = []#用來存放containerid
    feature = []#用來存放使用者資訊
    id_lose = []#用來存放訪問不成功的user_id

    get_comment_parameter()

    #獲取每條微博評論url
    c_url_base = 'https://m.weibo.cn/api/comments/show?id='
    for parameter in comment_parameter:
        for page in range(1,101):#提前知道每條微博只可抓取前100頁評論
            c_url = c_url_base + str(parameter) + "&page=" + str(page)
            comment_url.append(c_url)

    #獲取每個url下的user_id以及評論
    for url in comment_url:
        u_c_r = requests.get(url)
        try:
            for m in range(0,9):#提前知道每個url會包含9條使用者資訊
                one_id = json.loads(u_c_r.text)["data"]["data"][m]["user"]["id"]
                user_id.append(one_id)
                one_comment = json.loads(u_c_r.text)["data"]["data"][m]["text"]
                comment.append(one_comment)
        except:
            pass


    #獲取每個user對應的containerid
    user_base_url = "https://m.weibo.cn/api/container/getIndex?type=uid&value="
    for id in set(user_id):#需要對user_id去重
        containerid_url = user_base_url + str(id)
        try:
            con_r = requests.get(containerid_url)
            one_containerid = json.loads(con_r.text)["data"]['tabsInfo']['tabs'][0]["containerid"]
            containerid.append(one_containerid)
        except:
            containerid.append(0)

    #獲取每個user_id對應的基本資訊
    #這裡需要設定cookie和headers模擬請求
    user_agent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36"
    headers = {"User-Agent":user_agent}
    m = 1
    for num in zip(user_id,containerid):
        url = "https://m.weibo.cn/api/container/getIndex?uid="+str(num[0])+"&luicode=10000011&lfid=100103type%3D1%26q%3D&featurecode=20000320&type=uid&value="+str(num[0])+"&containerid="+str(num[1])
        try:
            r = requests.get(url,headers = headers,cookies = cookie)
            feature.append(json.loads(r.text)["data"]["cards"][1]["card_group"][1]["item_content"].split("  "))
            print("成功第{}條".format(m))
            m = m + 1
            time.sleep(1)
        except:
            id_lose.append(num[0])

    #將featrue建立成DataFrame結構便於後續分析
    user_info = pd.DataFrame(feature,columns = ["性別","年齡","星座","國家城市"])

資料預處理

根據使用者基本資訊的顯示順序,性別、年齡、星座、國家城市,主要用了以下幾方面的資料處理邏輯:

  • 對於國家列為空,星座列不空且不包含座字,則認為是國家城市名,則把星座列賦值給國家城市列。

  • 對於國家列為空,星座列也為空,年齡列不為空且不包含歲或座字,則把年齡列賦值給國家城市列。

  • 對於星座列為空,但是年齡列包含座字,則把年齡列賦值給星座列。

  • 對於星座列不包含座的,全部賦值為“未知”。

  • 對於年齡列不包含歲的,全部賦值為“999歲”(為便於後續好篩選)。

  • 對於國家列為空的,全部賦值為“其他”。

具體處理程式碼如下:

#資料清洗
user_info1 = user_info[(user_info["性別"] == "男") | (user_info["性別"] == "女")]#去除掉性別不為男女的部分
user_info1 = user_info1.reindex(range(0,5212))#重置索引


user_index1 = user_info1[(user_info1["國家城市"].isnull() == True)&(user_info1["星座"].isnull() == False)
                         &(user_info1["星座"].map(lambda s:str(s).find("座")) == -1)].index
for index in user_index1:
    user_info1.iloc[index,3] = user_info1.iloc[index,2]

user_index2 = user_info1[((user_info1["國家城市"].isnull() == True)&(user_info1["星座"].isnull() == True)
                          &(user_info1["年齡"].isnull() == False)&(user_info1["年齡"].map(lambda s:str(s).find("歲")) == -1))].index
for index in user_index2:
    user_info1.iloc[index,3] = user_info1.iloc[index,1]

user_index3 = user_info1[((user_info1["星座"].map(lambda s:str(s).find("座")) == -1)&
                          (user_info1["年齡"].map(lambda s:str(s).find("座")) != -1))].index
for index in user_index3:
    user_info1.iloc[index,2] = user_info1.iloc[index,1]

user_index4 = user_info1[(user_info1["星座"].map(lambda s:str(s).find("座")) == -1)].index
for index in user_index4:
    user_info1.iloc[index,2] = "未知"

user_index5 = user_info1[(user_info1["年齡"].map(lambda s:str(s).find("歲")) == -1)].index
for index in user_index5:
    user_info1.iloc[index,1] = "999歲"

user_index6 = user_info1[(user_info1["國家城市"].isnull() == True)].index
for index in user_index6:
    user_info1.iloc[index,3] = "其他"

評論多為褒義詞

我們抓取了菊姐的最新微博評論,將評論分詞以後製作成如下詞雲圖,程式碼如下:

import fool
from collections import Counter
from PIL import Image,ImageSequence  
from wordcloud import WordCloud,ImageColorGenerator

#因留言結構比較亂,所以先儲存到本地做進一步處理
pd.DataFrame(comment).to_csv(r"C:\Users\zhangjunhong\Desktop\comment.csv")

#處理完以後再次載入進來
comment_data = pd.read_excel(r"C:\Users\zhangjunhong\Desktop\comment.xlsx")

#將資料轉換成字串
text = (",").join(comment_data[0])

#進行分詞
cut_text = ' '.join(fool.cut(text))

#將分詞結果進行計數
c = Counter(cut_text)
c.most_common(500)#挑選出詞頻最高的500詞

#將結果匯出到本地進行再一次清洗,刪除一些符號詞
pd.DataFrame(c.most_common(500)).to_excel(r"C:\Users\zhangjunhong\Desktop\fenci.xlsx")


image = Image.open('C:/Users/zhangjunhong/Desktop/圖片1.png')#作為背景形狀的圖  
graph = np.array(image)  
#引數分別是指定字型、背景顏色、最大的詞的大小、使用給定圖作為背景形狀  
wc = WordCloud(font_path = "C:\\Windows\\Fonts\\simkai.ttf", background_color = 'White', max_words = 150, mask = graph)  

fp = pd.read_csv(r"C:\Users\zhangjunhong\Desktop\da200.csv",encoding = "gbk")#讀取詞頻檔案  
name = list(fp.name)#詞  
value = fp.time#詞的頻率   
dic = dict(zip(name, value))#詞頻以字典形式儲存  
wc.generate_from_frequencies(dic)#根據給定詞頻生成詞雲 
image_color = ImageColorGenerator(graph)  
plt.imshow(wc)  
plt.axis("off")#不顯示座標軸  
plt.show() 
wc.to_file('C:/Users/zhangjunhong/Desktop/wordcloud.jpg')

分詞沒有用 jieba 分詞,而是用了 fool,據稱是最準確的中文分詞包。

GitHub 地址:

https://github.com/rockyzhengwu/FoolNLTK

排名前三的熱詞分別是:“加油”、“哈哈”、“菊姐”,可以看出大家對菊姐的態度大多還是支援的態度。

男女比例 1:2

繪製男女比例的餅圖
ax = user_info1["性別"].value_counts(normalize = True).plot.pie(title = "菊粉男女分佈",autopct='%.2f')

菊粉性別分佈

既然是女團節目,按理來說應該男生人數要多於女生的(為什麼這麼說,大家應該都懂哈),但再想想,菊姐並不是因為長相甜美而走紅,那男生比例少一點也就可以理解了。

關於女性,菊姐經典語錄:精神獨立和經濟獨立,對於女生而言是很重要的。

有可能是是菊姐的獨立女性做自己的形象觸動了各位小姐姐的內心。

20-25 歲成中堅力量

#將把年齡從字串變成數字
user_info1["age_1"] = [int(age[:-1]) for age in user_info1["年齡"]]

#對年齡進行分組
bins = (0,10,20,25,30,100,1000)#將年齡進行區間切分
cut_bins = pd.cut(user_info1["age_1"],bins = bins,labels = False)
ax = cut_bins[cut_bins < 5].value_counts(normalize =True).plot.bar(title = "菊粉年齡分佈")#將大於100歲的過濾掉
ax.set_xticklabels(["20-25歲","10-20歲","25-30歲","0-10歲","30+"],rotation = 0)

菊粉年齡分佈

如上圖,還有 0-10 歲的粉絲,比較神奇,不過我更願意相信這部分是因為使用者隨意填寫導致的。還好只有約 10% 的比例,影響不大。

20-25 歲這一部分開始對新生事物有自己的判斷,比較喜歡有個性的東西,對美的定義也是各不相同,可能菊姐的特性在他們眼裡就是最美的吧。

還有 30 多歲的大哥哥大姐姐們,看來菊姐的某些特質引發了大哥哥大姐姐們的共鳴。

海外粉絲數位居榜首

#將國家和城市進行分列
country_data = pd.DataFrame([country.split(" ") for country in user_info1["國家城市"]],columns = ["省份","城市"])

#將國家和城市與user表合併
user_data = pd.merge(user_info1,country_data,left_index = True,right_index = True,how = "left")

#按省份進行分組計數
shengfen_data = user_data.groupby("省份")["性別"].count().reset_index().rename(columns = {"性別":"人次"})

#匯入解析好的省份經緯度資訊
location = pd.read_table(r"C:\Users\zhangjunhong\Desktop\latlon_106318.txt",sep = ",")

#將省份資料和經緯度進行匹配
location_data = pd.merge(shengfen_data,location[["關鍵詞","地址","谷歌地圖緯度","谷歌地圖經度"]],
                    left_on = "省份",right_on = "關鍵詞",how = "left")

#進行地圖視覺化
fig = plt.figure(figsize=(16,12))
ax  = fig.add_subplot(111)

basemap = Basemap(llcrnrlon= 75,llcrnrlat=0,urcrnrlon=150,urcrnrlat=55,projection='poly',lon_0 = 116.65,lat_0 = 40.02,ax = ax)
basemap.readshapefile(shapefile = "C:/Users/zhangjunhong/Desktop/CHN_adm/CHN_adm1",name = "china")

def create_great_points(data):
    lon   = np.array(data["谷歌地圖經度"])
    lat   = np.array(data["谷歌地圖緯度"])
    pop   = np.array(data["人次"],dtype=float)
    name = np.array(data["地址"])
    x,y = basemap(lon,lat)
    for lon,lat,pop,name in zip(x,y,pop,name):
        basemap.scatter(lon,lat,c = "#778899",marker = "o",s = pop*10)
        plt.text(lon,lat,name,fontsize=10,color = "#DC143C")
create_great_points(location_data)

plt.axis("off")  #關閉座標軸
plt.savefig("C:/Users/zhangjunhong/Desktop/itwechat.png") #儲存圖表到本地
plt.show()    #顯示圖表

菊粉全國分佈

#Top10省份圖表繪製
ax = shengfen_data[shengfen_data["省份"] != "其他"].sort_values(by = "人次",ascending = False).head(10).plot.barh(legend = False,color = "#BF0003",title = "菊粉Top10省份")
ax.set_yticklabels(["海外","廣東","北京","浙江","上海","四川","江蘇","山東","湖北","福建"])

菊粉分佈 Top10 省份

這裡的海外是指大陸+港澳臺以外的其他所有地方。除海外使用者以外就北上廣的使用者最多了,這些地方的網際網路使用者基數本來就大。

#Top10城市圖表繪製
chengshi_data = user_data.groupby("城市")["性別"].count().reset_index().rename(columns = {"性別":"人次"})
ax = chengshi_data[chengshi_data["城市"] != "其他"].sort_values(by = "人次",ascending = False).head(10).plot.barh(legend = False,color = "#BF0003",title = "菊粉Top10城市")
ax.set_yticklabels(["廣州","成都","杭州","武漢","長沙","朝陽區","西安","深圳","美國","福州"])

菊粉分佈 Top10 城市

因為北京上海比較特殊,北京上海的一些區相當於北京上海這兩個省下面的市區,所以你會看到一些北京上海的區域也進入了榜單,比如說朝陽群眾。

摩羯座粉絲人最多

#菊粉星座樹地圖繪製
# 建立資料
xingzuo = user_info1["星座"].value_counts(normalize = True).index
size = user_info1["星座"].value_counts(normalize = True).values
rate = np.array(["34%","6.93%","5.85%","5.70%","5.62%","5.31%","5.30%","5.24%","5.01%","4.78%","4.68%","4.36%"])

# 繪圖
colors = ['steelblue','#9999ff','red','indianred',
          'green','yellow','orange']
plot = squarify.plot(sizes = size, # 指定繪圖資料
                     label = xingzuo, # 指定標籤
                     color = colors, # 指定自定義顏色
                     alpha = 0.6, # 指定透明度
                     value = rate, # 新增數值標籤
                     edgecolor = 'white', # 設定邊界框為白色
                     linewidth =3 # 設定邊框寬度為3
                    )
# 設定標籤大小
plt.rc('font', size=10)
# 設定標題大小
plt.title('菊粉星座分佈',fontdict = {'fontsize':12})

# 去除座標軸
plt.axis('off')
# 去除上邊框和右邊框刻度
plt.tick_params(top = 'off', right = 'off')

菊粉星座分佈

除了未知星座以外,菊姐的粉絲中摩羯座的粉絲最多。我們看看摩羯座都有哪些特性。

摩羯座男性:摩羯座男生特別細心有責任感,對待誰,都會問心無愧,仁至義盡。這是摩羯座整個人生當中的核心信條,只有在做到了自己該做到的事情之後摩羯座才能夠坦然的去批評別人,去申述理論,因此摩羯座一直都站在正義的一方和大義的立場上。

摩羯座女性:一板一眼的,戴著厚厚的平底眼鏡,梳著中規中矩的頭髮,就連自己的衣著,也儘量以不突出不惹人矚目為目標,摩羯座的女生就是這樣,貌似不起眼的外表,耐力驚人(怎麼感覺是在說菊姐)。

摩羯座可以連續十年作同一件普通簡單的事情,只是單一重複仍然感覺樂趣無窮,這樣的事情,在別的星座女生看來,簡直不敢想象,不過摩羯座卻能完成,而且津津有味。

菊姐語錄:沒有奇蹟,只有累積,更是完美匹配了摩羯女十年做同一件事情的堅強毅力。

"陶淵明"究竟是何方神聖

最後將菊粉上面的幾個特徵綜合了做了一個詞雲圖,得到了菊粉的一個大概畫像,程式碼如下:

image = Image.open('C:/Users/zhangjunhong/Desktop/圖片1.png')#作為背景形狀的圖  
graph = np.array(image)  
#引數分別是指定字型、背景顏色、最大的詞的大小、使用給定圖作為背景形狀  
wc = WordCloud(font_path = "C:\\Windows\\Fonts\\simkai.ttf", background_color = 'White', max_words = 150, mask = graph)  

name = ["女性","摩羯座","20歲","21歲","22歲","23歲","24歲","25歲","廣州","杭州","成都","武漢","長沙","上海","北京","海外","美國","深圳"]
value = [20,20,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10]#詞的頻率 
dic = dict(zip(name, value))#詞頻以字典形式儲存  
wc.generate_from_frequencies(dic)#根據給定詞頻生成詞雲 
image_color = ImageColorGenerator(graph)  
plt.imshow(wc)  
plt.axis("off")#不顯示座標軸  
plt.show() 
wc.to_file('C:/Users/zhangjunhong/Desktop/wordcloud.jpg')

菊粉畫像

通過該詞雲圖可以看出,“陶淵明”主要有以下特質:

女性

20-25 歲

海外、北京、上海、廣州

摩羯座

關於菊粉陶淵明的使用者畫像就到這了,關於菊姐你有什麼想說的,我們留言區見。

*文章為作者獨立觀點,不代表職問立場