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条子评论,耗时也不少。有多机子可以考虑使用分布式爬虫。