網(wǎng)易云課堂 VS 慕課網(wǎng) 哪家網(wǎng)課評(píng)分高?
作者 | 丁毅
來源 | 簡說Python
「本文目錄」
1 數(shù)據(jù)抓取
2 數(shù)據(jù)清洗
2.1 數(shù)據(jù)格式統(tǒng)一
2.2 空值處理
2.3 數(shù)據(jù)去重
2.4 評(píng)論清洗
3 數(shù)據(jù)分析及可視化
3.1 課程評(píng)分分析
3.2 用戶昵稱格式
3.3 各平臺(tái)評(píng)論的平均長度
3.4 各平臺(tái)評(píng)論高頻詞
3.5 評(píng)論數(shù)與課程評(píng)分之間的關(guān)系
參考資料
本文通過爬取的數(shù)據(jù),對(duì)中國大學(xué)MOOC(icourse)、慕課網(wǎng)(imooc)、騰訊課堂(keqq)、網(wǎng)易云課堂(study163)四大網(wǎng)課平臺(tái)的課程信息及評(píng)論進(jìn)行簡要分析(包含5k+課程數(shù)據(jù)和34w+課程評(píng)論信息)。同時(shí),對(duì)數(shù)據(jù)分析的整體流程做一個(gè)總結(jié)。內(nèi)容如有紕漏,敬請(qǐng)指出。
本文相關(guān)源碼和部分?jǐn)?shù)據(jù)可以在文末獲取。
1 數(shù)據(jù)抓取
數(shù)據(jù)的獲取是我們進(jìn)行數(shù)據(jù)分析的第一步?,F(xiàn)在獲取數(shù)據(jù)的主要途徑一般為:現(xiàn)成數(shù)據(jù);自己寫爬蟲去爬取數(shù)據(jù);使用現(xiàn)有的爬蟲工具爬取所需內(nèi)容,保存到數(shù)據(jù)庫,或以文件的形式保存到本地。
如果是想通過自己寫爬蟲來爬取數(shù)據(jù),那么整體思路大致分為:
確定爬取的內(nèi)容 對(duì)主頁面解析 子頁面的獲取 子頁面的解析 數(shù)據(jù)的保存
現(xiàn)在的網(wǎng)站或多或少都有一些基本的反爬措施,那么,我們在寫爬蟲時(shí)就應(yīng)針對(duì)該網(wǎng)站制定相應(yīng)的反反爬策略,如請(qǐng)求頭、IP代理、cookie限制、驗(yàn)證碼限制等,這些常見的反爬機(jī)制要能夠應(yīng)用在你寫的爬蟲當(dāng)中。
如果爬蟲大致能夠爬取我們所需的內(nèi)容,下一步,我認(rèn)為就是提高爬取速度,增加穩(wěn)定性了。
我們知道當(dāng)requests模塊對(duì)頁面發(fā)起請(qǐng)求時(shí),整個(gè)程序是處于阻塞狀態(tài),在請(qǐng)求的這段時(shí)間,后面的代碼是無法運(yùn)行的,所以說當(dāng)我們需要對(duì)很多個(gè)頁面發(fā)起請(qǐng)求時(shí),我們可以通過使用異步協(xié)程的方式,使我們能夠利用阻塞的這段時(shí)間去執(zhí)行其他任務(wù)。由于requests模塊是不支持異步協(xié)程的,我們需要使用aiohttp模塊來對(duì)頁面發(fā)起請(qǐng)求,再搭配asyncio來實(shí)現(xiàn)異步爬蟲。
提高穩(wěn)定性,就需要一些穩(wěn)定的ip代理,防止爬蟲運(yùn)行期間ip被封,推薦自己爬取一些免費(fèi)的ip代理的網(wǎng)站,通過代碼測試一下,將能用的保存到數(shù)據(jù)庫中,使用時(shí)直接通過類來使用即可。如果你對(duì)爬蟲中的ip代理的使用還是很了解,不妨看下這篇文章(異步爬蟲)requests和aiohttp中代理IP的使用。
因?yàn)槲覜]用過爬蟲工具,所以就不介紹了。
2 數(shù)據(jù)清洗
數(shù)據(jù)得到手,我們就需要對(duì)我們爬取的數(shù)據(jù)進(jìn)行清洗工作,為之后的數(shù)據(jù)分析做鋪墊,如果清洗的不到位勢必會(huì)對(duì)之后的數(shù)據(jù)分析造成影響。
下文將從數(shù)據(jù)格式統(tǒng)一、空值處理、數(shù)據(jù)去重、評(píng)論清洗等方面來介紹。
2.1 數(shù)據(jù)格式統(tǒng)一
對(duì)于四個(gè)平臺(tái)的數(shù)據(jù),由于各個(gè)平臺(tái)爬取的內(nèi)容有所不同,數(shù)據(jù)類型也有差別,我們根據(jù)后期分析的需要,提取我們需要的內(nèi)容,數(shù)據(jù)類型與格式統(tǒng)一后再將課程及評(píng)論信息合并。
如課程評(píng)分中,imooc的評(píng)分格式為‘9分’,而數(shù)據(jù)分析時(shí),只需要9來作為分?jǐn)?shù)即可。各個(gè)平臺(tái)的評(píng)分要求不同,這里統(tǒng)一5分制。
#?正則提取數(shù)字
df['評(píng)論分?jǐn)?shù)']?=?df['評(píng)論分?jǐn)?shù)'].str.extract(r'(\d+)',?expand=True)
#?類型轉(zhuǎn)換,并改為五分制
df['評(píng)論分?jǐn)?shù)']?=?df['評(píng)論分?jǐn)?shù)'].values.astype(float)/2
重新對(duì)列名進(jìn)行命名
df.rename(columns=({'課程名':'course_name',?'學(xué)習(xí)人數(shù)':'total_stu'}),?inplace=True)
2.2 空值處理
對(duì)于數(shù)據(jù)中的空值(Nan),如果該行中的數(shù)據(jù)對(duì)后面數(shù)據(jù)分析影響不大。那么,直接刪除改行即可。
#?直接刪除course_id列中值為空的行(不包含空字符串)
df?=?df.dropna(subset=['comment'])
如果要對(duì)空字符串進(jìn)行刪除,直接使用上述方法并不能實(shí)現(xiàn)。可以先將字符串轉(zhuǎn)為np.nan類型,再使用dropna來刪除。
#?將空字符串轉(zhuǎn)為'np.nan',用于下一步刪除
df['course_id'].replace(to_replace=r'^\s*$',?value=np.nan,?regex=True,?inplace=True)
#?刪除course_id中的空值,并重置索引
df?=?df.dropna(subset=['course_id'])
df.reset_index(drop=True,?inplace=True)
2.3 數(shù)據(jù)去重
用于爬取時(shí)的誤差,部分?jǐn)?shù)據(jù)有部分重復(fù),這時(shí)就需要?jiǎng)h除這些重復(fù)的數(shù)據(jù),只保留一條即可。
#?根據(jù)course_id列的唯一性,可以把它作為作為參照,如存在多行course_id相同,那么只保留最開始出現(xiàn)的。
df.drop_duplicates(subset=['course_id'],?keep='first',?inplace=True)
#?重置索引
df.reset_index(drop=True,?inplace=True)
2.4 評(píng)論清洗
對(duì)單一用戶的重復(fù)評(píng)論去重。對(duì)于同一用戶在不同時(shí)間對(duì)同一課程進(jìn)行的評(píng)論如果內(nèi)容相同,可以認(rèn)為該用戶在評(píng)論時(shí)并未認(rèn)真思考,因此應(yīng)只保留第一次,以保證后期分析數(shù)據(jù)的有效性。
df.drop_duplicates(subset=['user_id',?'comment'],?keep='first',?inplace=True)
df.reset_index(drop=True,?inplace=True)
去除評(píng)論中的換行符(\n)和回車(\r)。
df['comment']?=?df['comment'].str.replace('\r|\n',?'')
去除評(píng)論開頭和結(jié)尾的空格。
df['comment']?=?df['comment'].str.strip()
對(duì)于純數(shù)字評(píng)論(如‘111’,‘123456’,‘666’),無實(shí)際意義,并不能說明對(duì)某一事物的評(píng)價(jià),應(yīng)刪除。先通過正則將其替換成空字符串,后面統(tǒng)一刪除。
df['comment']?=?df['comment'].str.replace('^[0-9]*$',?'')
對(duì)于單一重復(fù)字符評(píng)論(如‘a(chǎn)aaa’,‘?。。 ?,無實(shí)際意義。
df['comment']?=?df['comment'].str.replace(r'^(.)\1*$',?'')
部分評(píng)論包含時(shí)間(如‘2020/11/20 20:00:00打卡’),通過正則匹配將時(shí)間日期轉(zhuǎn)為空字符串,防止影響之后對(duì)評(píng)論的分詞。
df['comment']?=?df['comment'].str.replace(r'\d+/\d+/\d+?\d+:\d+:\d+',?'')
「機(jī)械壓縮去詞」
(1)機(jī)械壓縮去詞思想由于評(píng)論信息中評(píng)論質(zhì)量參差不齊,沒有實(shí)際意義的評(píng)論有很多,只通過簡單的文本去重,很難將那些沒有意義的評(píng)論大量刪除,因此經(jīng)過簡單的文本去重后,還要使用機(jī)械壓縮進(jìn)行再次去重。如‘非常好非常好’,‘好呀好呀’等。
這類連續(xù)重復(fù)的評(píng)論,在之前的清洗中很難刪除,但放任不管,當(dāng)之后情感分析時(shí),經(jīng)過分詞,積極詞匯量與實(shí)際詞匯量相差較多,對(duì)后期統(tǒng)計(jì)會(huì)產(chǎn)生較大影響。
(2)機(jī)械壓縮去詞的結(jié)構(gòu)從一般的評(píng)論來講,人們一般只會(huì)在開頭和結(jié)尾添加無意義的重復(fù)語料,如‘為什么為什么課程這么貴’,‘真的非常好好好’。而中間出現(xiàn)連續(xù)詞時(shí),大多是成語及名詞修飾等,如‘老師講課真的滔滔不絕,如同江河!’等。如果對(duì)這樣的詞進(jìn)行壓縮,可能會(huì)改變語句原意。因此,只對(duì)開頭和結(jié)尾出現(xiàn)重復(fù)詞時(shí)進(jìn)行壓縮去詞。
(3)機(jī)械壓縮處理過程及規(guī)則制定連續(xù)累贅重復(fù)的判斷可以通過建立兩個(gè)存放字符的列表來完成,一個(gè)一個(gè)的讀取字符,并按照不同的情況,將字符放入第一個(gè)或第二個(gè)列表或觸發(fā)壓縮判斷,若觸發(fā)判斷得出的結(jié)果是重復(fù)(即列表1和列表2有意義的字符部分完全相同)則進(jìn)行去除,這里根據(jù)python數(shù)據(jù)分析與挖掘?qū)崙?zhàn)書中的規(guī)則參考,指定七條規(guī)則。
「規(guī)則1」:如果讀入的字符與列表1的第一個(gè)字符相同,而列表2中沒有放入任何字符,則將這個(gè)字符放入列表2中。
「規(guī)則2」:如果讀入的字符與列表1中的第一個(gè)字符相同時(shí),而列表2也有字符,那么觸發(fā)壓縮判斷,若結(jié)果是重復(fù),則進(jìn)行去除,并清空列表2。
「規(guī)則3」:如果讀入的字符與列表1的第一個(gè)字符相同,而列表2也有字符,觸發(fā)壓縮判斷,若不重復(fù),則清空兩個(gè)列表,并把讀入的這個(gè)字符放入列表1的第一個(gè)位置。
「規(guī)則4」:如果讀入的字符與列表1的第一個(gè)字符不相同,觸發(fā)壓縮判斷,若重復(fù),且兩個(gè)列表中字符數(shù)目大于2,則去除,并清空兩個(gè)列表,將該字符存入列表1。
「規(guī)則5」:如果讀入的字符與列表1的第一個(gè)字符不相同,觸發(fā)壓縮判斷,若不重復(fù),且列表2中沒有字符,則繼續(xù)在列表1中放入字符。
「規(guī)則6」:如果讀入的字符與列表1的第一個(gè)字符不相同,觸發(fā)壓縮判斷,若不重復(fù),且列表2中有字符,則繼續(xù)在列表2中放入字符。
「規(guī)則7」:讀完所有字符后,觸發(fā)壓縮判斷,對(duì)列表1與列表2進(jìn)行比較,若重復(fù)則刪除。
(4)機(jī)械壓縮效果展示
3 數(shù)據(jù)分析及可視化
3.1 課程評(píng)分分析
首先對(duì)各個(gè)平臺(tái)課程的評(píng)分進(jìn)行分析,并可視化。
由于平臺(tái)數(shù)據(jù)中只有兩個(gè)平臺(tái)包含課程評(píng)分,所以只對(duì)這兩個(gè)平臺(tái)的課程評(píng)分進(jìn)行分析。
從合并好的課程信息csv中選擇兩平臺(tái)的500門課程評(píng)分進(jìn)行分析,統(tǒng)計(jì)各個(gè)評(píng)分區(qū)間的課程數(shù)量。
df?=?pd.read_csv(r'merge_course.csv',?usecols=['platform',?'rating'])
df?=?df.loc[df['platform']?==?'imooc'].head(500)
print(len(df[df['rating']?>=?4.75]))
print(len(df[(df['rating']?4.75)?&?(df['rating']?>=4.5)]))
print(len(df[(df['rating']?4.5)]))
再數(shù)據(jù)通過matplotlib庫進(jìn)行可視化。得到下圖。

