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条子评论,耗时也不少。有多机子可以考虑使用分布式爬虫。
posted @ 2020-09-13 22:45  forxtz  阅读(2322)  评论(0编辑  收藏  举报