1. 程式人生 > 實用技巧 >python 輿情分析 nlp主題分析 (1) 待續

python 輿情分析 nlp主題分析 (1) 待續

參考資料:https://blog.csdn.net/Eastmount/article/details/50891162 # 該博主有很多篇幅,解釋演算法原理,演算法應用。

需求:一直想試試大資料+輿情分析,雖然資料量不是很大,大概應用一下,看看是否能從海量資料中,提取出主題思想,以看看當前的輿論導向。

具體應用案例:

微博熱門話題:#中印雙方達成五點共識# 閱讀量2.4億,討論7430條。

1、資料採集,使用python+selenium,採集該話題下的博文及作者資訊,以及每個博文下的評論及作者資訊;

2、資料預處理,採用Jieba庫,構建使用者詞典,以達到更好的分詞;情感分析,採用snownlp庫,尋找政治類積極和負面詞向量做一個訓練,再進行評論分類;

3、對博文及評論作者資訊進行分析,檢視調查主體的使用者類別概況;

4、lda主題分析,對博文做主題分析,依據top3主題關鍵字,對博文群主類看法進行分析;對正、負向評論做一次主題分析,並分別分析觀點;

本編主要先完成第一步,後續再繼續更新。

1、開啟瀏覽器,手工登入微博,這裡因為微博登入太嚴格(需要簡訊驗證才能登入),所以直接人工登入。