?通過圖可以很清晰的看出,兩個(gè)平臺(tái)的評(píng)分區(qū)間分布,總的來說study163平臺(tái)的課程整體評(píng)分要比imooc平臺(tái)高一些,4.5分以下的課程只占1.2%。imooc中網(wǎng)課的評(píng)分4.5分以上的占94.8%,study163中網(wǎng)課的評(píng)分4.5分以上的占98.8%,都超過了90%+,從整體來看學(xué)習(xí)者對(duì)網(wǎng)課的內(nèi)容和質(zhì)量還是很認(rèn)可的,但少部分低于4.5分的用戶評(píng)分來看,說明網(wǎng)課可以改進(jìn)的地方還是有的。
?
3.2 用戶昵稱格式
用戶名從評(píng)論信息中獲取,通過去重,來保證用戶名的唯一性,由于keqq平臺(tái)用戶名是隱藏的,所以不進(jìn)行統(tǒng)計(jì)。
df?=?pd.read_csv(r'/new_merge_comment.csv',?
????low_memory=False,?usecols=['platform',?'user_name'])
df?=?df.loc[df['platform']=='mooc163',?:]
df.dropna(inplace=True)
df.drop_duplicates(keep='first',?inplace=True)
df.reset_index(drop=True,?inplace=True)
df?=?df.head(30000)
chinese_name?=?[]
for?name?in?df['user_name'].values:
????if?re.findall(r'[\u4e00-\u9fff]',?name):
????????chinese_name.append(name)
pure_chinese_name?=?[]
for?name?in?chinese_name:
????if??name?==?''.join(re.findall(r'[\u4e00-\u9fff]',?name)):
????????pure_chinese_name.append(name)
print(30000-len(chinese_name))???#?字符
print(len(pure_chinese_name))????#?中文
得到各個(gè)平臺(tái)的用戶名格式,就可以進(jìn)行可視化了。結(jié)果如下圖。

