一.数据抓取 本次爬虫目标网站是中国社会组织公共服务平台。
第一步 分析网站
通过浏览器“审查元素”查看源代码并获取新闻的标题、URL、时间等。不同网站有不同分析方法,本文重点是文本挖掘分析。
第二步,进入具体的新闻页面抓取相关的文本信息。
article_title = text_html.xpath(’//*[@id=“fontinfo”]/p[2]/b[1]//text()’)
publish_time = text_html.xpath(’/html/body/div[2]/div/ul[1]/li[3]/strong/text()’)[0][5:]
source_text = text_html.xpath(’//*[@id=“fontinfo”]/p[last()]//text()’)[0]
text_list = text_html.xpath(’//*[@id=“fontinfo”]//text()’)
第三步,本爬虫存在一个技巧,每条新闻的URL非常相似,这里仅变换参数来抓取新闻。
我们只需要每次抓取数据时,通过“下一页”定位下次需要抓取的URL即可,核心代码为:
第四步,数据抓取完整代码如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 import requests,re, csv, sys, timefrom lxml import htmlfrom fake_useragent import UserAgent startTime = time.time() fp = open ('中国社会组织_疫情防控.csv' ,'a' ,newline='' ,encoding='utf-8-sig' ) writer = csv.writer(fp) writer.writerow(("标题" , "时间" , "URL" , "正文内容" , "来源" ))def spider_html_info (url ): try : headers = { "User-Agent" : UserAgent().chrome } response = requests.get(url=url, headers=headers).text text_html = html.fromstring(response) next_url = "http://www.chinanpo.gov.cn" + text_html.xpath('/html/body/div[2]/div/ul[1]/li[2]/a[2]/@href' )[0 ] print ("next_url" , next_url) article_title = text_html.xpath('//*[@id="fontinfo"]/p[2]/b[1]//text()' ) title = "" .join(article_title) if title == "" : title = "" .join(text_html.xpath('//*[@id="fontinfo"]/p[3]/b[1]//text()' )) print ("title = " ,title) publish_time = text_html.xpath('/html/body/div[2]/div/ul[1]/li[3]/strong/text()' )[0 ][5 :] print ("publish_time = " , publish_time) print ("url = " , url) source_text = text_html.xpath('//*[@id="fontinfo"]/p[last()]//text()' )[0 ] source = source_text[3 :] text_list = text_html.xpath('//*[@id="fontinfo"]//text()' ) article_text = "" .join(text_list).replace('\r\n' ,'' ).replace("\xa0" , "" ).replace("\t" , "" ).replace(source_text,"" ).replace(title, "" ) writer.writerow((title, publish_time, url, article_text, source)) except : pass if url == 'http://www.chinanpo.gov.cn/1944/123496/index.html' : fp.close() endTime =time.time() useTime =(endTime-startTime)/60 print ("该次所获的信息一共使用%s分钟" %useTime) sys.exit(0 ) else : return next_urldef main (): url = "http://www.chinanpo.gov.cn/1944/125177/nextindex.html" count = 1 while True : print ("正在爬取第%s篇:" %count, url) next_url = spider_html_info(url) url = next_url count = count + 1 if __name__ == '__main__' : main()
1.爬取运行截图
可能需要安装扩展包lxml和fake_useragent。
二.导入MongoDB python中csv文件中数据添加到MongoDB数据库,使用csv中的DictReader函数读取。
1.代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 from pymongo import MongoClientimport csvdef connection (): conn=MongoClient("mongodb://localhost:27017/" ) db=conn.cncovdf set1=db.data set1.remove(None ) return set1def insertToMongoDB (set1 ): with open ('中国社会组织_疫情防控.csv' ,'r' ,encoding='utf-8' ,errors='ignore' )as csvfile: reader=csv.DictReader(csvfile) counts=0 for each in reader: set1.insert(each) counts+=1 print ('成功添加了' +str (counts)+'条数据 ' )def main (): set1=connection() insertToMongoDB(set1)if __name__=='__main__' : main()
2.运行截图
三.中文分词及高频词统计 1.结巴分词 数据预处理是指在进行数据分析之前,对数据进行的一些初步处理,包括缺失值填写、噪声处理、不一致数据修正、中文分词等,其目标是得到更标准、高质量的数据,纠正错误异常数据,从而提升分析的结果。中文文本预处理的基本步骤,包括中文分词、词性标注、数据清洗、特征提取(向量空间模型存储)、权重计算(TF-IDF)等。
“结巴”(Jieba)工具是最常用的中文文本分词和处理的工具之一,它能实现中文分词、词性标注、关键词抽取、获取词语位置等功能。其在Github网站上的介绍及下载地址为:https://github.com/fxsjy/jieba
调用命令“pip install jieba”安装jieba中文分词包如下图所示。
Jieba具有以下特点:
支持三种分词模式,包括精确模式、全模式和搜索引擎模式
支持繁体分词
支持自定义词典
代码对Python2和Python3均兼容
支持多种编程语言,包括Java、C++、Rust、PHP、R、Node.js等
2.基本用法 首先读者看一段简单的结巴分词代码,主要调用两个函数实现。
jieba.cut(text, cut_all=True) 分词函数,第一个参数是需要分词的字符串,第二个参数表示是否为全模式。分词返回的结果是一个可迭代的生成器(generator),可使用for循环来获取分词后的每个词语,更推荐读者转换为list列表再使用。
jieba.cut_for_search(text) 搜索引擎模式分词,参数为分词的字符串,该方法适合用于搜索引擎构造倒排索引的分词,粒度比较细。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import jieba text = "小杨毕业于北京理工大学,从事Python人工智能相关工作。" data = jieba.cut(text,cut_all=True )print (type (data))print (u"[全模式]: " , "/" .join(data)) data = jieba.cut(text,cut_all=False )print (u"[精确模式]: " , "/" .join(data)) data = jieba.cut(text) print (u"[默认模式]: " , "/" .join(data)) data = jieba.cut_for_search(text) print (u"[搜索引擎模式]: " , "/" .join(data)) seg_list = jieba.lcut(text, cut_all=False )print ("[返回列表]: {0}" .format (seg_list))
输出结果如下图所示:
3.获取疫情文本高频词 接着我们将新闻正文文本“C-class.txt”数据进行中文分词,每行代表一条新闻,并生成对应的内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import jiebaimport reimport timefrom collections import Counter cut_words = "" all_words = "" f = open ('C-class-fenci.txt' , 'w' , encoding='utf-8' )for line in open ('C-class.txt' , encoding='utf-8' ): line.strip('\n' ) seg_list = jieba.cut(line,cut_all=False ) cut_words = (" " .join(seg_list)) f.write(cut_words) all_words += cut_wordselse : f.close() all_words = all_words.split()print (all_words) c = Counter()for x in all_words: if len (x)>1 and x != '\r\n' : c[x] += 1 print ('\n词频统计结果:' )for (k,v) in c.most_common(10 ): print ("%s:%d" %(k,v)) name = time.strftime("%Y-%m-%d" ) + "-fc.csv" fw = open (name, 'w' , encoding='utf-8' ) i = 1 for (k,v) in c.most_common(len (c)): fw.write(str (i)+',' +str (k)+',' +str (v)+'\n' ) i = i + 1 else : print ("Over write file!" ) fw.close()
输出结果如下图所示,采用空格连接的分词结果。
同时生成高频特征词,并保存至CSV文件中。
对应的特征词及词频排序如表“2020-12-29-fc.csv”所示,如果我们撰写图情论文,可以尝试建立Top50的特征词表。
四.WordCloud可视化分析 1.基本用法 词云分析主要包括两种方法:
调用WordCloud扩展包画图(兼容性极强,之前介绍过)
调用PyEcharts中的WordCloud子包画图(本文推荐新方法)
这里使用PyEcharts可视化,需要通过它来绘制词云,基础代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 from pyecharts import options as optsfrom pyecharts.charts import WordCloudfrom pyecharts.globals import SymbolType words = [ ('背包问题' , 10000 ), ('大整数' , 6181 ), ('Karatsuba乘法算法' , 4386 ), ('穷举搜索' , 4055 ), ('傅里叶变换' , 2467 ), ('状态树遍历' , 2244 ), ('剪枝' , 1868 ), ('Gale-shapley' , 1484 ), ('最大匹配与匈牙利算法' , 1112 ), ('线索模型' , 865 ), ('关键路径算法' , 847 ), ('最小二乘法曲线拟合' , 582 ), ('二分逼近法' , 555 ), ('牛顿迭代法' , 550 ), ('Bresenham算法' , 462 ), ('粒子群优化' , 366 ), ('Dijkstra' , 360 ), ('A*算法' , 282 ), ('负极大极搜索算法' , 273 ), ('估值函数' , 265 ) ]def wordcloud_base () -> WordCloud: c = ( WordCloud() .add("" , words, word_size_range=[20 , 100 ], shape='diamond' ) .set_global_opts(title_opts=opts.TitleOpts(title='WordCloud词云' )) ) return c wordcloud_base().render('词云图.html' )
输出结果如下图所示,出现词频越高显示越大。
核心代码为:
add(name, attr, value, shape=“circle”, word_gap=20, word_size_range=None, rotate_step=45)
name -> str: 图例名称
attr -> list: 属性名称
value -> list: 属性所对应的值
shape -> list: 词云图轮廓,有’circle’, ‘cardioid’, ‘diamond’, ‘triangleforward’, ‘triangle’, ‘pentagon’, ‘star’可选
word_gap -> int: 单词间隔,默认为20
word_size_range -> list: 单词字体大小范围,默认为[12,60]
rotate_step -> int: 旋转单词角度,默认为45
2.疫情词云 接着我们显示经过中文分词的疫情新闻文本信息,前1000个高频词的词云绘制代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 import jiebaimport reimport timefrom collections import Counter cut_words = "" all_words = "" f = open ('C-class-fenci.txt' , 'w' , encoding='utf-8' )for line in open ('C-class.txt' , encoding='utf-8' ): line.strip('\n' ) seg_list = jieba.cut(line,cut_all=False ) cut_words = (" " .join(seg_list)) f.write(cut_words) all_words += cut_wordselse : f.close() all_words = all_words.split()print (all_words) c = Counter()for x in all_words: if len (x)>1 and x != '\r\n' : c[x] += 1 print ('\n词频统计结果:' )for (k,v) in c.most_common(10 ): print ("%s:%d" %(k,v)) name = time.strftime("%Y-%m-%d" ) + "-fc.csv" fw = open (name, 'w' , encoding='utf-8' ) i = 1 for (k,v) in c.most_common(len (c)): fw.write(str (i)+',' +str (k)+',' +str (v)+'\n' ) i = i + 1 else : print ("Over write file!" ) fw.close()from pyecharts import options as optsfrom pyecharts.charts import WordCloudfrom pyecharts.globals import SymbolType words = []for (k,v) in c.most_common(1000 ): words.append((k,v))def wordcloud_base () -> WordCloud: c = ( WordCloud() .add("" , words, word_size_range=[20 , 100 ], shape=SymbolType.ROUND_RECT) .set_global_opts(title_opts=opts.TitleOpts(title='全国新型冠状病毒疫情词云图' )) ) return c wordcloud_base().render('疫情词云图.html' )
运行结果如下图所示:
输出结果如下图所示:
五.TF-IDF计算及KMeans文本聚类 1.TF-IDF计算 TF-IDF(Term Frequency-InversDocument Frequency)是一种常用于信息处理和数据挖掘的加权技术。该技术采用一种统计方法,根据字词的在文本中出现的次数和在整个语料中出现的文档频率来计算一个字词在整个语料中的重要程度。它的优点是能过滤掉一些常见的却无关紧要本的词语,同时保留影响整个文本的重要字词。计算方法如下面公式所示:
$$ T F − I D F = T F ∗ I D F TF-IDF = TF* IDF TF−IDF=TF∗IDF $$
TF(Term Frequency)表示某个关键词在整篇文章中出现的频率。IDF(InversDocument Frequency)表示计算倒文本频率。文本频率是指某个关键词在整个语料所有文章中出现的次数。倒文档频率又称为逆文档频率,它是文档频率的倒数,主要用于降低所有文档中一些常见却对文档影响不大的词语的作用。
TF-IDF统计可视化完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 import osimport timeimport pandas as pdimport numpy as npimport jiebaimport jieba.analyseimport matplotlib.pyplot as pltfrom PIL import Imagefrom datetime import datetimefrom matplotlib.font_manager import FontProperties cut_words = "" for line in open ('C-class.txt' , encoding='utf-8' ): line.strip('\n' ) seg_list = jieba.cut(line,cut_all=False ) cut_words += (" " .join(seg_list)) keywords = jieba.analyse.extract_tags(cut_words, topK=50 , withWeight=True , allowPOS=('a' ,'e' ,'n' ,'nr' ,'ns' , 'v' )) print (keywords) pd.DataFrame(keywords, columns=['词语' ,'重要性' ]).to_excel('TF_IDF关键词前50.xlsx' ) ss = pd.DataFrame(keywords,columns = ['词语' ,'重要性' ]) plt.figure(figsize=(10 ,6 )) plt.title('TF-IDF Ranking' ) fig = plt.axes() plt.barh(range (len (ss.重要性[:25 ][::-1 ])),ss.重要性[:25 ][::-1 ]) fig.set_yticks(np.arange(len (ss.重要性[:25 ][::-1 ]))) font = FontProperties(fname=r'c:\windows\fonts\simsun.ttc' ) fig.set_yticklabels(ss.词语[:25 ][::-1 ],fontproperties=font) fig.set_xlabel('Importance' ) plt.show()
输出结果如下图所示,可以看到“疫情”、“组织”、“企业”、“复工”、“社会”、“新冠”、“慈善”等都是高频词,也是大众普遍关心的主题。
注意:可能需要安装openpyxl扩展包,to_excel()函数要用到。
2.文本聚类 同样,在Scikit-Learn包中也能计算TF-IDF权重值,此时需要用到两个类:CountVectorizer和TfidfTransformer。
(1) CountVectorizer
CountVectorizer类会将文本中的词语转换为词频矩阵,例如矩阵中包含一个元素a[i][j]
,它表示j词在i类文本下的词频。它通过fit_transform函数计算各个词语出现的次数,通过get_feature_names()可获取词袋中所有文本的关键字,通过toarray()可看到词频矩阵的结果。
(2) TfidfTransformer
TfidfTransformer用于统计vectorizer中每个词语的TF-IDF值。具体用法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 from sklearn.feature_extraction.text import CountVectorizer corpus = [ 'This is the first document.' , 'This is the second second document.' , 'And the third one.' , 'Is this the first document?' , ] vectorizer = CountVectorizer() X = vectorizer.fit_transform(corpus) word = vectorizer.get_feature_names()print wordprint X.toarray() from sklearn.feature_extraction.text import TfidfTransformer transformer = TfidfTransformer()print transformer tfidf = transformer.fit_transform(X)print tfidf.toarray()
输出结果入下所示,从结果中可以看到,总共包括9个特征词,即: [u’and’, u’document’, u’first’, u’is’, u’one’, u’second’, u’the’, u’third’, u’this’]
同时在输出每个句子中包含特征词的个数。例如,第一句“This is the first document.”,它对应的词频为[0, 1, 1, 1, 0, 0, 1, 0, 1],假设初始序号从1开始计数,则该词频表示存在第2个位置的单词“document”共1次、第3个位置的单词“first”共1次、第4个位置的单词“is”共1次、第9个位置的单词“this”共1词。所以,每个句子都会得到一个词频向量,TF-IDF对应向量类似。
(3) 文本聚类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 import time import re import os import sysimport codecsimport shutilimport numpy as npimport matplotlibimport scipyimport matplotlib.pyplot as pltfrom sklearn import feature_extraction from sklearn.feature_extraction.text import TfidfTransformer from sklearn.feature_extraction.text import CountVectorizerfrom sklearn.feature_extraction.text import HashingVectorizer if __name__ == "__main__" : corpus = [] for line in open ('C-class-fenci.txt' , 'r' , encoding='utf-8' ).readlines(): corpus.append(line.strip()) vectorizer = CountVectorizer() transformer = TfidfTransformer() tfidf = transformer.fit_transform(vectorizer.fit_transform(corpus)) word = vectorizer.get_feature_names() weight = tfidf.toarray() print ('Features length: ' + str (len (word))) """ # 输出单词 for j in range(len(word)): print(word[j] + ' ') # 打印每类文本的tf-idf词语权重 第一个for遍历所有文本 第二个for便利某一类文本下的词语权重 for i in range(len(weight)): print u"-------这里输出第", i, u"类文本的词语tf-idf权重------" for j in range(len(word)): print weight[i][j], """ print ('Start Kmeans:' ) from sklearn.cluster import KMeans clf = KMeans(n_clusters=2 ) print (clf) pre = clf.fit_predict(weight) print (pre) print (clf.cluster_centers_) print (clf.inertia_) from sklearn.decomposition import PCA pca = PCA(n_components=2 ) newData = pca.fit_transform(weight) print (newData) x = [n[0 ] for n in newData] y = [n[1 ] for n in newData] plt.scatter(x, y, c=pre, s=100 ) plt.legend() plt.title("Cluster with Text Mining" ) plt.show()
输出结果如下图所示。需要注意,简单的聚类我们无法进行深入的分析,你可以理解为积极主题的一类(黄色)、消极主题的一类(黑色),也可以有其他理解,需要结合具体数据集进行分析,但其解释性始终不是很好。而真实的数据分析中会引入类标或标注,所以接着我们引入主题关键词聚类和LDA主题模型的分析,更能帮助大家理解文本挖掘和主题分析。
六.主题词层次聚类分析 1.层次聚类 层次聚类算法又称为树聚类算法,它根据数据之间的距离,透过一种层次架构方式,反复将数据进行聚合,创建一个层次以分解给定的数据集。主题词层次聚类主要调用scipy.cluster.hierarchy实现,推荐文章:层次聚类。
scipy.cluster.hierarchy.linkage(y, method=‘single’, metric=‘euclidean’, optimal_ordering=False)
层次聚类编码为一个linkage矩阵。假设代码如下,Z共有四列组成,第一字段与第二字段分别为聚类簇的编号,在初始距离前每个初始值被从0~n-1进行标识,每生成一个新的聚类簇就在此基础上增加一对新的聚类簇进行标识,第三个字段表示前两个聚类簇之间的距离,第四个字段表示新生成聚类簇所包含的元素的个数。
1 2 3 4 5 6 7 8 9 from scipy.cluster.hierarchy import dendrogram, linkage,fclusterfrom matplotlib import pyplot as plt X = [[i] for i in [2 , 8 , 0 , 4 , 1 , 9 , 9 , 0 ]]print (X) Z = linkage(X, 'ward' ) f = fcluster(Z, 4 , 'distance' ) fig = plt.figure(figsize=(5 , 3 )) dn = dendrogram(Z) plt.show()
下面是聚类结果的可视化聚类树:
下面是返回值的解析:
2.疫情分析 由于层次聚类绘制的树状图主题词太多,所以这里采用中文分词提取每条新闻(对应一行数据)的Top100特征词,再存储至TXT中进行层次聚类分析。完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 import osimport matplotlib.pyplot as pltimport numpy as npimport pandas as pdimport jiebaimport matplotlib.pyplot as pltfrom pylab import mplfrom collections import Counterfrom sklearn.metrics.pairwise import cosine_similarityfrom scipy.cluster.hierarchy import ward, dendrogramfrom sklearn.feature_extraction.text import CountVectorizer, TfidfTransformer mpl.rcParams['font.sans-serif' ] = ['SimHei' ] cut_words = "" all_words = "" for line in open ('C-class.txt' , encoding='utf-8' ): line.strip('\n' ) seg_list = jieba.cut(line,cut_all=False ) cut_words = (" " .join(seg_list)) all_words += cut_words all_words = all_words.split()print (all_words) c = Counter()for x in all_words: if len (x)>1 and x != '\r\n' : c[x] += 1 top_word = []print ('\n词频统计结果:' )for (k,v) in c.most_common(100 ): print ("%s:%d" %(k,v)) top_word.append(k)print (top_word) cut_words = "" f = open ('C-key.txt' , 'w' )for line in open ('C-class.txt' , encoding='utf-8' ): line.strip('\n' ) seg_list = jieba.cut(line,cut_all=False ) final = "" for seg in seg_list: if seg in top_word: final += seg + "|" cut_words += final f.write(final+"\n" )print (cut_words) f.close text = open ('C-key.txt' ).read() list1 = text.split("\n" )print (list1) mytext_list = list1 count_vec = CountVectorizer(min_df=3 ) xx1 = count_vec.fit_transform(list1).toarray() word = count_vec.get_feature_names() print ("word feature length: {}" .format (len (word)))print (word)print (xx1.shape)print (xx1[0 ]) titles = word df = pd.DataFrame(xx1)print (df.corr())print (df.corr('spearman' ))print (df.corr('kendall' )) dist = df.corr()print (dist)print (type (dist))print (dist.shape) linkage_matrix = ward(dist) fig, ax = plt.subplots(figsize=(15 , 20 )) ax = dendrogram(linkage_matrix, orientation="right" , labels=titles); plt.tight_layout() plt.savefig('Tree_word.png' , dpi=200 )
最终生成图像如下所示:
运行结果如下图所示:
注意:该方法更推荐大家在进行论文关键词共现分析、主题词聚类分析等领域。
七.LDA主题分布分析 1.LDA主题模型 文档主题生成模型(Latent Dirichlet Allocation,简称LDA)通常由包含词、主题和文档三层结构组成。LDA模型属于无监督学习技术,它是将一篇文档的每个词都以一定概率分布在某个主题上,并从这个主题中选择某个词语。文档到主题的过程是服从多项分布的,主题到词的过程也是服从多项分布的。
文档主题生成模型(Latent Dirichlet Allocation,简称LDA)又称为盘子表示法(Plate Notation),下图是模型的标示图,其中双圆圈表示可测变量,单圆圈表示潜在变量,箭头表示两个变量之间的依赖关系,矩形框表示重复抽样,对应的重复次数在矩形框的右下角显示。LDA模型的具体实现步骤如下:
从每篇网页D对应的多项分布θ中抽取每个单词对应的一个主题z。
从主题z对应的多项分布φ中抽取一个单词w。
重复步骤1和2,共计Nd次,直至遍历网页中每一个单词。
可以从gensim中下载ldamodel扩展包安装,也可以使用Sklearn机器学习包的LDA子扩展包,亦可从github中下载开源的LDA工具。下载地址详见列表所示。
本文和之前介绍的LDA算法略有不同,它主要采用sklearn中的LatentDirichletAllocation包实现主题分布研究,并调用pyLDAvis绘制相关图形。安装过程如下所示:
2.完整代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 import pandas as pdfrom sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer corpus = []for line in open ('C-class-fenci.txt' , 'r' , encoding='utf-8' ).readlines(): corpus.append(line.strip()) n_features = 2000 tf_vectorizer = TfidfVectorizer(strip_accents = 'unicode' , max_features=n_features, stop_words=['的' ,'或' ,'等' ,'是' ,'有' ,'之' ,'与' ,'可以' ,'还是' ,'比较' ,'这里' , '一个' ,'和' ,'也' ,'被' ,'吗' ,'于' ,'中' ,'最' ,'但是' ,'图片' ,'大家' , '一下' ,'几天' ,'200' ,'还有' ,'一看' ,'300' ,'50' ,'哈哈哈哈' , '“' ,'”' ,'。' ,',' ,'?' ,'、' ,';' ,'怎么' ,'本来' ,'发现' , 'and' ,'in' ,'of' ,'the' ,'我们' ,'一直' ,'真的' ,'18' ,'一次' , '了' ,'有些' ,'已经' ,'不是' ,'这么' ,'一一' ,'一天' ,'这个' ,'这种' , '一种' ,'位于' ,'之一' ,'天空' ,'没有' ,'很多' ,'有点' ,'什么' ,'五个' , '特别' ], max_df = 0.99 , min_df = 0.002 ) tf = tf_vectorizer.fit_transform(corpus)print (tf.shape)print (tf)from sklearn.decomposition import LatentDirichletAllocation n_topics = 2 lda = LatentDirichletAllocation(n_components=n_topics, max_iter=100 , learning_method='online' , learning_offset=50 , random_state=0 ) lda.fit(tf)print (lda.components_)print (lda.components_.shape) print (u'困惑度:' )print (lda.perplexity(tf,sub_sampling = False ))def print_top_words (model, tf_feature_names, n_top_words ): for topic_idx,topic in enumerate (model.components_): print ('Topic #%d:' % topic_idx) print (' ' .join([tf_feature_names[i] for i in topic.argsort()[:-n_top_words-1 :-1 ]])) print ("" ) n_top_words = 20 tf_feature_names = tf_vectorizer.get_feature_names() print_top_words(lda, tf_feature_names, n_top_words)import pyLDAvisimport pyLDAvis.sklearn data = pyLDAvis.sklearn.prepare(lda,tf,tf_vectorizer)print (data) pyLDAvis.show(data)
困惑度及各个主题下的关键词通过for循环显示如下,Topic1是疫情相关的主题词,Topic0是其他相关的主题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 D:\桌面\新建文件夹>python blog03_08_lda.py (138 , 2000 ) (0 , 403 ) 1.0 (1 , 914 ) 0.03179002916835669 (1 , 916 ) 0.0234264095629235 (1 , 965 ) 0.03491201537733099 (1 , 873 ) 0.02309606736903826 (1 , 1475 ) 0.032712252841322786 (1 , 959 ) 0.030194163252955503 (1 , 158 ) 0.027145424577929348 (1 , 1473 ) 0.024499551271543872 (1 , 1144 ) 0.058987094186498806 (1 , 436 ) 0.023769709611473348 (1 , 273 ) 0.030194163252955503 (1 , 1662 ) 0.027145424577929348 (1 , 912 ) 0.02160893613426232 (1 , 1612 ) 0.03491201537733099 (1 , 1942 ) 0.03491201537733099 (1 , 331 ) 0.027145424577929348 (1 , 712 ) 0.02884487784700509 (1 , 696 ) 0.03374321119491412 (1 , 1375 ) 0.03491201537733099 (1 , 431 ) 0.02247058496874336 (1 , 1295 ) 0.03491201537733099 (1 , 210 ) 0.017702572081147446 (1 , 979 ) 0.027145424577929348 (1 , 1323 ) 0.03491201537733099 : : (137 , 233 ) 0.032457418878176596 (137 , 282 ) 0.02615992004194761 (137 , 70 ) 0.052619347740271036 (137 , 21 ) 0.04204615129757714 (137 , 559 ) 0.05879281708622848 (137 , 1065 ) 0.030850260311284926 (137 , 1910 ) 0.02615992004194761 (137 , 5 ) 0.0376578782866175 (137 , 1098 ) 0.02154895843261173 (137 , 1847 ) 0.022401353249301426 (137 , 575 ) 0.023491381002374374 (137 , 1866 ) 0.03969475253942053 (137 , 1896 ) 0.07915772355142488 (137 , 919 ) 0.023872748317938564 (137 , 1500 ) 0.07728468461982171 (137 , 753 ) 0.043373413973474426 (137 , 235 ) 0.036290943551754246 (137 , 1043 ) 0.02885156855169558 (137 , 1576 ) 0.08423794621227862 (137 , 240 ) 0.09461262585467081 (137 , 1908 ) 0.12539855047313608 (137 , 1435 ) 0.1301998889606912 (137 , 854 ) 0.08359903364875737 (137 , 1484 ) 0.09174653065996949 (137 , 1044 ) 0.02885156855169558 [[1.47998182 1.1938479 0.93516535 ... 0.64108976 0.91951438 0.71990474 ] [0.50918993 0.56496607 0.50559418 ... 0.50385102 0.50605441 0.50574252 ]] (2 , 2000 ) 困惑度:4095.2085280249858 Topic 企业 疫情 防控 服务 复工 协会 工作 社会 复产 组织 会员 心理 抗疫 及时 提供 饲料 社区 积极 就业 行业协会 Topic 内容 美国 饭店 球队 赛区 酒店 客源 希腊 雅典 实验室 中超联赛 转会 广州 开幕式 猛犸 民众 日期 赛季 大连 苏州 PreparedData(topic_coordinates= x y topics cluster Freq topic0 0.024078 0.0 1 1 89.66411 1 -0.024078 0.0 2 1 10.33589 , topic_info= Term Freq Total Category logprob loglift403 内容 0.000000 0.000000 Default 30.0000 30.0000 1604 美国 0.000000 0.000000 Default 29.0000 29.0000 1975 饭店 0.000000 0.000000 Default 28.0000 28.0000 1865 酒店 0.000000 0.000000 Default 27.0000 27.0000 1404 球队 0.000000 0.000000 Default 26.0000 26.0000 ... ... ... ... ... ... ...1403 球员 0.093992 0.367312 Topic2 -7.2237 0.9065 886 平等 0.089363 0.349664 Topic2 -7.2742 0.9053 895 广州 0.104099 0.468188 Topic2 -7.1215 0.7660 1529 窗口 0.096892 0.421790 Topic2 -7.1933 0.7986 1304 民众 0.099578 0.576540 Topic2 -7.1659 0.5134 [94 rows x 6 columns], token_table= Topic Freq Term term134 1 0.810396 中国241 1 1.023200 企业255 1 1.017123 会员324 1 0.898946 做好528 1 1.054671 协会565 1 1.055105 及时715 1 1.000085 复产717 1 0.927915 复工736 1 0.886561 大学生800 1 0.942634 宣传833 1 1.133821 就业854 1 0.886084 工作919 1 0.872452 开展954 1 0.917835 心理1044 1 1.030462 抗疫1098 1 1.068630 提供1161 1 0.929940 新冠1229 1 1.078302 服务1383 1 0.851659 物业1386 1 0.894247 物资1435 1 0.981779 疫情1484 1 0.977973 社会1491 1 1.099239 社区1512 1 1.128643 积极1576 1 1.006787 组织1637 1 0.923467 肺炎1685 1 0.825705 行业1686 1 0.797830 行业协会1780 1 1.125439 赛事1908 1 1.065603 防控1910 1 0.941367 防疫1976 1 1.075012 饲料, R=30 , lambda_step=0.01 , plot_opts={'xlab' : 'PC1' , 'ylab' : 'PC2' }, topic_order=[1 , 2 ]) Serving to http://127.0 .0 .1 :8888 / [Ctrl-C to exit]127.0 .0 .1 - - [29 /Dec/2020 22 :23 :43 ] "GET / HTTP/1.1" 200 -127.0 .0 .1 - - [29 /Dec/2020 22 :23 :44 ] "GET /LDAvis.css HTTP/1.1" 200 -127.0 .0 .1 - - [29 /Dec/2020 22 :23 :44 ] "GET /d3.js HTTP/1.1" 200 -127.0 .0 .1 - - [29 /Dec/2020 22 :23 :44 ] "GET /LDAvis.js HTTP/1.1" 200 -127.0 .0 .1 - - [29 /Dec/2020 22 :23 :44 ] code 404 , message Not Found127.0 .0 .1 - - [29 /Dec/2020 22 :23 :44 ] "GET /favicon.ico HTTP/1.1" 404 -
生成对应的图形浏览器会打开,如下图所示:
注意:LDA主题分布分析需要设置不同的主题值,这里是2,也可以是3、4、5等等。那么如何确定最佳主题数呢?困惑数又有什么用呢?如果存在语义知识又怎么处理呢?主题如何能更加准确定位呢?读者可以带着这些思考去探索。加油~
八.总结
实时数据爬取
中文文本分词及高频词提取
词云可视化分析
TF-IDF权重计算和文本聚类分析
层次聚类分析
LDA主题模型分布
参考文献: