【寫文章不易,轉載請備註出處】
隨着大數據時代的到來,如何篩選以及獲取有效信息成爲了一項重要的技能,通過使用爬蟲按照一種規則自動從萬維網篩選信息已經成爲一種重要的能力,本次實驗的目的就是通過工程實踐加深對理論知識的深入理解和綜合應用,進一步提高實踐動手能力。
(1)首先對python環境的安裝以及美團商家信息的爬取,所爬取的商家信息爲商家的id、商家名、平均得分、地址、平均價格、獲得評論總數,得到的結果保存在csv文件之中,然後通過得到的商家的id構造出可以獲取用戶評論的url,通過訪問這個url,獲取這個商家的評論以及打分,存入到csv文件之中。
(2)其次對第一個實驗爬取到的評論數據按照評分進行評論分類,得分大於30的爲好評類,小於30的爲差評類,我們在美團上面爬取得到的評論和打分,最高爲50分,將所有數據劃分爲訓練集和測試集,使用多種模型來進行一個簡單的中文情感分類,最後用測試集來檢測我們模型的泛化能力。
(3)最後對每個商家的評論建立詞雲,將多的詞展示出來。
對所需要操作進行分析主要步驟可以分成兩個階段:
- 美團評論數據的爬取
- 建立模型進行分析
主要實驗環境、配置、需要學習到的知識:
(1)python2.7
(2)Bert中文預向量處理支持、Jupyter
(3)php、json相關知識,所運用到的一些包:requests、numpy、re、csv、pandas、keras、json、urllib、os、wordSegment、word_cloud、PIL、matplotlib
一、爬取美團商家信息和評論數據
1.具體思路:
首先,確定爬取美團—武漢美食的網站地址;
然後通過json得到請求的頁面信息,期間設置headers來進行僞造請求,避免被美團頁面攔截驗證;
再者,就是通過正則匹配找到要求我們在頁面中爬取的內容;
最後,遍歷各個頁面將需要信息爬取下來輸出保存在csv文檔之中。
在爬取評論內容,得到每個商家評論和評分所在的json,我們通過正則表達式爬取需要的內容然後存入csv文件中,每個商家的評論和評分存在一個csv文件中,以該商家的poiId作爲文件的名字。
上述思路流程圖:
2.美團商鋪爬取
在商鋪爬取中,首先構造武漢美團的網站,通過設置循環訪問的頁數使用python進行訪問:
for i in range(1,8):
url = 'https://wh.meituan.com/meishi/pn' + str(i) + '/'
headers = {
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11' }
在上述構造訪問美團的url過程中,我們需要設置header才能避免訪問失敗,通過一個循環來對美團的頁面進行訪問,我們也可以設置多個頁數,這裏對商鋪的設置是爬取8頁信息,我當時爬取的時候每頁有15家商鋪的信息,8*15 = 120 家商鋪信息。
當get_info函數得到main函數傳給的wurl和headers的信息之後,通過python的requests.get得到url的信息然後存到一個response裏面,通過python的正則來獲取response.text裏面我們需要的關鍵點。
response = requests.get(url,headers = headers) # 獲取信息
poiId = re.findall(r'"poiId":(\d+),',response.text # 商家id
shopname = re.findall(r'"frontImg".*?title":(.*?)',response.text) # 商家名字
avgScore = re.findall(r'"avgScore":(.*?),'response.text) # 平均分
allCommentNum = re.findall(r'"allCommentNum":(\d+)',response.text) # 評論總數
address = re.findall(r'"address":(.*?),'response.text) # 商家地址
avgPrice = re.findall(r'"avgPrice":(\d+),'response.text) # 平均價格
這裏第一行代碼的目的是通過url、headers對目標構造訪問請求,接下來的代碼爲通過正則表達式—findall對特徵的內容進行爬取,從而得到需要的相應信息,存到對應的變量之中,這裏的poiId、shopname、avgScore、allCommentNum、address、avgPrice都是list格式,裏面有15個數據內容,因爲爬取一頁美團商家的頁面有15家商家,這在上面已經提及,然後通過append函數添加到一個總的list之中,構成一行數據,然後返回包含15個商家信息數據的list。
最後調用write_to_file函數將商家信息存到csv文件之中,循環進行8次,以此爬取8個武漢美團美食的商家頁面,總共105家商家信息。
3.商鋪評論爬取
基於我們對商鋪的爬取獲得的poiId,從第一個得到的csv文件之中獲取,這裏通過使用python對於csv的操作函數就能夠實現,簡單的操作直接略過,得到poiId用來構造訪問評論信息所在的url。
def get_originUrl(self):
"""
構造訪問店鋪的url拼接,這裏返回的每個特定商家的網頁的開頭部分,返回到調用該函數的地方,便於得到需要爬取該商家的url
"""
return parse.quote_plus('http://www.meituan.com/meishi/' + self.shop_id + '/')
然後通過對以下參數來確定得到評論和評分數據所在的url地方
params = {
'platform': '1',
'partner': '126',
'originUrl': url_code,
'riskLevel': '1',
'optimusCode': '1',
'id': self.shop_id, # 我們設置的內部poiId
'offset': '0',
'pageSize': '200', # 根據捕獲異常獲得評論數較多的量,評論少就不要了
'sortType': '1',
}
headers = {'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11' }
response = requests.get(url=url, params=params, headers=headers)
data = response.text
同樣是構造一個header,通過requests.get帶有特定的參數訪問url得到信息存入到data之中,data是一個str類型的數據。
data_dict = json.loads(data)
for item in data_dict.get('data').get('comments'):
with open(r'CommentData/{}.csv'.format(str(self.shop_id)), 'a', encoding='utf-8') as csvfile:
job_list = [item.get('comment'),item.get('star')]
write = csv.writer(csvfile)
write.writerow(job_list)
except TypeError:
pass
上面設置了一個捕獲異常的處理,主要是因爲在爬取評論的時候,一些評論爲空或者其他類型的錯誤導的出錯,這裏我們就pass掉,直接進行下一條的爬取。
二、建立模型對評論數據進行分析
一般思路是運用樸素貝葉斯算法進行文本分類,分析實驗說明書提供給我網頁,成功實現使用樸素貝葉斯算法的文本分類,主要思路爲對數據進行處理,然後讀取,處理缺失的評論,然後重置索引,對評論的特徵進行處理,根據評分進行分類,設置特徵標籤數組,分別爲好的爲1,壞的爲0,然後引入樸素貝葉斯算法訓練,通過jieba分詞將評論變爲多詞組並進行向量化,然後加入一些停用詞處理,剔除一些分析之中的干擾詞,然後實例化一個模型,通過管道把特徵詞放進行訓練,通過交叉驗證以及對測試集進行測試,當然,訓練集和測試集都打亂,最後得到精確度爲0.84002000000000006,也就是84%左右,我當然不滿意這個實驗結果。
然後通過查詢資料,瞭解到Bert,決定想用Bert進行實驗,查閱到的資料顯示Bert來做各種實驗都是結果挺高的,於是打算使用bert來進行,下面的思路主要就是面對Bert來寫的,因爲樸素貝葉斯方法我在上面也進行了大致的描述,也得到了結果。
Bert中文向量分析思路描述:
其實,不管你用什麼模型或者方法去進行簡單情感分類的訓練,他的大致流程其實和我在上面進行的樸素貝葉斯方法差不多,大致內容都是數據預處理、標識組設置—>評論數據打亂—>評論數據向量化—>輸入到模型之中—>訓練—>驗證—>得到結果這一個流程展開的,使用Bert進行分析也是這個流程,只是很多細節不一樣罷了。
在這裏能夠使用Bert主要是感謝騰訊AI實驗室所發佈的bert-as-service接口,通過這個接口,得到預訓練好的bert模型生成的句向量和詞向量,不需要引入一些特徵詞,而是通過評論數據轉換得到的句向量經過訓練得到兩個好、壞模型然後進行驗證。但是主要的缺點就是跑得挺慢的,我這裏10000多條訓練數據和1000多條驗證數據在電腦上跑了大概20多分鐘,還是功率全開的那種,聲音特別響、散熱一直在運作,所以基本上我總共跑了2個樸素貝葉斯的模型,還有3個Bert的模型。
主要實現操作:
- 導入數據
- 對評論進行過濾,只保留中文
- 構建評論數據集合——訓練數據和測試數據
- 創建評論數據好壞匹配標籤組
- 隨機打亂評論
- 通過Bert-as-service得到數據中文詞向量
- 建立美團評論情感分析模型
- 開始訓練
- 查看訓練情況並將其可視化顯示
- 測試集進行測試並得到模型驗證結果
1.導入數據
good = pd.read_csv('N_good.csv') # 導入不同評論信息
bad = pd.read_csv('N_bad.csv')
comments_10 = good.comment
comments_20 = bad.comment
這裏我就不贅述關於使用python對csv文件進行數據處理的過程了,只需要知道使用python將爬取到的商家評論全都整合到一個csv文件之中,根據實驗說明書中,將評論評分大於30的視之爲好評整合在N_good.csv文件之中,小於30的評論評分整合到N_bad.csv之中,然後通過pandas庫導入。
2.對評論進行過濾,只保留中文
for i in range(len(comments_10)): # 對好的評論進行過濾
goodcom = comments_10.iloc[i]
try:
goodcom = re.sub("[^\u4E00-\u9FA5]","",goodcom)
if len(goodcom) > 2 and len(goodcom) < 100:
X_data1.append(goodcom)
except:
pass
for j in range(len(comments_20)): # 對壞的評論進行過濾
badcom = comments_20.iloc[j]
try:
badcom = re.sub("[^\u4E00-\u9FA5]","",badcom)
if len(badcom) > 2 and len(badcom) < 100:
X_data2.append(badcom)
except:
pass
在這個階段,我們通過for循環,把每一行中的評論導入,使用正則表達式來過濾評論中的非中文字符,s"[^\u4E00-\u9FA5]"
這裏表示的是匹配中文字符的正則表達式,然後放入到goodcom之中,最後我們篩選出字符長度大於2且小於100的評論加入到好評集合之中,壞評集過程和好評一樣。
3.構建評論數據集合——訓練數據和測試數據
X_data = X_data1[:7000] + X_data2[:7000]
X_test_data = X_data1[7000:7900] + X_data2[7000:7900]
下面X_data是選出X_data1之後好的評論的前6000條和X_data2之後的壞的評論的7000條來組成訓練,X_test_data爲測試集,選出X_data1和X_data2之中的各900條。
4.創建評論數據好壞匹配標籤組
y_data = [1 for i in range(7000)] + [0 for i in range(7000)]
y_data = np.asarray(y_data, dtype=np.float32)
y_test_data = [1 for i in range(900)] + [0 for i in range(900)]
y_test_data = np.asarray(y_test_data, dtype=np.float32)
這裏主要就是在得到訓練數據之後,我們手工創建一個labels數組來作爲訓練標籤,前7000個是好的評論我們設置爲1,後7000個爲壞的評論我們設置爲0,測試集同樣如此。
5.隨機打亂評論
nums = []
nums_ = []
X_train = []
Y_train = []
X_test = []
Y_test = []
nums = np.arange(14000)
np.random.shuffle(nums)
for i in nums:
X_train.append(X_data[i])
Y_train.append(y_data[i])
nums_ = np.arange(1800)
np.random.shuffle(nums_)
for i in nums_:
X_test.append(X_test_data[i])
Y_test.append(y_test_data[i])
藉助numpy來對數據進行隨機打亂,標籤數組同樣與訓練集、測試集應着,用shuffle來隨機打亂訓練集和測試集,標籤數組對應,然後list轉換爲numpy數組,後面模型輸入的需要是numpy的數據類型。
6.通過Bert-as-service得到數據中文詞向量
bc = BertClient(ip = 'localhost')
input_train = bc.encode(X_train)
input_test = bc.encode(X_test)
這裏是重點,這行代碼將訓練集和測試集進行向量化,每一行評論數據轉換爲1*768維的句向量,通過在終端開啓一個端口,並設置相應線程來對輸入的評論數據進行向量化轉換,具體操作過程如下所示:
這裏通過導入bert的中文預向量轉換模型的地址,然後設置工作的可以說是線程數,我這裏設置的是4個線程。
中文預向量轉換模型在這裏下載。
具體說明如下所示:
這裏,箭頭指的是下載的中文的Bert訓練文件的位置、進行評論轉換的工作的線程數量,開放的轉換的本地端口號。下面爲進行訓練的過程:
7.建立美團評論情感分析模型
model = Sequential()
model.add(Dense(32, activation = 'relu', input_shape=(768,)))
model.add(Dropout(0.5))
model.add(Dense(32, activation = 'relu'))
model.add(Dense(1, activation = 'sigmoid'))
model.compile(
loss = 'binary_crossentropy',
optimizer = tf.train.AdamOptimizer(),
metrics = ['accuracy']
)
這裏運用了Keras來進行建模,用的是Sequential類來定義這個模型,然後模型使用Dense來進行全連接層拼接,引入Dropout層來進行部分數據丟棄防止過擬合,然後指定這個模型的優化器和損失函數,以及監控的數據。
8.開始訓練
start = model.fit(
input_train, Y_train,
epochs = 30,
batch_size = 256,
validation_split = 0.2,
verbose = 1
)
這裏迭代訓練30個,使用訓練集的20%來作爲驗證集,通過fit的方式將這些numpy形式的數組傳入到模型之中,開始訓練:
9.查看訓練情況並將其可視化顯示
# 將數據擬合和準確率打印出來
import matplotlib.pyplot as plt
history_dict = history.history
epochs = range(1, len(history_dict['acc']) + 1)
plt.figure()
plt.plot(epochs, history_dict['acc'], 'b',label='acc')
plt.plot(epochs, history_dict['val_acc'], 'bo',label='val_acc')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
plt.show()
plt.clf()
plt.figure()
plt.plot(epochs, history_dict['loss'], 'b', label='acc')
plt.plot(epochs, history_dict['val_loss'],'bo',label='val_acc')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()
上面兩個圖都在一定程度上顯示了我的訓練數據與測試數據擬合度是很高的,訓練集是有效的。
回憶了一下,我這裏在jupyter能夠順利運行,在vscode搭建的操作平臺下卻是顯示失敗的,主要是python裏面的繪圖函數庫有問題,我的解決方案是去使用Qt來進行繪製,mac下對於python繪圖主要爲tk。
10.測試集進行測試並得到模型驗證結果
test_loss = model.evaluate(
input_test,Y_test,
batch_size = 64,
verbose = 1
)
print(test_loss) # 輸出測試信息
使用Bert進行情感分類,經過模型訓練之後,使用測試集進行測試,可以得到該精度達到了92.8%,訓練損失爲0.20。
三、繪製詞雲
具體思路:
用jieba進行分詞,還有python提供的wordcloud庫。首先導入需要進行分詞的文本,然後用jieba將文本進行分詞,返回分詞之後的結果,中文文本需要進行分詞操作。然後就是設置顯示方式、生成詞雲、將生成的詞雲保存到本地,然後顯示出來。
d = path.dirname(__file__)
tag = np.array(Image.open(path.join(d, "Images//head.jpg")))
font_path = path.join(d,"font//msyh.ttf")
stopwords = set(STOPWORDS)
wc = WordCloud(background_color="white", # 設置背景顏色
max_words = 2000, # 詞雲顯示的最大詞數
mask = tag, # 設置背景圖片
stopwords = stopwords, # 設置停用詞
font_path=font_path, # 兼容中文字體,不然中文會顯示亂碼
)
# 生成詞雲
wc.generate(text)
這裏不想介紹了,突然發現寫得挺多的,上面就是詞雲構建的關鍵代碼,通過設置各類參數然後得到詞雲,還可以引入不同的商家的信息進行詞雲建立。