?根據(jù)圖中信息可知,在imooc平臺(tái),三種格式的用戶名使用人數(shù)大致持平,而在icourse和study163平臺(tái)上使用純字符來做為昵稱的人數(shù)要明顯多于純中文的昵稱,數(shù)字和英文昵稱一定程度上更加潔簡,同時(shí)也可以避免被熟人認(rèn)出,其中應(yīng)該個(gè)體學(xué)習(xí)者居多,而學(xué)校安排的網(wǎng)課,更多要求考核學(xué)生,一般會(huì)要求學(xué)生使用真實(shí)姓名作為昵稱,從昵稱角度分析用戶可以參考這篇文章xxx。
?
3.3 各平臺(tái)評(píng)論的平均長度
之前已經(jīng)統(tǒng)計(jì)了各個(gè)平臺(tái)課程評(píng)論的平均長度,現(xiàn)在只需要取它們的平均值即可。
df?=?pd.read_csv(r'Chinese_comment.csv',?low_memory=False,
?????usecols=['platform',?'average_length'])
print(df.groupby('platform').describe().reset_index(drop=None))

可視化結(jié)果如下。

?四個(gè)平臺(tái)的評(píng)論平均長度以騰訊課堂(keqq)最高,中國大學(xué)MOOC(icourse)與網(wǎng)易云課堂(study163)相近,慕課網(wǎng)(imooc)最低。用戶評(píng)論從兩個(gè)方面考慮,正向(好評(píng))和反向(差評(píng)),但不論哪個(gè)方面,用戶評(píng)論越長,一定程度說明用戶更加喜歡這平臺(tái),對(duì)這個(gè)平臺(tái)有更多的期待。
?
3.4 各平臺(tái)評(píng)論高頻詞
提取各個(gè)平臺(tái)的積極消極高頻詞。
使用dataframe保存讀取csv文件中的內(nèi)容,再從dataframe中讀取每一條評(píng)論,使用groupby()函數(shù)按課程名進(jìn)行分組,同時(shí)將該課程評(píng)論合并到同一列。
course_comment?=?df.groupby(['course_id'])['comment'].apply(sum)
將dataframe中的數(shù)據(jù)保存到字典中,創(chuàng)建一個(gè)新的dataframe對(duì)象new_df來保存課程名和提取的正面/負(fù)面高頻詞,循環(huán)字典中的鍵‘comemnt’的每一個(gè)評(píng)論,使用jieba分詞精準(zhǔn)模式對(duì)其進(jìn)行分詞,對(duì)比導(dǎo)入的中文停用詞表將詞匯列表中無用的字符漢字進(jìn)行剔除。之后導(dǎo)入positive.txt,negative.txt分別作為正面情感詞典,負(fù)面情感詞典。循環(huán)每個(gè)分詞后得到的課程正面/負(fù)面詞匯列表,使用Courter函數(shù)獲取正面/負(fù)面詞匯列表出現(xiàn)次數(shù)最多的五組詞。
up_list?=?Counter(positive_list).most_common(5)
down_list?=?Counter(negative_list).most_common(5)
之后將得到的高頻詞和課程名保存到new_df中,循環(huán)直至所有的課程評(píng)論都已保存。
到這里已經(jīng)獲取了各平臺(tái)每一課程中所有評(píng)論的高頻詞。接下來只需要整合每個(gè)平臺(tái)課程的高頻詞,將其以詞匯圖的形式來展現(xiàn)即可。這里以慕課網(wǎng)(imooc)平臺(tái)的積極消極高頻詞作為展示。可視化結(jié)果如下圖
課程評(píng)論積極高頻詞

