Python3+Selenium爬蟲實(shí)戰(zhàn):微博粉絲榜水分大揭秘

高能預(yù)警!分析到最后,我不得不感慨這個世界太真實(shí)了!

文中有大量代碼,注重閱讀體驗(yàn)的請?jiān)赑C站打開!或者直接去我的個人博客(www.data-insights.cn)閱讀!

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

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

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

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

image

image

我們不知道幕后真相,也沒有人提供爆料,有的只是微博上公開的信息,那我們?nèi)绾尾拍軗荛_迷霧,從這些公開的信息中抽絲剝繭,找到我們想要的答案呢?作為一個數(shù)據(jù)工作者,自然是要從數(shù)據(jù)入手了!

預(yù)警:前方會有不少技術(shù)相關(guān)的內(nèi)容,對技術(shù)不感興趣的同學(xué)可以跳著閱讀,不會影響閱讀體驗(yàn)!

二、準(zhǔn)備工作

首先我們先觀察一下我們可以獲取到的信息。我們以人民日報為例,先在微博搜索“人民日報”。在結(jié)果頁中,我們看到了粉絲數(shù),也能看到熱門微博的轉(zhuǎn)發(fā)、評論、點(diǎn)贊數(shù)。

image

然后我們點(diǎn)進(jìn)去人民日報官博的首頁,可以看到它的更多微博,每條微博的轉(zhuǎn)發(fā)、評論和點(diǎn)贊的數(shù)據(jù)都是可用的。

image

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

image

我們梳理下現(xiàn)在確認(rèn)可以找到的信息:

  1. 微博表現(xiàn):包括評論、轉(zhuǎn)發(fā)、點(diǎn)贊
  2. 粉絲總數(shù)
  3. 每個粉絲的站內(nèi)表現(xiàn):關(guān)注數(shù)、粉絲數(shù)、微博數(shù)、性別、地址、關(guān)注來源

那么我們?nèi)绾螐倪@些數(shù)據(jù)里挖掘出誰的粉絲水分最大,誰的粉絲最真實(shí)呢?

我們定義幾個指標(biāo):

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

三、數(shù)據(jù)收集

數(shù)據(jù)收集是數(shù)據(jù)分析的關(guān)鍵步驟,畢竟巧婦難為無米之炊。那么我們?nèi)绾潍@取這些數(shù)據(jù)呢?一條微博一條微博地點(diǎn)擊然后手動錄入?這不是我們技術(shù)派的風(fēng)格。

我們準(zhǔn)備使用Python3Selenium,通過頁面抓取并解析來完成這一任務(wù)。這一部分的內(nèi)容對非技術(shù)類同學(xué)來說簡直是又臭又長,不感興趣的可以直接跳過看分析部分。

1. 自動登錄微博

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

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

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

image

單擊之后出現(xiàn)一個登錄模塊的浮層,在頁面源碼中我們發(fā)現(xiàn)了這么一個class="Bv6_layer"<div>節(jié)點(diǎn),正是這個登錄模塊對應(yīng)的源碼。這個模塊里有兩個<input>節(jié)點(diǎn),分別對應(yīng)著用戶名和密碼的輸入框,他們的name屬性的取值分別為"username""password"。輸入完用戶名和密碼,我們需要定位到登錄按鈕的元素并執(zhí)行點(diǎn)擊行為。

image

下面看下代碼:

# ./../.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('751718003@qq.com')

        # 獲取密碼框并輸入密碼
        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)函數(shù)中,我們定義了初始化的一些信息:比如我們啟動了一個無頭模式的chromedriver,設(shè)置了顯式等待的方法,初始化了粉絲數(shù)據(jù)、主頁微博數(shù)據(jù)、粉絲總數(shù)以及明星頁面ID數(shù)據(jù)。

顯示等待對應(yīng)的方法是selenium.webdriver.support.ui.WebDriverWait,它用來應(yīng)對頁面加載較慢的情況。比如說你的網(wǎng)速不太好,頁面沒有加載完成,那程序很可能會因?yàn)槎ㄎ徊坏皆囟鴪箦e。但是使用time.sleep()的話,在網(wǎng)絡(luò)條件比較好的情況下又會很浪費(fèi)時間,因此使用顯示等待就可以幫助我們在設(shè)置的超時范圍內(nèi)一直等待頁面加載我們需要定位的元素,經(jīng)濟(jì)而實(shí)用。

明星與對應(yīng)的頁面ID的關(guān)系是綁定的,也就是說我們可以抓取一次并緩存到本地,之后就可以直接從本地讀取對應(yīng)關(guān)系。

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

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

2. 獲取明星對應(yīng)的ID

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

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

image

然后,單擊粉絲數(shù)對應(yīng)的鏈接,進(jìn)入粉絲頁。

image

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

image

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

通過觀察,我們發(fā)現(xiàn),follow?relate=fans&page=x是必要的參數(shù),1003061192329374就是我們需要的ID。(后來我發(fā)現(xiàn)直接使用用戶ID也就是默認(rèn)粉絲頁的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)

        # 點(diǎn)擊搜索
        self.driver.find_element_by_id('plc_top').\
            find_element_by_class_name('gn_search_v2').find_element_by_tag_name('a').click()

        # 點(diǎn)擊進(jìn)入粉絲頁
        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. 獲取粉絲數(shù)據(jù)

