01 功能与效果呈现
免责声明:仅供个人学习及研究使用,严禁用于其他用途。
之前发的这篇帖子 【讨论】大家来讨论下有关于“置顶”消息处理的逻辑判断,看上去像是个专栏, 不过早些天也做得差不多了。趁周末有空,完成最后一个语法分类功能后,现已成型。在原有基础写入内容到xlsx的基础上,增加自然语言文本分析。
总功能如下:
- 自动化分类;整体匹配率:81%~94%左右;其中,微博噪音最大,失真较高,信息价值相对较低。
- 情感分析;每条文本情感值与整体平均值。(见最后透视图)
- 基础热度分析;指数平均值与总值,从指数推测三者平台用户总量占有情况。
- 语法分析;主要针对副词、数词、形容词、限定词,标记出可能存在引导情绪、话题讨论的词语
- 词频统计;如果一个热搜条目在三者信息平台都有出现,说明可能为持续性的话题热度,其信息密度较高。
热搜中使用副词频率会受到这几个方面影响:时事、八卦、赛事:使用副词的频率通常会比较高,这类话题更容易引起人们的情感共鸣和争议;媒体的倾向:举例来说,如果小编以点标题击量作为的KPI指标,那么就会更加注重主观情感解读与表达;受众群体性:口语化、俚语化、网络化最容易传播,再加副词更方便引导和传播、热议、讨论。虽动词未在统计范围内,不过一些动词也会有形容词那种生动的画面感,比如8848的广告利用了我们感知的一些肌肉记忆,通过不断重复和强调“从不把”、“致敬”这类画面感的语言单位,达到心理暗示和价值观塑造的目的,潜意识植入“高端”、“奢华”、“大气”。
以上,这就是上Stanza语言模型分析的原因。ThuLAC虽不错,但较封闭,AI的魔法略显麻烦;故选型 Stanza。
话不多说,上图



(windows可用,get_save_path_xlsx_file()
做了路径兼容处理)
02 注意说明及附源码
注意:附源码为带Stanza自然语言文法分析功能。斯坦福大学语言中文模型890M,解压后1.88G;光是网速下载估计都会劝退大半部分人,没有词性分析需求的朋友使用 https://github.com/hoochanlon/ihs-simple/blob/main/d-python/get_resou_today.py 就够用了。