課程評(píng)論消極高頻詞
?從這兩個(gè)詞云圖中的高頻詞,不難看出,用戶對(duì)課程的難易度、教師的講課水平、課程的整體結(jié)構(gòu)、課后習(xí)題的安排有較高的要求,如積極詞匯中出現(xiàn)較多的“易懂”、“詳細(xì)”、“基礎(chǔ)”、“清晰”等,消極詞匯中出現(xiàn)較多的“復(fù)雜”、“辛苦”、“廢話”等。
?
3.5 評(píng)論數(shù)與課程評(píng)分之間的關(guān)系
這里我們隨機(jī)選取慕課網(wǎng)(imooc)和網(wǎng)易云課堂(study163)200門課程,分別統(tǒng)計(jì)它們的課程評(píng)分和評(píng)論數(shù)。
df?=?pd.read_csv('merge_course.csv',?low_memory=False,?
????usecols=['platform',?'course_name',?'course_id',?'rating'])
df?=?df.loc[df['platform']?==?'imooc'].reset_index(drop=None).head(200)
df_comment?=?pd.read_csv('merge_comment.csv',?low_memory=False,?
????usecols=['platform',?'course_id',?'comment'])
df_comment?=?df_comment.loc[df_comment['platform']?==?'mooc163']
series?=?df_comment.groupby('course_id')['comment'].count()
df_comment?=?pd.DataFrame()
df_comment['course_id']?=?series.index
df_comment['count']?=?series.values
df_comment['course_id']?=?df_comment['course_id'].str.extract('^(\d+)',?expand=True)
new_df?=?pd.merge(df,?df_comment,?on='course_id')
rating_list?=?new_df['rating'].values.tolist()????#?課程評(píng)分
count_list?=?new_df['count'].values.tolist()????#?課程評(píng)論
得到rating_list、count_list后通過散點(diǎn)圖可視化。結(jié)果如下圖。
慕課網(wǎng)(imooc)