我們剛才已經(jīng)知道了粉絲頁面url的格式,所以我們可以直接拼接并訪問。

觀察源碼,我們發(fā)現(xiàn)粉絲昵稱、用戶ID、性別可以在一個<li>標(biāo)簽的action-data屬性中找到,關(guān)注數(shù)、粉絲數(shù)、微博數(shù)也都能在下邊輕易找到。那我們要做的就是把他們解析并保存下來。

image

看下代碼:

    def get_fans_data(self):
        """
        獲取粉絲數(shù)據(jù)
        :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]

                    # 關(guān)注/粉絲/微博
                    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...模塊,這是因?yàn)轫撁嬷心承┳侄螘腥笔?,會出現(xiàn)定位不到元素的情況,這種頁面我們直接跳過即可。這樣,我們就可以將粉絲數(shù)據(jù)保存到self.fan_data中了。

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

    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. 獲取主頁微博表現(xiàn)數(shù)據(jù)

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

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

微博的具體內(nèi)容我們不考慮,我們只考慮每條微博的表現(xiàn)數(shù)據(jù)??梢园l(fā)現(xiàn),這個數(shù)據(jù)隱藏得非常深,但還是難不倒我們。我們依次定位到轉(zhuǎn)發(fā)、評論和點(diǎn)贊所在的元素并將文本解析出來。

image

看下代碼:

    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)

            # 轉(zhuǎn)發(fā)數(shù)據(jù)
            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]

            # 評論數(shù)據(jù)
            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]

            # 點(diǎn)贊數(shù)據(jù)
            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)

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

    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. 執(zhí)行抓取

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

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

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

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

    # 若本地緩存中沒有,則在線獲取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))

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

    # 獲取并緩存數(shù)據(jù)到本地
    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)

大功告成!鼓掌!撒花!

四、數(shù)據(jù)分析

數(shù)據(jù)到手,接下來我們就要開始從數(shù)據(jù)中發(fā)掘信息了!

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()
image

先展示下粉絲數(shù)據(jù),看起來一切正常。這里我們需要對數(shù)據(jù)執(zhí)行一下清洗:

  1. 去重:這是因?yàn)槲覀兪菨L動抓取的,用戶可能有重復(fù)。但是同一個用戶作為兩個明星的粉絲的情況我們認(rèn)為是正常的,所以以明星、粉絲用戶ID兩個key一起來去重。
  2. 地址:省級的地址就夠用了,事實(shí)上這樣還是太過分散,我們不會太關(guān)注地址。
df2 = pd.read_csv('./home.csv')
df2['forward'] = df2['forward'].map(lambda x: 0 if x =="轉(zhuǎn)發(fā)" 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()
image

然后看下主頁微博表現(xiàn)數(shù)據(jù),這里沒有展示出來的情況是,當(dāng)一條微博轉(zhuǎn)發(fā)、評論或者點(diǎn)贊的數(shù)字為0時,該數(shù)據(jù)對應(yīng)著一個字符串"轉(zhuǎn)發(fā)"、"評論"或者"贊",我們要將它們清洗為0,并全部轉(zhuǎn)化為int格式。

1. 誰的粉絲增長最快?

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

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

這個肯定不能代表總的粉絲量,但是這個數(shù)據(jù)能在一定程度上代表在我抓取數(shù)據(jù)的那段時間里,每個明星的粉絲增長量。為什么強(qiáng)調(diào)“一定程度上”呢,因?yàn)橛械拿餍堑姆劢z的增長速度遠(yuǎn)遠(yuǎn)超過了我的抓取速度,所以這里出現(xiàn)了天花板效應(yīng)。1900以上的明星在這段時間里實(shí)際增長的粉絲量都可能早已超過了2000,但我們前邊提到過,我們一共20次循環(huán),每次循環(huán)最多抓到100個,最后這兩千個還要放一起去重。

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

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

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

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)
image

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

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

這一指標(biāo)已經(jīng)有些尖銳了,他已經(jīng)在某種程度上反映了不同明星的粉絲在微博上的活躍程度?;钴S的用戶各有各的活躍,而不活躍的用戶就那么幾種:一種是只看不說,也沒有社交圈子沒人關(guān)注;一種是新用戶尚未開始發(fā)力;還有一種,就是僵尸了,具體是人造僵尸還是程序僵尸,我們不得而知。

df_tmp = pd.concat([df_stats1, df_stats2], axis=1)
df_tmp.columns = ['粉絲樣本數(shù)', '合格粉絲數(shù)']
df_tmp['合格比例'] = df_tmp['合格粉絲數(shù)'] / df_tmp['粉絲樣本數(shù)']
# 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)
image
image

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

張小嫻的粉絲如此合格讓我感到很欣慰,因?yàn)楹细癖壤龔哪撤N程度上也可以說是非僵尸比例,這說明即使炒作已經(jīng)遍及各個領(lǐng)域,但是至少有些文字工作者還是有底線的,不會單純地為了數(shù)據(jù)而去做刷榜的事情。當(dāng)然,這并不能排除她也有團(tuán)隊(duì)在做買粉等商業(yè)運(yùn)作,只是我覺得這種商業(yè)運(yùn)作是非常正常的。就像我們打廣告廣而告之一樣,這是一種提高曝光度、提升身價的方法,并不算在數(shù)據(jù)上作假,我覺得無可厚非。

4. 誰的粉絲更活躍?

我們增加一個定義,那就是粉絲數(shù)大于1或者微博數(shù)大于0的,我們勉強(qiáng)算作活躍粉絲。至于僅進(jìn)行了改名操作的用戶,我們暫且算作不活躍。

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 = ['粉絲樣本數(shù)', '活躍粉絲數(shù)']
df_tmp['活躍比例'] = df_tmp['活躍粉絲數(shù)'] / df_tmp['粉絲樣本數(shù)']
# 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)
image
image

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

5. 誰的粉絲是主動關(guān)注的?

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

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 = '粉絲樣本數(shù)'
df_tmp = pd.concat([df_stats1, df_stats3], axis=1)
df_tmp['主動粉比例'] = df_tmp[['微博找人', '微博搜索']].sum(axis=1) / df_tmp['粉絲樣本數(shù)']
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
image
image

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

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

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

6. 誰的女粉比例最高?

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

df_stats4 = df.groupby(['star_name', 'gender'])['id'].count().unstack().fillna(0).applymap(int)
df_stats4.columns = ['女粉數(shù)', '男粉數(shù)']
df_tmp = pd.concat([df_stats1, df_stats4], axis=1)
df_tmp['女粉比例'] = df_tmp['女粉數(shù)'] / df_tmp['粉絲樣本數(shù)']
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
image
image

嗯……觸目驚心??!

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

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

唐嫣的粉絲中,高達(dá)82%的粉絲為男粉!?。《蟹劢z并不活躍,所以這就是為什么她的活躍粉、合格粉、主動粉的比例這么低?

我產(chǎn)生了以下推測:

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

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

7. 誰的粉絲對熱度貢獻(xiàn)更大?

這一指標(biāo)能在一定程度上反應(yīng)平均每個粉絲對于該明星流量、熱度的貢獻(xiàn)大小。但是呢,由于評論、轉(zhuǎn)發(fā)、點(diǎn)贊的行為和是否是粉絲沒有關(guān)系,所以刷轉(zhuǎn)發(fā)、評論、點(diǎn)贊和刷粉絲榜是完全獨(dú)立的。也就是說,假如兩邊的數(shù)據(jù)都摻雜了水分,那他們直接原有的相關(guān)性會被污染的數(shù)據(jù)所掩蓋。

由于每條微博的轉(zhuǎn)發(fā)、評論和點(diǎn)贊量不是完全在一個數(shù)量級,所以我們對三個貢獻(xiàn)量做一個簡單的0-1標(biāo)準(zhǔn)化,并以相同權(quán)重相加得到綜合貢獻(xiàn)量這一指標(biāo)。

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

作圖:

df2_stats['轉(zhuǎn)發(fā)貢獻(xiàn)量'].sort_values(ascending=False).plot(kind='bar', figsize=(18,9), fontsize=16, grid=True)
df2_stats['評論貢獻(xiàn)量'].sort_values(ascending=False).plot(kind='bar', figsize=(18,9), fontsize=16, grid=True)
df2_stats['點(diǎn)贊貢獻(xiàn)量'].sort_values(ascending=False).plot(kind='bar', figsize=(18,9), fontsize=16, grid=True)
df2_stats['綜合貢獻(xiàn)量'].sort_values(ascending=False).plot(kind='bar', figsize=(18,9), fontsize=16, grid=True)
image
image
image
image

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

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

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

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

五、結(jié)論

我不敢下結(jié)論,你們自己總結(jié)吧……

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 社交紅利閱讀筆記 書名:社交紅利(修訂升級版) 作者:徐志斌 出版社:中信出版社 正文前筆記: 推薦序1摘要 社交...
    鳧水閱讀 9,394評論 4 26
  • 中午吃飯的時候,隔壁桌坐了兩個女孩,一個女孩說上午在公司你聽見張姐聽說我過年只給我媽買衣服沒給婆婆買衣服時說的話了...
    茉白慢慢閱讀 585評論 0 2
  • 秋·杏葉 還算溫暖的秋,不用裹著厚重衣物。 樹縫間散落暖暖光影,靜坐樹下享受溫暖陽光和偶爾微風(fēng)的輕撫。 黃綠相間的...
    樂播報閱讀 236評論 2 4
  • 我是飛雪,這是我的第6篇讀書心得。 房價辣么高,你還敢買嗎? 股市風(fēng)險大,怎么一買它就跌,一賣它就...
    靜盈閱讀 221評論 0 0
  • 蝶舞鳥鳴嘉福報, 鮮花香味沁心潮。 和平盛世同歡慶, 長壽延年喜事招。
    秋月白_b8b3閱讀 3,658評論 0 3

友情鏈接更多精彩內(nèi)容