高能預警!分析到最後,我不得不感慨這個世界太真實了!

文中有大量代碼,注重閱讀體驗的請在PC站打開!或者直接去我的個人博客(data-insights.cn)閱讀!

一、微博粉絲榜:一潭深水

微博粉絲榜爭奪戰由來已久,每個明星在榜單上的位置似乎就象徵著他(她)在粉絲心中、在娛樂圈中的地位。

但眾所周知,微博粉絲榜是有著極大水分的。微博刷榜、刷關注等早已形成一套產業鏈。發展源自需求,有人提供服務就說明有人需要這種服務,明星們刷粉絲刷榜是可以將其轉化為利益的,他們有充分的理由和動力去做這件事情。

但是我們相信,不可能所有明星都會通過這種手段來偽裝自己的強大。那麼,現在微博粉絲榜前列的大明星們,到底誰的粉絲最真實,誰的粉絲水分最大?

我們不知道幕後真相,也沒有人提供爆料,有的只是微博上公開的信息,那我們如何才能撥開迷霧,從這些公開的信息中抽絲剝繭,找到我們想要的答案呢?作為一個數據工作者,自然是要從數據入手了!

預警:前方會有不少技術相關的內容,對技術不感興趣的同學可以跳著閱讀,不會影響閱讀體驗!

二、準備工作

首先我們先觀察一下我們可以獲取到的信息。我們以人民日報為例,先在微博搜索「人民日報」。在結果頁中,我們看到了粉絲數,也能看到熱門微博的轉發、評論、點贊數。

然後我們點進去人民日報官博的首頁,可以看到它的更多微博,每條微博的轉發、評論和點贊的數據都是可用的。

接下來我們點進去粉絲頁(從搜索頁中點進去),現在微博僅開放最新的100個粉絲給我們看。在粉絲頁,我們可以看到每個粉絲的一部分屬性,比如關注數、粉絲數和微博數。

我們梳理下現在確認可以找到的信息:

  1. 微博表現:包括評論、轉發、點贊
  2. 粉絲總數
  3. 每個粉絲的站內表現:關注數、粉絲數、微博數、性別、地址、關注來源

那麼我們如何從這些數據裏挖掘出誰的粉絲水分最大,誰的粉絲最真實呢?

我們定義幾個指標:

  1. 微博人均評論/轉發/點贊貢獻量:用最近數條微博的平均評論數/轉發數/點贊數除以粉絲量,得出一個平均每個粉絲對於微博熱度的貢獻量,這一指標可以衡量粉絲對於明星的支持度、忠誠度,如果是為了刷量買的粉絲,那這些粉絲大概率是不會有太多活動的。當然,可以刷粉絲量就可以刷轉發、刷點贊、刷評論,但是這些刷出來的內容我們暫時不太容易去剔除。
  2. 合格粉比例:如何定義合格粉絲呢?我觀察到有很多用戶僅有一個粉絲(微博小助手,新用戶默認),微博量為0,用戶名的格式為「用戶xxxxxxx」,也就是說,這些完全是新用戶,且無法確定是否是殭屍用戶。那我們本著寧可錯殺絕不放過的原則,將其歸類為不合格粉絲,剩下的就是合格粉絲。由於微博僅展示最近的100個粉絲,這個量級完全不夠我們分析使用。所以我們採用滾動抓取去重的方式,來獲得更多的粉絲數據。假設我們抓取了1000個粉絲樣本,其中800個合格,那合格粉絲比例就是80%。
  3. 主動粉比例:我們觀察關注來源,發現有以下幾種:微博搜索、微博找人、手機型號、微博推薦。其中,微博推薦量級最為龐大,微博站內有諸多推薦位,這些位置應該是明碼標價可以購買的,同時新用戶註冊完之後會有一些名人推薦,這一部分可能有冷啟動的作用,同時也為明星增加了曝光。這些應該都屬於微博推薦;微博搜索和微博找人很好理解,應該是用戶主動找到這位明星並關注的,一般是真粉絲或者由熱門事件帶來;來源為手機型號的暫且不明,不清楚是不是微博與廠商的合作帶來的App外流量。那麼我們就將來自微博搜索和微博找人的粉絲定義為主動粉,主動粉比合格粉更能代表這一明星粉絲中忠誠粉的比例情況。