網(wǎng)易云課堂(study163)

?通過兩個(gè)平臺(tái)的對(duì)比不難看出,當(dāng)評(píng)論數(shù)量較多時(shí),慕課網(wǎng)的課程評(píng)分基本穩(wěn)定在4.8--5.0之間,網(wǎng)易云課堂課程評(píng)分基本穩(wěn)定在4.9--5.0之間。兩平臺(tái)評(píng)論數(shù)量最多的課程評(píng)分分別為4.8,4.9分??梢娫u(píng)論人數(shù)越多,越不容易達(dá)到滿分。同時(shí)兩個(gè)平臺(tái)中評(píng)分較低的課程普遍評(píng)論量不高,可見當(dāng)評(píng)論人數(shù)較少時(shí),個(gè)別用戶的低分就會(huì)對(duì)課程評(píng)分產(chǎn)生較大影響;說明評(píng)論人數(shù)作為課程火熱度的一個(gè)指標(biāo)對(duì)課程評(píng)分是有直接影響的。
?
以上便是本文全部內(nèi)容,部分代碼并不完整,只提供思 路,matplotlib可視化可以參考官方文檔,所以在文中就沒放可視化相關(guān)代碼。
數(shù)據(jù)和完整源碼鏈接:https://pan.baidu.com/s/16Xyv0Ln2oVbu3oyyQdwg5g 提取碼:h9n8
你在哪些平臺(tái)學(xué)習(xí)過呢?
參考資料
「python數(shù)據(jù)分析與挖掘?qū)崙?zhàn)」https://book.douban.com/subject/26677686/
「pandas官方文檔」https://pandas.pydata.org/docs/user_guide/index.html
「matplotlib官方文檔」https://matplotlib.org/