%%time
# 配置瀏覽器
options = webdriver.ChromeOptions()
# 此步驟很重要,設定為開發者模式,防止被各大網站識別出來使用了Selenium
options.add_experimental_option('excludeSwitches
', ['enable-automation']) # 進入網頁 browser = webdriver.Chrome(options=options) wait = WebDriverWait(browser,2) browser.get('https://s.weibo.com/weibo?q=%23%E4%B8%AD%E5%8D%B0%E5%8F%8C%E6%96%B9%E8%BE%BE%E6%88%90%E4%BA%94%E7%82%B9%E5%85%B1%E8%AF%86%23')

2.1、分析微博頁面,在博文頁可以發現,是分頁展示,下一頁需要通過點選下一頁進入。博文內容在'//div[@class="card"]'中。此處需要獲取作者名稱、作者主頁連結、博文內容、博文url(為下一步獲取評論準備)、發表日期、收藏、轉發、評論、點贊數目。

%%time
list_ct = []    # 儲存文章內容

while True:
    # 掃描文章內容,掃描一頁,判斷是否還有下一頁
    wait.until(EC.presence_of_element_located((By.XPATH,'//div[@class="card"]/div[@class="card-feed"]/div[@class="content"]')))
    # 獲取博文列表
    list_el = browser.find_elements_by_xpath('//div[@class="card"]')
    try:
        for l in list_el:
            actor_url = actor_name = content = content_url =content_date = sg = zf = pl = dz = ''
            try:
                # 獲取作者
                t = l.find_element_by_xpath('./div[@class="card-feed"]/div[@class="content"]/div[@class="info"]/div[2]/a')
                actor_url = t.get_attribute('href')
                actor_name = t.text
                
                # 博文內容
                t = l.find_elements_by_xpath('./div[@class="card-feed"]/div[@class="content"]/p[@class="txt"]')
                if len(t) > 1:
                    content = t[1].get_attribute('innerText')
                else:
                    content = t[0].get_attribute('innerText')
                    
                # 博文連結
                t = l.find_element_by_xpath('./div[@class="card-feed"]/div[@class="content"]/p[@class="from"]/a')
                content_url = t.get_attribute('href')
                content_date = t.text
                
                # 收藏、轉發、評論、點贊
                t = l.find_elements_by_xpath('./div[@class="card-act"]/ul/li')
                sg = t[0].get_attribute('innerText')
                zf = t[1].get_attribute('innerText')
                pl = t[2].get_attribute('innerText')
                dz = t[3].get_attribute('innerText')
            except Exception as e:
                pass
            # 追加
            list_ct.append([actor_url , actor_name , content , content_url,content_date,sg , zf , pl , dz ])
            
        # 輸出當前頁碼
        try:
            t = browser.find_element_by_xpath('//a[@class="pagenum"]')
            print('掃描進行到',str(t.text))
        except :
            pass
        # 判斷是否還有下一頁
        #break
        try:
            t = browser.find_element_by_xpath('//div[@class="m-page"]/div/a[@class="next"]')
            # 點選下一頁
            t.click()
        except:
            print('文章掃描結束')
            break
    except Exception as e:
        print('非正常結束:',str(e))

2.2將資料暫存到excel

%%time
# 儲存發表的文章,到excel
df = pd.DataFrame(list_ct, columns=['actor_url' , 'actor_name' , 'content' , 'content_url','content_date','sg' , 'zf','pl','dz']) 
# 去重
df.drop_duplicates(subset=['actor_url' , 'actor_name' , 'content' , 'content_url','content_date','sg' , 'zf','pl','dz'],inplace = True)
with pd.ExcelWriter(r'../data/npl_asan/wenzhangs.xlsx') as writer:
    df.to_excel(writer,index=False,sheet_name = 'Sheet1')

3、通過博文連結,進入到博文主頁,分析頁面資訊,內容需要ajax非同步更新,1、需要不斷下拉進度條到底部重新整理,並點選“檢視更多”;2、某些評論回覆的會摺疊,需要不斷點選檢視更多評論。綜上所述,先下拉到最底部,再逐個點選評論顯示,最後讀取所有評論。

#
#  處理評論的內容
#
def deal_comment_content(content):
    rel = content = content.replace(":","")
    if content.find(':回覆') != -1:
        lt = content.split('',2)
        rel = lt[len(lt)-1]
    else:
        lt = content.split('',1)
        rel = lt[len(lt)-1]
    return rel

# 獲取WB_text內容
def get_comment_data(l):
    actor = actor_url = render = render_url = content = date =  ''
    # 獲取發表者
    t = l.find_element_by_xpath('./div[@class="WB_text"]/a[1]')
    actor_url = t.get_attribute('href')
    actor = t.text

    # 判斷是否回覆的評論
    try:
        t = l.find_element_by_xpath('./div[@class="WB_text"]/a[@render="ext"]')
        render_url = t.get_attribute('href')
        render = t.text
    except NoSuchElementException:
        pass

    # 讀取評論內容
    t = l.find_element_by_xpath('./div[@class="WB_text"]')
    content = t.text
    content = deal_comment_content(content)
    
    # 讀取評論日期
    t = l.find_element_by_xpath('./div[@class="WB_func clearfix"]/div[@class="WB_from S_txt2"]')
    date = t.text
    
    return [actor , actor_url , render , render_url , content,date]

# 滾動到最底部
def scroll_down():
    while True:
        sh1 = browser.execute_script("return document.body.scrollHeight;")
        browser.execute_script('window.scrollTo(0,document.body.scrollHeight)')
        time.sleep(0.5)
        sh2 = browser.execute_script("return document.body.scrollHeight;")
        
        if sh1 == sh2: 
            break

# 載入更多評論
def loading_all_comment():
    # 執行到最下,等待檢視更多
    while True:
        scroll_down()
        try:
            morr_btn = wait.until(EC.presence_of_element_located((By.XPATH,'//span[@class="more_txt"]')))
            # 如果超時
            morr_btn.click()
        except:
            #print('執行到最底部')
            break

# 載入所有子評論
def loading_all_child_comment():
    while True:
        btns = browser.find_elements_by_xpath('//a[@action-type="click_more_child_comment_big"]')
        if len(btns) == 0:
            break
        for btn in btns:
            try:
                #browser.execute_script("arguments[0].click();", btn)
                ActionChains(browser).move_to_element(btn).click(btn).perform()  # 需要移動到該控制元件,點選才有效
                #btn.click()
                time.sleep(0.5)
            except:
                # 存在點了以後還沒載入完的,直接忽略錯誤
                pass
%%time
# 完整版執行
err_url = []
list_comment = []
for index, r in df.iterrows():
    url = r.content_url
    #url = df.loc[2,'content_url']
    print('序號:',str(index),'開始執行:',url)
    browser.get(url)
    try:
        loading_all_comment()  # 載入所有評論
        loading_all_child_comment()  # 載入所有子評論
        print('開啟所有評論')
        #等待內容
        wait.until(EC.presence_of_element_located((By.XPATH,'//div[@node-type="root_comment"]/div[@class="list_con"]')))
        list_el = browser.find_elements_by_xpath('//div[@node-type="root_comment"]/div[@class="list_con"]')
        # 遍歷
        for l in list_el:
            # 獲取博文資訊
            c = get_comment_data(l)
            list_comment.append(c)
            # 獲取博文評論資訊
            list_child = l.find_elements_by_xpath('.//div[@node-type="child_comment"]//div[@class="list_con"]')
            for lc in list_child:
                c = get_comment_data(lc)
                list_comment.append(c)
    except TimeoutException:
        print('TimeoutException:',url)
    except NoSuchElementException:
        print('NoSuchElementException:',url)
    except:
        print('something wring:',url)
        err_url.append(url)
    #break

最後輸出到檔案:

%%time
# 儲存評論到excel
df = pd.DataFrame(list_comment, columns=['actor' , 'actor_url' , 'render' , 'render_url' , 'content' , 'date']) 
# 去重
df.drop_duplicates(subset=['actor' , 'actor_url' , 'render' , 'render_url' , 'content' , 'date'],inplace = True)
with pd.ExcelWriter(r'../data/npl_asan/comments.xlsx') as writer:
    df.to_excel(writer,index=False,sheet_name = 'Sheet1')

本篇到此結束,下篇再做資料處理。

不足:沒有使用代理,也沒有使用cookies池,需要注意sleep,不知道達到什麼情況會被封號;

Wall time: 1h 34min 57s,最終534條博文+6626條子評論,耗時也不少。有多機子可以考慮使用分散式爬蟲。