三、數據收集

數據收集是數據分析的關鍵步驟,畢竟巧婦難為無米之炊。那麼我們如何獲取這些數據呢?一條微博一條微博地點擊然後手動錄入?這不是我們技術派的風格。

我們準備使用Python3Selenium,通過頁面抓取並解析來完成這一任務。這一部分的內容對非技術類同學來說簡直是又臭又長,不感興趣的可以直接跳過看分析部分。

1. 自動登錄微博

現在想要繞過微博的反爬蟲策略並成功抓取內容,必須要模擬登錄。因此我們先看一下如何模擬瀏覽器行為,自動登錄微博。

我們先打開微博首頁,右鍵檢查,觀察頁面元素。在我自己檢驗的過程中,使用無頭(headless)模式抓取時,圖中2標記的部分會有一定的失敗概率;因此我們通過單擊標記1所示的登錄按鈕來登錄。

觀察發現這是在一個<a>節點中,它有一些屬性,比如class="S_txt1"node-type="loginBtn"等。那我們可以通過XPath的方式來定位,定位到之後要模擬瀏覽器的單擊行為。

單擊之後出現一個登錄模塊的浮層,在頁面源碼中我們發現了這麼一個class="Bv6_layer"<div>節點,正是這個登錄模塊對應的源碼。這個模塊裏有兩個<input>節點,分別對應著用戶名和密碼的輸入框,他們的name屬性的取值分別為"username""password"。輸入完用戶名和密碼,我們需要定位到登錄按鈕的元素並執行點擊行為。

下面看下代碼:

# ./../.venv/bin/python
# -*- coding:utf-8 -*-
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
import pandas as pd
import time

class WeiboFansCrawler:
def __init__(self):
options = webdriver.ChromeOptions()
options.add_argument(headless)
self.driver = webdriver.Chrome(options=options)
self.wait = WebDriverWait(self.driver, 10)
self.fan_data = []
self.home_data = []
self.fan_cnt = {}
try:
self.stars = eval(open(star_id.txt, r).read())
except Exception as e:
self.stars = {}
print(e)

