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