pip 环境
pip install bs4
pip install jieba
pip install openpyxl
pip install requests
pip install json
pip install snownlp
pip install urllib
pip install stanza
lite
python -c "$(curl -fsSL https://ghproxy.com/https://raw.githubusercontent.com/hoochanlon/ihs-simple/main/d-python/get_resou_today.py)"
full
python -c "$(curl -fsSL https://ghproxy.com/https://raw.githubusercontent.com/hoochanlon/ihs-simple/main/d-python/get_resou_today_s.py)"
附源码:https://github.com/hoochanlon/ihs-simple/blob/main/d-python/get_resou_today_s.py
import os
from datetime import datetime
import openpyxl
import requests
from bs4 import BeautifulSoup
from openpyxl import Workbook
from snownlp import SnowNLP
import jieba
from collections import Counter
import jieba.posseg as pseg
import json
import urllib.request
import stanza
def get_save_path_xlsx_file():
"""
获取格式化后的当前时间
:return: 格式化后的当前时间字符串
"""
return os.path.join(os.path.join(os.path.expanduser("~"), "Desktop"),
"resoubang_{}.xlsx".format(datetime.now().strftime('%Y-%m-%d')))
def get_news_from_url(url: str):
"""
从指定的 URL 抓取热搜新闻
:param url: 网页 URL
:return: 热搜新闻列表
"""
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'}
r = requests.get(url, headers=headers)
r.encoding = 'utf-8'
soup = BeautifulSoup(r.text, 'html.parser')
toutiao_resoubang = soup.find_all('div', class_='single-entry tindent')
resoubang_list = []
for item in toutiao_resoubang:
spans = item.find_all('span')
for span in spans:
resoubang_list.append(span.string)
return resoubang_list
def delete_empty_rows(sheet_name: str, wb: Workbook):
"""
删除指定工作表中的空行
:param sheet_name: 工作表名称
:param wb: Excel 工作簿对象
:param None 关键字
https://notes-by-yangjinjie.readthedocs.io/zh_CN/latest/python/05-modules/openpyxl.html?highlight=openpyxl
"""
ws = wb[sheet_name]
for row in ws.iter_rows():
if all(cell.value is None for cell in row):
ws.delete_rows(row[0].row)
def calculate_average_index_and_sentiment_score(sheet_name: str, wb: Workbook):
"""
计算指定工作表中热搜新闻的平均指数和情感得分
:param sheet_name: 工作表名称
:param wb: Excel 工作簿对象
:return: 平均指数、情感得分元组
"""
ws = wb[sheet_name]
total_index = 0
count = 0
sentiment_score_list = []
for row in ws.iter_rows(min_row=2, min_col=1, max_col=3):
news_str = ''
for cell in row:
if cell.value is not None:
news_str += str(cell.value)
s = SnowNLP(news_str)
sentiment_score = s.sentiments
sentiment_score_list.append(sentiment_score)
total_index += row[2].value
count += 1
ws.cell(row=row[0].row, column=4, value=sentiment_score)
return (total_index / count, sum(sentiment_score_list) / len(sentiment_score_list))
def calculate_word_count(sheet_names: list, wb: Workbook):
"""
计算工作表中出现最多的20个单词,将结果写入新的工作表中
:param sheet_names: 工作表名称
:param wb: Excel 工作簿对象
:param stopwords_file: 停用词文件路径
停用词是指在自然语言中使用频率很高,
但通常不具有实际含义或对文本分析任务没有太大帮助的单词,如“的”,“了”等。
"""
stopwords_file = 'https://ghproxy.com/https://raw.githubusercontent.com/goto456/stopwords/master/cn_stopwords.txt'
response = requests.get(stopwords_file)
stopwords = response.content.decode('utf-8').split('\n')
for word in stopwords:
jieba.del_word(word.strip())
word_count = Counter()
for sheet_name in sheet_names:
ws = wb[sheet_name]
for row in ws.iter_rows(min_row=2, min_col=1, max_col=3):
news_str = ''
for cell in row:
if cell.value is not None:
news_str += str(cell.value)
words = jieba.lcut(news_str)
new_words = []
for word in words:
if len(word) <= 1:
continue
if not(word.isdigit() or (word.replace('w', '').replace('.', '').isdigit())):
new_words.append(word)
words = new_words
word_count.update(words)
for word in list(word_count):
if word in stopwords:
del word_count[word]
top_words = word_count.most_common(30)
ws = wb.create_sheet(title='词频统计')
ws.append(['排名', '词语', '词频'])
for i, (word, freq) in enumerate(top_words,1):
ws.append([i, word, freq])
def write_category_to_sheet(sheet_name: str, wb: Workbook):
"""
将新闻事件的关键词分类信息写入到 Excel 工作表中的第五列中
:param sheet_name: 工作表名称
:param wb: Excel 工作簿对象
:jieba分词:https://github.com/fxsjy/jieba
"""
response = urllib.request.urlopen('https://ghproxy.com/https://raw.githubusercontent.com/hoochanlon/ihs-simple/main/AQUICK/category_news.json')
json_data = response.read().decode('utf-8')
category_keywords = json.loads(json_data)
ws = wb[sheet_name]
for row in ws.iter_rows(min_row=2, min_col=1, max_col=4):
title_str = ''
for cell in row:
if cell.value is not None:
title_str += str(cell.value)
words = pseg.cut(title_str)
category = ''
for word, flag in words:
for key, keywords in category_keywords.items():
if word in keywords:
category = key
break
if category:
break
if not category:
category = '其他'
ws.cell(row=row[0].row, column=5, value=category)
def write_news_to_sheet(news_list: list, sheet_name: str, wb: Workbook):
"""
将新闻列表写入到 Excel 工作表中
:param news_list: 新闻列表
:param sheet_name: 工作表名称
:param wb: Excel 工作簿对象
:cell.value.isnumeric() 表示当前字符串是否能表示为一个数字
:isinstance(cell.value, str) 表示当前值是否字符串
"""
ws = wb.create_sheet(title=sheet_name)
row = []
for i, item in enumerate(news_list, start=1):
if i >= 156:
continue
if i % 3 == 1:
item = item.replace("、", "")
row.append(item)
if i % 3 == 0:
ws.append(row)
row = []
for row in ws.iter_rows(min_row=2, min_col=1):
for cell in row:
if cell.column == 1 or cell.column == 3:
if isinstance(cell.value, str) and not cell.value.isnumeric():
cell.value = cell.value.replace('[置顶]', '185w')
if isinstance(cell.value, str) and cell.value.isnumeric():
cell.value = int(cell.value)
elif isinstance(cell.value, str):
cell.value = float(cell.value.replace('w', ''))
ws.cell(row=1, column=3, value='指数(万)')
ws.cell(row=1, column=4, value='情感得分')
ws.cell(row=1, column=5, value='分类')
def fenmenbielei():
result_list = []
urls = ['http://resou.today/art/11.html', 'http://resou.today/art/22.html','http://resou.today/art/6.html']
sheet_names = ['今日头条热榜', '抖音时事热榜', '微博热搜']
wb = Workbook()
for url, sheet_name in zip(urls, sheet_names):
news_list = get_news_from_url(url)
write_news_to_sheet(news_list, sheet_name, wb)
delete_empty_rows(sheet_name, wb)
write_category_to_sheet(sheet_name, wb)
average_index, sentiment_score = calculate_average_index_and_sentiment_score(sheet_name, wb)
result_list.append((average_index, sentiment_score))
calculate_word_count(sheet_names, wb)
wb.remove(wb['Sheet']);wb.save(get_save_path_xlsx_file())
return result_list
def add_special_pos_columns(sheet):
'''
赠加文本词性识别
stanza : https://stanfordnlp.github.io/stanza/data_objects.html
'''
nlp = stanza.Pipeline('zh')
sheet.cell(row=1, column=6, value="是否存在特定词性")
sheet.cell(row=1, column=7, value="特定词性")
for i, cell in enumerate(sheet['B'], start=1):
if i == 1: continue
doc = nlp(cell.value)
flag = False
special_pos_list = []
for word in doc.sentences[0].words:
if word.upos in ['ADV', 'DET', 'ADJ', 'NUM']:
flag = True
special_pos_list.append(f"【{word.upos}】{word.text}")
sheet.cell(row=i, column=6, value="是" if flag else "否")
sheet.cell(row=i, column=7, value=", ".join(special_pos_list))
def load_stanza_to_sheet():
wb = openpyxl.load_workbook(get_save_path_xlsx_file())
for sheet_name in ['今日头条热榜', '抖音时事热榜', '微博热搜']:
sheet = wb[sheet_name]
add_special_pos_columns(sheet)
wb.save(get_save_path_xlsx_file())
def main():
result_list = fenmenbielei()
load_stanza_to_sheet()
print("\n热搜词性分析已加载完成,现开始计算各表热搜的指数、文本情感的平均值 \n")
for sheet_name, result in zip(['今日头条热榜', '抖音时事热榜', '微博热搜'], result_list):
average_index, sentiment_score = result
print(f'{sheet_name} 平均指数:{average_index:.2f} 情感得分: {sentiment_score:.2f}')
print("\n")
if __name__ == '__main__':
main()
经过我这次实现,实际上有兴趣的坛友可以用AI API的方式来完成写入xlsx智能分类,词组分析,看看行不行。我的方式的核心就是命中组建语料字典命中关键字,包括Stanza语言模型也是。Stanza虽说是神经网络和深度学习技术来进行的文本处理,但比如说“一三一四”,“五二零”这类具有情感含义的数字就不那么本土了。用AI的话,也许会在识别精度上提高不少吧。
最后,文本分析举例个数据透视图