def login(self):
"""
登錄微博
:param driver: selenium webdriver
:return: Nothing
"""
self.driver.get(https://www.weibo.com)
# 顯式獲取用戶名輸入框並輸入用戶名
# print(self.driver.page_source)
login_entrance = self.wait.until(lambda s: s.find_element_by_xpath(//li/a[@class="S_txt1" and @target="_top" and @node-type="loginBtn"]))
login_entrance.click()

# 用戶名
username = self.wait.until(lambda s: s.find_element_by_xpath(//div[@class="Bv6_layer "]//input[@name="username"]))
username.send_keys([email protected])

# 獲取密碼框並輸入密碼
password = self.wait.until(lambda s: s.find_element_by_xpath(//div[@class="Bv6_layer "]//input[@name="password"]))
password.send_keys(password)

# 單擊登錄
login_button = self.wait.until(lambda s: s.find_element_by_xpath(//div[@class="Bv6_layer "]//a[@node-type="submitBtn"]))
login_button.click()
time.sleep(5)

__init__(self)函數中,我們定義了初始化的一些信息:比如我們啟動了一個無頭模式的chromedriver,設置了顯式等待的方法,初始化了粉絲數據、主頁微博數據、粉絲總數以及明星頁面ID數據。

顯式等待對應的方法是selenium.webdriver.support.ui.WebDriverWait,它用來應對頁面載入較慢的情況。比如說你的網速不太好,頁面沒有載入完成,那程序很可能會因為定位不到元素而報錯。但是使用time.sleep()的話,在網路條件比較好的情況下又會很浪費時間,因此使用顯式等待就可以幫助我們在設置的超時範圍內一直等待頁面載入我們需要定位的元素,經濟而實用。

明星與對應的頁面ID的關係是綁定的,也就是說我們可以抓取一次並緩存到本地,之後就可以直接從本地讀取對應關係。

接下來看登錄模塊,我們先控制瀏覽器打開微博首頁,然後通過find_element_by_xpath方法,使用XPath表達式來定位到用戶名輸入框、密碼輸入框、登錄按鈕對應的元素,使用send_keys()方法將用戶名和密碼輸入,使用click()方法單擊登錄提交。然後我們使用time.sleep(5)來休息五秒,這個地方不一定有必要,但是為了避免被當作爬蟲封了IP,我們穩妥一點比較好。

需要注意的是,這裡的用戶名和密碼要改成自己的用戶名和密碼才能使用。

2. 獲取明星對應的ID

這個ID其實不是用戶ID,但是是url地址中的一個參數,由用戶ID加一個前綴構成。我們不清楚他的構成規則,但我們可以直接抓取到它。

首先,我們在頂部搜索框輸入明星的姓名或者暱稱,然後單擊搜索按鈕。

然後,單擊粉絲數對應的鏈接,進入粉絲頁。

這時,會打開一個新的標籤頁,所以我們需要控制瀏覽器切換標籤。然後我們單擊第2頁,觀察url格式:

https://weibo.com/p/1003061192329374/follow?relate=fans&page=2#Pl_Official_HisRelation__59

通過觀察,我們發現,follow?relate=fans&page=x是必要的參數,1003061192329374就是我們需要的ID。(後來我發現直接使用用戶ID也就是默認粉絲頁的ID也是可以的,不過這個不影響大局。)

獲取ID之後,我們將其以字典格式保存到self.stars,並緩存到本地。看下這部分的代碼:

def get_page_id(self, search_word):
# 顯式等待獲取搜索框並輸入搜索詞
search_box = WebDriverWait(self.driver, 10).
until(lambda s: s.find_element_by_id(plc_top).find_element_by_tag_name(input))
search_box.send_keys(search_word)

# 點擊搜索
self.driver.find_element_by_id(plc_top).
find_element_by_class_name(gn_search_v2).find_element_by_tag_name(a).click()

# 點擊進入粉絲頁
fans_page = WebDriverWait(self.driver, 10)
.until(lambda s: s.find_element_by_class_name(card-user-b)
.find_elements_by_tag_name(p)[2].find_elements_by_tag_name(a)[1])
fans_page.click()

self.driver.switch_to.window(self.driver.window_handles[1])
fans_page2 = WebDriverWait(self.driver, 10)
.until(lambda s: s.find_element_by_class_name(W_pages).find_element_by_link_text(2))
fans_page2.click()
time.sleep(1)

ID = self.driver.current_url.split(/)[-2]
self.driver.close()
self.driver.switch_to.window(self.driver.window_handles[0])
self.driver.back()
time.sleep(0.5)

self.stars[search_word] = ID
print(self.stars)

3. 獲取粉絲數據

我們剛才已經知道了粉絲頁面url的格式,所以我們可以直接拼接並訪問。

觀察源碼,我們發現粉絲暱稱、用戶ID、性別可以在一個<li>標籤的action-data屬性中找到,關注數、粉絲數、微博數也都能在下邊輕易找到。那我們要做的就是把他們解析並保存下來。

看下代碼:

def get_fans_data(self):
"""
獲取粉絲數據
:param driver:
:param ID:
:return:
"""
for star_name, ID in self.stars.items():
for cnt in range(1, 6):
try:
url = https://weibo.com/p/{0}/follow?relate=fans&page={1}.format(ID, cnt)
self.driver.get(url)

# 暱稱/性別
name_elements = self.wait.until(lambda s: s.find_elements_by_class_name(info_name))
names = [i.text for i in name_elements]
genders = [i.find_element_by_tag_name(i).get_attribute(class) for i in name_elements]
genders = [i.split( )[1].split(_)[1] for i in genders]

name_elements = self.wait.until(lambda s: s.find_elements_by_class_name(follow_item))
ng_data = [i.get_attribute(action-data) for i in name_elements]
fan_ids = [i.split(&)[0].split(=)[1] for i in ng_data]
fan_names = [i.split(&)[1].split(=)[1] for i in ng_data]
fan_genders = [i.split(&)[2].split(=)[1] for i in ng_data]

# 關注/粉絲/微博
stat_elements = self.driver.find_elements_by_class_name(info_connect)
follows = [i.find_elements_by_tag_name(a)[0].text for i in stat_elements]
fans = [i.find_elements_by_tag_name(a)[1].text for i in stat_elements]
posts = [i.find_elements_by_tag_name(a)[2].text for i in stat_elements]

# 地址
addr_elements = self.driver.find_elements_by_class_name(info_add)
addrs = [i.find_element_by_tag_name(span).text for i in addr_elements]

# 訂閱來源
from_elements = self.driver.find_elements_by_class_name(info_from)
froms = [i.find_element_by_tag_name(a).text for i in from_elements]

# 明星
stars = [star_name] * len(names)

info_list = list(zip(stars, fan_names, fan_ids, fan_genders, follows, fans, posts, addrs, froms))
self.fan_data.extend(info_list)
print(info_list)
time.sleep(1)

except Exception as e:
print(e)
continue

這裡我們加了一個try...except...模塊,這是因為頁面中某些欄位會有缺失,會出現定位不到元素的情況,這種頁面我們直接跳過即可。這樣,我們就可以將粉絲數據保存到self.fan_data中了。

我們還需要將數據緩存到本地進行進一步分析(也可以考慮MySQL之類的資料庫),所以我們定義一個函數來實現緩存功能:

def dump_fans_data(self):
df = pd.DataFrame(self.fan_data)
df.columns = [star_name, nick, id, gender, follow_cnt, fan_cnt, post_cnt, addr, from]
df.to_csv(./fans.csv, mode=a, index=False, header=False)

4. 獲取主頁微博表現數據

我們還需要抓取他們主頁展示的那些微博的表現情況。觀察主頁url的格式,我們可以發現可以跟剛才一樣用ID去拼接。

url = https://weibo.com/p/{0}?is_all=1.format(ID)

微博的具體內容我們不考慮,我們只考慮每條微博的表現數據。可以發現,這個數據隱藏得非常深,但還是難不倒我們。我們依次定位到轉發、評論和點贊所在的元素並將文本解析出來。

看下代碼:

def get_home_data(self):
for star_name, ID in self.stars.items():
url = https://weibo.com/p/{0}?is_all=1.format(ID)
self.driver.get(url)

# 轉發數據
forward_elements = self.wait.until(lambda s: s.find_elements_by_xpath(//span[@node-type="forward_btn_text"]/span//em[2]))
forwards = [i.text for i in forward_elements]

# 評論數據
comment_elements = self.wait.until(lambda s: s.find_elements_by_xpath(//span[@node-type="comment_btn_text"]/span//em[2]))
comments = [i.text for i in comment_elements]

# 點贊數據
like_elements = self.wait.until(lambda s: s.find_elements_by_xpath(//span[@node-type="like_status"]//em[2]))
likes = [i.text for i in like_elements]

# 明星
stars = [star_name] * len(forwards)
total = [self.fan_cnt[star_name]] * len(forward_elements)

# index
indexes = list(range(1, len(forwards) + 1))

perform_list = list(zip(stars, total, indexes, forwards, comments, likes))
self.home_data.extend(perform_list)
print(perform_list)
time.sleep(1)

接下來呢,我們同樣要將主頁數據緩存到本地。

def dump_home_data(self):
df = pd.DataFrame(self.home_data)
df.columns = [star_name, total_fans, post_index, forward, comment, like]
df.to_csv(./home.csv, mode=w, index=False, header=True)

5. 執行抓取

我們挑選一些比較熱門的明星來看下他們的粉絲情況,這裡選了排行榜的前22位(2018-10-15數據),再加上陳羽凡這幾天特別火,所以我們把他也加進來。

在抓取粉絲頁時,由於每次抓取最多隻能抓到100個用戶,所以我們每次抓取後暫停100秒,繼續下一輪抓取,做20個循環,這樣就能積累較多的粉絲。(這些人太火了,所以粉絲增長非常快,100秒不夠200秒也足夠了,我為了效率每次只休息100秒)

if __name__ == __main__:
browser = WeiboFansCrawler()
browser.login()

# 明星列表
star_lists = [謝娜, 何炅, Angelababy, 楊冪, 陳坤, 趙薇, 姚晨,
林心如, 鄧超, 郭德綱, 林志穎, 張小嫻, 趙麗穎, 范冰冰,
賈乃亮, 唐嫣, 胡歌, 陳喬恩, 王力宏, 黃曉明, 文章同學,
劉亦菲, 陳羽凡]

# 若本地緩存中沒有,則在線獲取ID
if len(browser.stars) == 0:
for star in star_lists:
browser.get_page_id(star)

# ID緩存到本地
with open(star_id.txt, w) as f:
f.write(str(browser.stars))

# 獲取主頁數據
browser.get_fan_cnt()
browser.get_home_data()
browser.dump_home_data()

# 獲取並緩存數據到本地
loop = 0
while True:
browser.get_fans_data()
browser.dump_fans_data()
loop += 1
if loop > 20:
break
time.sleep(100)

print(browser.stars)
print(browser.data)

大功告成!鼓掌!撒花!

四、數據分析

數據到手,接下來我們就要開始從數據中發掘信息了!

import pandas as pd

df = pd.read_csv(fans.csv)
df = df.drop_duplicates([star_name, id], keep=last)
df[addr] = df[addr].map(lambda x: x.split( )[0])
df.head()

先展示下粉絲數據,看起來一切正常。這裡我們需要對數據執行一下清洗: 1. 去重:這是因為我們是滾動抓取的,用戶可能有重複。但是同一個用戶作為兩個明星的粉絲的情況我們認為是正常的,所以以明星、粉絲用戶ID兩個key一起來去重。 2. 地址:省級的地址就夠用了,事實上這樣還是太過分散,我們不會太關注地址。

df2 = pd.read_csv(./home.csv)
df2[forward] = df2[forward].map(lambda x: 0 if x =="轉發" else x)
df2[comment] = df2[comment].map(lambda x: 0 if x =="評論" else x)
df2[like] = df2[like].map(lambda x: 0 if x == "贊" else x)
df2[[total_fans, post_index, forward, comment, like]] = df2[[total_fans, post_index, forward, comment, like]].applymap(int)
df2.head()

然後看下主頁微博表現數據,這裡沒有展示出來的情況是,當一條微博轉發、評論或者點贊的數字為0時,該數據對應著一個字元串"轉發"、"評論"或者"贊",我們要將它們清洗為0,並全部轉化為int格式。

1. 誰的粉絲增長最快?

接下來我們看下抓取到的每個明星的粉絲樣本量是多少:

df_stats1 = df.groupby(star_name)[id].count()
df_stats1.sort_values(ascending=False).plot(kind=bar, figsize=(18,9), fontsize=16)

這個肯定不能代表總的粉絲量,但是這個數據能在一定程度上代表在我抓取數據的那段時間裡,每個明星的粉絲增長量。為什麼強調「一定程度上」呢,因為有的明星的粉絲的增長速度遠遠超過了我的抓取速度,所以這裡出現了天花板效應。1900以上的明星在這段時間裡實際增長的粉絲量都可能早已超過了2000,但我們前邊提到過,我們一共20次循環,每次循環最多抓到100個,最後這兩千個還要放一起去重。

那麼這個榜單說明瞭什麼呢?首先,雖然大家都是大腕兒,但是在粉絲增長速度上還是可以劃分出梯隊的,僅以這一段時間來說,林志穎、郭德綱、范冰冰、趙薇、劉亦菲、陳喬恩、黃曉明夫婦、胡歌明顯是在第一梯隊的。而趙麗穎、張小嫻、賈乃亮、文章同學的粉絲增長速度則稍緩。陳羽凡總粉絲僅有500萬,但是粉絲增長速度如此之快,明顯不是一個正常狀態,這種情況就是受到熱點事件的影響了。

2. 誰的合格粉絲增長最快?

回顧之前的定義:合格粉絲是指滿足粉絲數大於1、微博數大於0和用戶名非默認三個條件任意一個的粉絲。

df[auto_name] = df[nick].map(lambda x: 1 if x.startswith(用戶) else 0)
df_stats2 = df.query((fan_cnt>1) or (post_cnt>0) or (auto_name==1)).groupby(star_name)[id].count()
df_stats2.sort_values(ascending=False).plot(kind=bar, figsize=(18,9), fontsize=16)

林志穎郭德綱位置不變,陳羽凡直接衝到榜單第四。單純看這一指標意義不大,但是如果跟前邊的指標結合起來,看一個合格粉絲的比例,就有意義了。

3. 誰的粉絲合格比例最高?

這一指標已經有些尖銳了,他已經在某種程度上反映了不同明星的粉絲在微博上的活躍程度。活躍的用戶各有各的活躍,而不活躍的用戶就那麼幾種:一種是隻看不說,也沒有社交圈子沒人關注;一種是新用戶尚未開始發力;還有一種,就是殭屍了,具體是人造殭屍還是程序殭屍,我們不得而知。

df_tmp = pd.concat([df_stats1, df_stats2], axis=1)
df_tmp.columns = [粉絲樣本數, 合格粉絲數]
df_tmp[合格比例] = df_tmp[合格粉絲數] / df_tmp[粉絲樣本數]
# df_tmp[合格比例] = df_tmp[合格比例].map(lambda x: {0:.2f}%.format(x*100))
df_tmp.index.name = 明星姓名
df_tmp.sort_values(合格比例, ascending=False)[合格比例].plot(kind=bar, figsize=(18,9), fontsize=16, grid=True)

張小嫻、文章的粉絲增長速度不算快,但是基本上都符合我們設定的合格標準。至於陳羽凡和賈乃亮……我只能說這個時間太過真實。全世界人民都愛喫瓜,這不是僅停留在嘴上說說而已的。吸毒隊、出軌隊每每有了新隊員,當事人、相關人都會受到莫大的關注。僅有500萬粉絲的陳羽凡,短時間內的粉絲增長速度直逼甚至超過近億粉絲的大腕兒們。

張小嫻的粉絲如此合格讓我感到很欣慰,因為合格比例從某種程度上也可以說是非殭屍比例,這說明即使炒作已經遍及各個領域,但是至少有些文字工作者還是有底線的,不會單純地為了數據而去做刷榜的事情。當然,這並不能排除她也有團隊在做買粉等商業運作,只是我覺得這種商業運作是非常正常的。就像我們打廣告廣而告之一樣,這是一種提高曝光度、提升身價的方法,並不算在數據上作假,我覺得無可厚非。

4. 誰的粉絲更活躍?

我們增加一個定義,那就是粉絲數大於1或者微博數大於0的,我們勉強算作活躍粉絲。至於僅進行了改名操作的用戶,我們暫且算作不活躍。

df_stats20 = df.query((fan_cnt>1) or (post_cnt>0)).groupby(star_name)[id].count()
df_tmp = pd.concat([df_stats1, df_stats20], axis=1)
df_tmp.columns = [粉絲樣本數, 活躍粉絲數]
df_tmp[活躍比例] = df_tmp[活躍粉絲數] / df_tmp[粉絲樣本數]
# df_tmp[活躍比例] = df_tmp[活躍比例].map(lambda x: {0:.2f}%.format(x*100))
df_tmp.index.name = 明星姓名
df_tmp.sort_values(活躍比例, ascending=False)[活躍比例].plot(kind=bar, figsize=(18,9), fontsize=16, grid=True)

榜單順序與合格榜差異不算大,但是活躍比例就有些觸目驚心了,最低的粉絲活躍比例能在20%以下。當然,這個比例並不能代表每個明星全部粉絲的表現。事實上,這些粉絲中,相當大的比例應該都是新用戶,對於新用戶來說,不活躍再正常不過了。

5. 誰的粉絲是主動關注的?

前邊提到,通過搜索和找人功能關注某個明星的,我們稱之為主動粉。主動粉一方面體現了該明星忠實粉絲的比例,另一方面也體現了該明星近期是否火熱。

df[froms] = df[from].map(lambda x: x if x in [微博推薦, 微博搜索, 微博找人] else 其他)
df_stats3 = df.groupby([star_name, froms])[id].count().unstack().fillna(0).applymap(int)
df_stats1.name = 粉絲樣本數
df_tmp = pd.concat([df_stats1, df_stats3], axis=1)
df_tmp[主動粉比例] = df_tmp[[微博找人, 微博搜索]].sum(axis=1) / df_tmp[粉絲樣本數]
df_tmp.index.name = 明星姓名
df_tmp = df_tmp.sort_values(主動粉比例, ascending=False).reset_index()
df_tmp[主動粉比例] = df_tmp[主動粉比例].map(lambda x: {0:.2f}%.format(x*100))
df_tmp

嗯……同樣是大腕兒,差距怎麼就這麼大呢?

文章同學的主動粉比例高達73%,看來他的粉絲是真愛啊。拋卻出軌的問題無法接受外,我也挺喜歡文章的作品風格的。榜單前三的另外兩位不做多說,出軌隊(及相關人員)、吸毒隊向來是喫瓜羣眾最關注的隊伍。張小嫻在榜單第四,看來知識輸出帶來的粉絲更真實、更主動。

至於榜單靠後的這些,也不是說他們的粉絲基礎比較差。我們都知道,新用戶註冊完成之後,微博會羅列一大堆名人明星、媒體號等,給新用戶選擇去關注哪些。主動粉比例較低,說明這種通過推薦的方式得到的新增粉絲比例較大。也許他們的主動粉也不少,只是近期團隊花了較多的精力在購買推薦位上(也可能是微博主動提供的,我不得而知)。

6. 誰的女粉比例最高?

我們都知道,微博是女同學的大本營。前一段王校長抽獎,讓我們知道了微博上活躍的用戶有112/113是女生。那麼這些名人明星們,他們的新增粉絲中的男女比例是怎麼樣的呢?

df_stats4 = df.groupby([star_name, gender])[id].count().unstack().fillna(0).applymap(int)
df_stats4.columns = [女粉數, 男粉數]
df_tmp = pd.concat([df_stats1, df_stats4], axis=1)
df_tmp[女粉比例] = df_tmp[女粉數] / df_tmp[粉絲樣本數]
df_tmp.index.name = 明星姓名
df_tmp = df_tmp.sort_values(女粉比例, ascending=False).reset_index()
df_tmp[女粉比例] = df_tmp[女粉比例].map(lambda x: {0:.2f}%.format(x*100))
df_tmp

嗯……觸目驚心啊!

首先,陳坤同學以他的盛世美顏俘虜了一大批妹子的芳心!結合微博男女失衡以及陳坤的個人屬性,我們認為這個比例雖然有些高但還可以接受,畢竟後邊幾位名人的女粉比例跟他也沒差多少。然後張小嫻的粉絲多為女粉,這個就更好理解了,男生看言情內容的還是比較少的(雖然當年我很愛看……我確定我是直男!)。

榜單中間的我們不多聊,聊聊比較極端的。

唐嫣的粉絲中,高達82%的粉絲為男粉!!!而男粉絲並不活躍,所以這就是為什麼她的活躍粉、合格粉、主動粉的比例這麼低?

我產生了以下推測: 1. 微博的推薦位是可以購買的 2. 微博的推薦位可以個性化購買,比如選定性別、地域等 3. 最近唐嫣的團隊購買了大量男性新用戶的推薦位或者微博很貼心地為她免費提供了大量男性新用戶的推薦位 4. 希望大家一起來幫忙想一下還有什麼原因能產生如此極端的結果 5. 想不起來也沒關係,存在即是合理,我們尊重結果就是了

我們看到,上邊推測下方有5條記錄,這說明我們有5條推測可以說明這個結果是合理的,都散了吧。

7. 誰的粉絲對熱度貢獻更大?

這一指標能在一定程度上反應平均每個粉絲對於該明星流量、熱度的貢獻大小。但是呢,由於評論、轉發、點贊的行為和是否是粉絲沒有關係,所以刷轉發、評論、點贊和刷粉絲榜是完全獨立的。也就是說,假如兩邊的數據都摻雜了水分,那他們直接原有的相關性會被污染的數據所掩蓋。

由於每條微博的轉發、評論和點贊量不是完全在一個數量級,所以我們對三個貢獻量做一個簡單的0-1標準化,並以相同權重相加得到綜合貢獻量這一指標。

df2_stats = df2.groupby(star_name).mean()
df2_stats[轉發貢獻量] = df2_stats[forward] / df2_stats[total_fans]
df2_stats[評論貢獻量] = df2_stats[comment] / df2_stats[total_fans]
df2_stats[點贊貢獻量] = df2_stats[like] / df2_stats[total_fans]
df2_stats[avg_forward_normal] = (df2_stats[轉發貢獻量] - df2_stats[轉發貢獻量].min()) / (df2_stats[轉發貢獻量].max() - df2_stats[轉發貢獻量].min())
df2_stats[avg_comment_normal] = (df2_stats[評論貢獻量] - df2_stats[評論貢獻量].min()) / (df2_stats[評論貢獻量].max() - df2_stats[評論貢獻量].min())
df2_stats[avg_like_normal] = (df2_stats[點贊貢獻量] - df2_stats[點贊貢獻量].min()) / (df2_stats[點贊貢獻量].max() - df2_stats[點贊貢獻量].min())
df2_stats[綜合貢獻量] = df2_stats[[avg_forward_normal, avg_comment_normal, avg_like_normal]].sum(axis=1) / 3
df2_stats = df2_stats.sort_values(綜合貢獻量, ascending=False)
df2_stats[total_fans] = df2_stats[total_fans].map(int)
df2_stats = df2_stats[[total_fans, 轉發貢獻量, 評論貢獻量, 點贊貢獻量, 綜合貢獻量]]
df2_stats.columns = [粉絲數, 轉發貢獻量, 評論貢獻量, 點贊貢獻量, 綜合貢獻量]
df2_stats.index.name =
df2_stats

作圖:

df2_stats[轉發貢獻量].sort_values(ascending=False).plot(kind=bar, figsize=(18,9), fontsize=16, grid=True)
df2_stats[評論貢獻量].sort_values(ascending=False).plot(kind=bar, figsize=(18,9), fontsize=16, grid=True)
df2_stats[點贊貢獻量].sort_values(ascending=False).plot(kind=bar, figsize=(18,9), fontsize=16, grid=True)
df2_stats[綜合貢獻量].sort_values(ascending=False).plot(kind=bar, figsize=(18,9), fontsize=16, grid=True)

這裡我們看到了跟粉絲數據相悖的一些地方。比如說主動粉、活躍粉比例高的陳羽凡、文章等人在這裡得到了較低的粉絲貢獻量數據。

同時男粉比例較高的名人基本上得到了較低的粉絲貢獻量數據,這也從側面驗證了之前微博老大所說的女用戶更活躍是的確存在的現象。

不太一樣的是雖然唐嫣的男粉比例異常地高,但她的粉絲貢獻量數據排在榜單前三。也許是男粉對於男明星只是默默關注,但對於女明星就會比較愛表現吧……還有一種可能就是許多喜歡唐嫣的粉絲並沒有關注她,但是每次都能及時出現在她的微博下並紛紛轉發、評論和點贊。

啊,其實我覺得唐嫣挺漂亮的,我也挺喜歡她的,但是數據長這樣真不能怪我啊!

五、結論

我不敢下結論,你們自己總結吧……

總得來說,這裡的數據不能代表總體,這裡的分析方法也存在很多問題,作者本人沒有任何明顯的傾向性,也不屬於任何門派,也不是誰派來的黑子或者捧哏,大家看了權當一樂。如果有更好的分析思路,可以在下方留言,咱們共同探討。


推薦閱讀:
相關文章