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