该系列文章是Python大数据分析系列博客,包括网络爬虫、可视化分析、GIS地图显示、情感分析、舆情分析、主题挖掘、威胁情报溯源、知识图谱、预测预警及AI和NLP应用等。希望该系列文章对您有所帮助。
前文分享了采用SnowNLP对微博话题进行简单的情感分析及文本挖掘。这篇文章将讲解Python抓取人民网新闻,并利用Gephi构建主题知识图谱,发现各主题关键词的关联。希望这篇基础性文章对您有所帮助,也非常感谢参考文献中老师的分享!如果您有想学习的知识或建议,可以给作者留言~
本文的重点不是数据抓取,请读者结合自己的需求和数据进行分析,也可以直接使用作者的数据集,希望给大家知识图谱提供一点思路。就作者而言,知识图谱包括两种类型,一种是Google于2012年提出的Knowledge Graph(下一代搜索引擎),另一种是图书情报和文献分析的知识图谱,这里其实介于两者之间,也建议读者结合自己领域理解。
代码下载地址:https://github.com/eastmountyxz/Wuhan-data-analysis
CSDN下载地址:https://download.csdn.net/download/Eastmount/12323815
同时推荐前面作者另外五个Python系列文章。从2014年开始,作者主要写了三个Python系列文章,分别是基础知识、网络爬虫和数据分析。2018年陆续增加了Python图像识别和Python人工智能专栏。
前文阅读:
[Pyhon大数据分析] 一.腾讯实时数据爬取、Matplotlib和Seaborn可视化分析全国各地区、某省各城市、新增趋势
[Pyhon大数据分析] 二.PyEcharts绘制全国各地区、某省各城市地图及可视化分析
[Pyhon大数据分析] 三.新闻信息抓取及词云可视化、文本聚类和LDA主题模型文本挖掘
[Pyhon大数据分析] 四.微博话题抓取及新冠肺炎文本挖掘和情感分析
本文抓取数据为人民网某专栏的新闻数据。数据包括重要新闻、人民评论、实况武汉、各地动态、八方支援等专栏,通过抓取新闻数据进行主题关键词分析和知识图谱构建。下面以YQ快速为例,先进行网页分析,再抓取相关的新闻数据。
http://society.people.com.cn/GB/369130/431577/431608/index.html
本文采用Selenium和BeautifulSoup进行抓取,定位网页节点关键代码如下:
获取标题、时间、链接之后,我们通过访问链接获取正文信息,核心代码如下:
完整代码:
# -*- coding: utf-8 -*-
import os
import re
import csv
import time
import json
import random
import urllib.request
from lxml import etree
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
#-------------------------------------------------写入文件-------------------------------------------------
path = os.getcwd() + "/yqkx_data.csv"
csvfile = open(path, 'a', newline='', encoding = 'utf-8-sig')
writer = csv.writer(csvfile)
writer.writerow(('序号','文章标题','发布时间','文章链接','文章内容'))
#--------------------------------------------YQ快讯-数据抓取---------------------------------------------
url = "http://society.people.com.cn/GB/369130/431577/431608/index.html"
driver = webdriver.Chrome() #chromedriver.exe置于python37根目录
driver.implicitly_wait(5)
chrome_option = webdriver.ChromeOptions()
driver.get(url) #打开网页网页
driver.implicitly_wait(6) #等待加载六秒
#-------------------------------------------------获取标题-------------------------------------------------
titles = driver.find_elements_by_xpath('//div[@class=" p2j_list_lt fl"]/ul/li')
for t in titles:
print(t.text)
links = driver.find_elements_by_xpath('//div[@class=" p2j_list_lt fl"]/ul/li/a')
for link in links:
print(link.get_attribute('href'))
print("\n\n==========================================================")
#-------------------------------------------------获取正文-------------------------------------------------
def get_content(url):
print(url)
try:
content = urllib.request.urlopen(url).read()
soup = BeautifulSoup(content,"html.parser")
#来源
ly = soup.find(attrs={"class":"fl"}).get_text()
#print(ly)
#正文
zw = soup.find(attrs={"class":"box_con"})
#防止某些文章仅图片
if zw is not None:
zw = zw.get_text()
zw = zw.replace("\n", "")
else:
zw = ""
print(zw)
print("succeed")
return ly,zw
except Exception as e:
zw = e.partial
ly = ""
print("except")
print(zw)
return ly, page
#-------------------------------------------------写入文件-------------------------------------------------
k = 0
while k<len(titles):
#序号
num = str(k+1)
#文章标题和发布时间
value = titles[k].text.split('\n')
con_title = value[0]
con_time = value[1]
#文件链接
url = links[k].get_attribute('href')
#获取来源和正文
ly,zw = get_content(url)
content = (num, con_title, con_time, url, zw)
#文件写入操作
writer.writerow((content))
k = k + 1
#文件关闭
csvfile.close()
数据抓取如下图所示:
存储至CSV文件如下图所示:
作者抓取了多个专栏,汇总相关数据如下图所示(可从github下载)。由于论文原因,作者仅公开每个专栏的50篇新闻,包括:八方支援、各地动态、抗疫英雄、权威解读、人民网评、实况武汉、一线守护等。
数据如下图所示:
作者提取新闻正文内容或新闻标题进行词频分析,存储为“all-data.txt”。
接下来的代码是中文分词,并提取每条新闻排名前50的关键词,作为后续的共现分析和知识图谱构建。
# coding=utf-8
import jieba
import re
import time
from collections import Counter
from snownlp import SnowNLP
#------------------------------------中文分词------------------------------------
cut_words = ""
all_words = ""
f = open('all-data-key.txt', 'w', encoding='utf-8')
for line in open('all-data.txt', encoding='utf-8'):
line = line.strip('\n')
#停用词过滤
line = re.sub('[0-9’!"#$%&\'()*+,-./:;<=>?@,。?★、…【】《》?“”‘’![\\]^_`{|}~\s]+', "", line)
seg_list = jieba.cut(line, cut_all=False)
cut_words = (" ".join(seg_list))
#计算关键词
all_words = cut_words.split()
c = Counter()
for x in all_words:
if len(x)>1 and x != '\r\n':
c[x] += 1
#Top50
output = ""
#print('\n词频统计结果:')
for (k,v) in c.most_common(50):
#print("%s:%d"%(k,v))
output += k + " "
f.write(output+"\n")
else:
f.close()
运行结构如下图所示,每一行表示一条新闻的Top50关键词,接下来需要计算它们的共现矩阵。
引文分析常见的包括两类,一种是共现关系,另一种是引用和被引用关系。本文主要讲解共现关系,假设现在存在三篇文章,如下所示:
文章标题 作者
大数据发展现状分析 A,B,C
Python网络爬虫 A,D,C
贵州省大数据战略 D,B
(1) 首先写代码抓取该领域文章的所有作者或关键词,即:A、B、C、D。
(2) 接着获取对应的共现矩阵,比如文章“大数据发展现状分析”,则认为A、B、C共现,在他们之间建立一条边。共现矩阵如下所示:
[ − A B C D A 0 1 2 1 B 1 0 1 1 C 2 1 0 1 D 1 1 1 0 ] (1) \left[ \begin{matrix} -& A & B & C & D \\ A & 0 & 1 & 2 & 1 \\ B & 1 & 0 & 1 & 1 \\ C & 2 & 1 & 0 & 1 \\ D & 1 & 1 & 1 & 0 \end{matrix} \right] \tag{1} ⎣⎢⎢⎢⎢⎡−ABCDA0121B1011C2101D1110⎦⎥⎥⎥⎥⎤(1)
(3) 通过共现矩阵分别获取两两关系及权重,再写入CSV或Excel文件中,如下所示。
[ S o u r c e T a r g e t W e i g h t A B 1 A C 2 A D 1 B C 1 B D 1 C D 1 ] (2) \left[ \begin{matrix} Source & Target & Weight \\ A & B & 1 \\ A & C & 2 \\ A & D & 1 \\ B & C & 1 \\ B & D & 1\\ C & D & 1 \end{matrix} \right] \tag{2} ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡SourceAAABBCTargetBCDCDDWeight121111⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤(2)
(4) 将该CSV文件导入Gephi中,并绘制相关的图形。因为该实例节点比较少,下面是Pyhton调用Networkx绘制的代码及图形。
# -*- coding: utf-8 -*-
import networkx as nx
import matplotlib.pyplot as plt
#定义有向图
DG = nx.Graph()
#添加五个节点(列表)
DG.add_nodes_from(['A', 'B', 'C', 'D'])
print(DG.nodes())
#添加边(列表)
DG.add_edge('A', 'B', weight=1)
DG.add_edge('A', 'C', weight=2)
DG.add_edge('A', 'D', weight=1)
DG.add_edge('B', 'C', weight=1)
DG.add_edge('B', 'D', weight=1)
DG.add_edge('C', 'D', weight=1)
#DG.add_edges_from([('A', 'B'), ('A', 'C'), ('A', 'D'), ('B','C'),('B','D'),('C','D')])
print(DG.edges())
#绘制图形 设置节点名显示\节点大小\节点颜色
colors = ['red', 'green', 'blue', 'yellow']
nx.draw(DG,with_labels=True, node_size=900, node_color = colors)
plt.show()
绘制图形如下所示:
大家也可以采用PyEcharts绘制网络图、Neo4j图数据库绘制关系图。由于我们的数据集比较多,故推荐大家使用Gephi软件绘制相关图形,而且图形比较美观。
下面的代码是获取每条新闻主题关键词的共现矩阵,代码如下:
# -*- coding: utf-8 -*-
"""
@author: eastmount CSDN 2020-04-12
"""
import pandas as pd
import numpy as np
import codecs
import networkx as nx
import matplotlib.pyplot as plt
import csv
from scipy.sparse import coo_matrix
#---------------------------第一步:读取数据-------------------------------
word = [] #记录关键词
f = open("all-data-key.txt", encoding='utf-8')
line = f.readline()
while line:
#print line
line = line.replace("\n", "") #过滤换行
line = line.strip('\n')
for n in line.split(' '):
#print n
if n not in word:
word.append(n)
line = f.readline()
f.close()
print(len(word)) #关键词总数
#--------------------------第二步 计算共现矩阵----------------------------
a = np.zeros([2,3])
print(a)
#共现矩阵
#word_vector = np.zeros([len(word),len(word)], dtype='float16')
#MemoryError:矩阵过大汇报内存错误
#采用coo_matrix函数解决该问题
print(len(word))
#类型
word_vector = coo_matrix((len(word),len(word)), dtype=np.int8).toarray()
print(word_vector.shape)
f = open("all-data-key.txt", encoding='utf-8')
line = f.readline()
while line:
line = line.replace("\n", "") #过滤换行
line = line.strip('\n') #过滤换行
nums = line.split(' ')
#循环遍历关键词所在位置 设置word_vector计数
i = 0
j = 0
while i<len(nums): #ABCD共现 AB AC AD BC BD CD加1
j = i + 1
w1 = nums[i] #第一个单词
while j<len(nums):
w2 = nums[j] #第二个单词
#从word数组中找到单词对应的下标
k = 0
n1 = 0
while k<len(word):
if w1==word[k]:
n1 = k
break
k = k +1
#寻找第二个关键字位置
k = 0
n2 = 0
while k<len(word):
if w2==word[k]:
n2 = k
break
k = k +1
#重点: 词频矩阵赋值 只计算上三角
if n1<=n2:
word_vector[n1][n2] = word_vector[n1][n2] + 1
else:
word_vector[n2][n1] = word_vector[n2][n1] + 1
#print n1, n2, w1, w2
j = j + 1
i = i + 1
#读取新内容
line = f.readline()
f.close()
#--------------------------第三步 TXT文件写入--------------------------
res = open("word_word_weight.txt", "a+", encoding='utf-8')
i = 0
while i<len(word):
w1 = word[i]
j = 0
while j<len(word):
w2 = word[j]
#判断两个词是否共现 共现&词频不为0的写入文件
if word_vector[i][j]>0:
#print w1 +" " + w2 + " "+ str(int(word_vector[i][j]))
res.write(w1 +" " + w2 + " "+ str(int(word_vector[i][j])) + "\n")
j = j + 1
i = i + 1
res.close()
#--------------------------第四步 CSV文件写入--------------------------
c = open("word-word-weight.csv","w", encoding='utf-8', newline='') #解决空行
#c.write(codecs.BOM_UTF8) #防止乱码
writer = csv.writer(c) #写入对象
writer.writerow(['Word1', 'Word2', 'Weight'])
i = 0
while i<len(word):
w1 = word[i]
j = 0
while j<len(word):
w2 = word[j]
#判断两个词是否共现 共现词频不为0的写入文件
if word_vector[i][j]>0:
#写入文件
templist = []
templist.append(w1)
templist.append(w2)
templist.append(str(int(word_vector[i][j])))
#print templist
writer.writerow(templist)
j = j + 1
i = i + 1
c.close()
输出结果如下所示:
5267
[[0. 0. 0.]
[0. 0. 0.]]
5267
(5267, 5267)
生成如下图所示共现关系,主题词word1和word2共现weight词,共计33,0341条关系。
由于主题关系较多会影响绘制的知识图谱,而我们的分析目的是提取核心关键词的关联关系。因此接下来需要进行数据预处理,将关系权重(Weight)小于等于9的都过滤。最终得到“word-word-weight-gl9.csv”文件,共计3177条关系。
实体构建:324个主题关键词或实体
构建实体表“entity.csv”,包括id编号和label类标名称,共324个主题关键词(实体),如下图所示。这里采用的方法是将“word-word-weight-gl9.csv”文件的Word1和Word2关键词复制至一列并删除重复项,而真实的知识图谱需要通过命令实体识别来提取,如果想知道这部分内容,后续我也可以尝试分享。
关系构建:3177条关系
构建关系表“relationship.csv”,包括Source起始实体、Target目标实体、Type类型(无方向)和Weight权重,共3177条三元关系<实体,关系,实体>,如下图所示。
第一步,新建工程,并选择“数据资料”,输入电子表格。
第二步,导入节点表格,选择entity实体表。
第三步,导入数据如下图所示,设置为“边表格”,注意CSV表格数据一定设置为 Source(起始点)、Target(目标点)、Weight(权重),这个必须和Gephi格式一致,否则导入数据会提示错误。
导入数据如下图所示,它们是无向图。
第四步,导入成功后点击“概览”显示如下所示,接着就是调整参数。
设置外观如下图所示,主要包括颜色、大小、标签颜色、标签尺寸。
第一步,设置节点大小和颜色。
选中颜色,点击“数值设定”,选择渲染方式为“度”。
显示结果如下所示:
接着设置节点大小,数值设定为“度”,最小尺寸为20,最大尺寸为120。
第二步,设置模块化。
在右边统计中点击“运行”,设置模块性。
第三步,设置平均路径长度。
在右边统计中点击“运行”,设置边概述。
第四步,重新设置节点属性。
节点大小数值设定为“度”,最小值还是20,最大值还是120。节点颜色数值设定为“Modularity Class”,表示模块化。
第五步,设置边大小和颜色。
设置边颜色的数值设定为“边的权重”,如下图所示。
设置边的大小数值设定为“边的权重”。
第六步,在布局中选择“Fruchterman Reingold”。
调整区、重力和速度。
显示结果如下所示:
第七步,点击预览。
设置宋体字,显示标签,透明度调整为20,如下图所示。
最终结构如下图所示。
导出PNG、PDF、SVG如下图所示。
第一步,图谱调整。
选择喜欢的颜色。
边的厚度调整。
最终显示结果如下图所示:
第二步,权重过滤。
我们可以使用滤波功能过滤一些不重要的点,让重要的点清晰可见。滤波可以对节点做过滤,也可以对边做过滤。节点过滤:在属性中,可以做到节点的过滤,可以通过节点的lable、weight做等于、范围值、非空等过滤。如下图,就是通过节点的weight进行了范围的过滤,过滤掉了低weight值的节点,让这个图形不重要的节点变少。
在右上有一个“上下文”,展示的节点量、边量,做完过滤之后,这块会展示过滤后的量和占比。
做完了过滤之后,仍然可以继续执行布局的自动运行,直到节点的排列符合你的目的。
从下图可以看到一个大的社群,由三个小社群组成(粉色圈、黑色圈和部分灰色圈),他们之间通过两个uid彼此链接在了一起。将一个粉圈的头目和一个黑圈头目,看似一点也不相关的账户给联系在了一起。是不是就达到了通过一个用户查找到群体的目的呀~
最后删除掉无关联的节点,可以移动到一起区域删除,最终得到如下图所示的知识图谱。不知道是否有更好的方法?
最终得到如下图所示的知识图谱。
我们也可以设置其他布局,如下图所示:
最终如下图所示,如果增加停用词过滤效果更好些。
写到这里,第五篇YQ分析的文章就讲解完毕,希望对您有所帮助,尤其是想写文本挖掘论文的读者。后续还会分享舆情分析、威胁情报溯源、预测预警及AI和NLP应用等。如果文章对您有所帮助,将是我写作的最大动力。作者将源代码上传至github,大家可以直接下载。你们的支持就是我撰写的最大动力,加油~
同时,向钟院士致敬,向一线工作者致敬。侠之大者,为国为民。咱们中国人一生的最高追求,为天地立心,为生民立命,为往圣继绝学,为万世开太平。以一人之力系万民康乐,以一身犯险保大业安全。他们真是做到了,武汉加油,中国加油!
(By:Eastmount 2020-04-12 周末下午5点于贵阳 http://blog.csdn.net/eastmount/)
参考文献:
[1] [关系图谱] 一.Gephi通过共现矩阵构建知网作者关系图谱
[2] [关系图谱] 二.Gephi导入共线矩阵构建作者关系图谱
[3] [Python知识图谱] 一.哈工大pyltp安装及中文分句、中文分词、导入词典基本用法
[4] [Python知识图谱] 二.哈工大pyltp词性标注、命名实体识别、依存句法分析和语义角色标注
[5] [Python知识图谱] 三.Jieba工具中文分词、添加自定义词典及词性标注详解
[6] [Python知识图谱] 四.Python和Gephi实现中国知网合作关系知识图谱
[7] 知识图谱相关会议之观后感分享与学习总结
[8] 中文知识图谱研讨会的学习总结 (上) 图谱引入、百度知心、搜狗知立方
[9] 搜索引擎和知识图谱那些事 (上).基础篇
[10] Gephi的使用–以社交网络图为例 - Yan Li
[11] Gephi简易学习[六]———— 拓展分析红楼梦数据