数据就不贴了,给个描述吧
# <class 'pandas.core.frame.DataFrame'>
# RangeIndex: 4492 entries, 0 to 4491
# Data columns (total 7 columns):
# # Column Non-Null Count Dtype
# --- ------ -------------- -----
# 0 标题 4417 non-null object
# 1 标题链接 4492 non-null object
# 2 brief 4483 non-null object
# 3 keywords 3272 non-null object
# 4 发布时间 3817 non-null object
# 5 新闻类别 4492 non-null object
# 6 采集时的时间 4492 non-null object
# dtypes: object(7)
# memory usage: 245.8+ KB
大概就是这7列,还需要预处理一下
先对发布时间处理,好奇这是什么时段的新闻
dataI0['发布时间'].sort_values()
# Out[7]:
# 2988 2020-10-08
# 2987 2020-10-09
# 2986 2020-10-09
# 2985 2020-10-10
# 2984 2020-10-10
# ...
# 4356 NaN
# 4357 NaN
# 4358 NaN
# 4359 NaN
# 4360 NaN
# Name: 发布时间, Length: 4492, dtype: object
啊这…有缺失值来着,忘记处理了
dataI0['发布时间'].dropna().sort_values()
# Out[8]:
# 2988 2020-10-08
# 2987 2020-10-09
# 2986 2020-10-09
# 2985 2020-10-10
# 2984 2020-10-10
# ...
# 2374 21-4-23
# 2373 21-4-23
# 2371 21-4-23
# 2369 21-4-23
# 2372 21-4-23
# Name: 发布时间, Length: 3817, dtype: object
噢,数据格式还不统一。由于上面是排序后的结果,所以我们可以直接找到所有不合格式的数据,它们排序后都在一块儿
dataI0.loc[2369:2393,'发布时间']
# Out[10]:
# 2369 21-4-23
# 2370 21-4-23
# 2371 21-4-23
# 2372 21-4-23
# 2373 21-4-23
# 2374 21-4-23
# 2375 21-4-23
# 2376 21-4-23
# 2377 21-4-22
# 2378 21-4-22
# 2379 21-4-22
# 2380 21-4-22
# 2381 21-4-22
# 2382 21-4-22
# 2383 21-4-22
# 2384 21-4-22
# 2385 21-4-22
# 2386 21-4-22
# 2387 21-4-22
# 2388 21-4-22
# 2389 21-4-21
# 2390 21-4-21
# 2391 21-4-21
# 2392 21-4-21
# 2393 21-4-21
# Name: 发布时间, dtype: object
噢,这个需要找找资料1
tmpDF = dataI0.loc[2369:2393,'发布时间']
# 取出时间格式不同的这几个样本
tmpDF = pd.to_datetime(tmpDF,format="%y-%m-%d")
# 按照'两位数年份-月份-天数'的格式进行转换读入时间类型
dataI0.loc[2369:2393,'发布时间'] = tmpDF.dt.strftime("%Y-%m-%d")
# 按照'四位数年份-月份-天数'的格式从时间类型当中输出
dataI0['发布时间'].dropna().sort_values()
# 去除缺失值之后对数据进行排序
# 可以看到新闻的发布时间为2020-10-08到2021-04-24
# Out[47]:
# 2988 2020-10-08
# 2987 2020-10-09
# 2986 2020-10-09
# 2985 2020-10-10
# 2984 2020-10-10
# ...
# 1934 2021-04-24
# 1935 2021-04-24
# 1936 2021-04-24
# 828 2021-04-24
# 0 2021-04-24
# Name: 发布时间, Length: 3817, dtype: object
这里是直接使用了pandas自带的模块处理,也还有其他方法,可以参考博文1
做完这个才想起来好像有点不对…考虑了缺失,没考虑重复,赶紧给去个重,按着新闻的链接作为唯一标识。
dataI0 = dataI0.drop_duplicates(['标题链接'])
# 网址链接相当于数据的id
# 根据id对网址进行去重
接下来,嗯,考虑补全数据。我觉得这个思路就很混乱,处理缺失、去除重复,应该是一开始就要做的事情吧…上面是在干啥,咋突然处理了一波时间格式。
dataM0 = dataI0.loc[dataI0['标题'].isnull(),['标题链接','标题']].reset_index(drop=True)
# 先抽取出标题缺失的数据,拿到新闻的网址链接
import requests
from lxml import etree
for i in range(0,dataM0.shape[0]):
print(i,'-'*10)
# 打印序号
htmlT = requests.get(dataM0.iloc[i,0])
# 打开链接
htmlT.encoding = 'utf-8'
# 使用utf-8编码
htmlT = etree.HTML(htmlT.text)
# 取出其中的文本
dataM0.iloc[i,1] = htmlT.xpath('/html/body/div[12]/div[1]/div[1]/h1/text()')[0]
# 根据xpath取出新闻标题,需要精确到text()
print(dataM0.iloc[i,1],'\n')
# 打印新闻标题
dataI0.loc[dataI0['标题'].isnull(),'标题'] = dataM0['标题'].values
# 直接使用数据框赋值不可取
# 应当将取values后再赋值
# 因为数据框之间的赋值好像需要看index
这里需要说一下,我之前习惯的都是selenium的爬虫,但是这次不顶用了,所以换了个方式。但是etree搞下来的数据,它的xpath竟然需要精确到text(),这是我没有想到的。按selenium的经验,我只把路径写到了h1,然后怎么搞都拿不到文本,后来查了博文才知道2。这个事情说来神奇,当时查的其他博文34,都写着可以到h1,然后用i.text拿到文本,但我这里就是不行。另外,这里还需要注意编码的问题,我调成htmlT.encoding = 'utf-8’才正常,主要参考了博文5。
至于selenium为啥不行呢,我感觉根本原因是我太菜了不会用,直接原因如下:
# from selenium import webdriver
#
# driver_path = "D:\Software\Anaconda3\msedgedriver.exe"
# # msedgedriver.exe 的路径
#
# browser = webdriver.Edge(executable_path=driver_path)
# # 打开浏览器
#
# browser.get(dataM0.iloc[0,0])
# # 打开网页
#
# rowCount = browser.find_element_by_xpath("/html/body/div[12]/div[1]/div[1]/h1")
# # 选择表格
#
# # /html/body/div[13]/div[1]/div[1]/h1
# # /html/body/div[12]/div[1]/div[1]/h1
# # 在第一次爬取的时候div是12,第二次打开网页就是13
# # 可以每次循环都新打开浏览器,每次循环关闭浏览器
# # 这样似乎有点浪费资源,放弃selenium
接着处理一下keywords。访问了一下原网页发现,原网页当中没有keywords,看来需要自己动手。考虑用TF-IDF,使用jieba库6。这里有一个神奇的点,本来是打算从brief当中抽取关键词,毕竟文本信息更多。但brief里面竟然不仅仅有这条新闻的消息,还有其他的诸如推荐的新闻等的文本…于是放弃brief,改用标题。
dataM0 = dataI0.loc[dataI0['keywords'].isnull(),['标题','keywords']].reset_index(drop=True)
# 抽取keyword缺失的行
from jieba.analyse import extract_tags
# from jieba.analyse import textrank
# for keyword, weight in extract_tags(dataM0.iloc[0,0], withWeight=True):
# print('%s %s' % (keyword, weight))
# for keyword, weight in textrank(dataM0.iloc[0,0], withWeight=True):
# print('%s %s' % (keyword, weight))
for i in range(0,dataM0.shape[0]):
print(i, '-' * 10)
# 打印序号
keyW = extract_tags(dataM0.iloc[i,0])
dataM0.iloc[i, 1] = ','.join(keyW[0:3])
# 对关键词进行拼接
print(dataM0.iloc[i, 1])
dataI0.loc[dataI0['keywords'].isnull(),'keywords'] = dataM0['keywords'].values
# 直接使用数据框赋值不可取
# 应当将取values后再赋值
# 因为数据框之间的赋值好像需要看index
然后发布时间的话,可以直接从网址里面提取,用split进行文本处理就好
brief缺失的话,直接丢掉那些行吧,我印象缺失的行不到1%
下面开始进行可视化分析吧,好像也没其他预处理了
首先想做一个按天为单位的新闻发布量时序图,然后就出问题了。天数太多,坐标轴密密麻麻地重叠,这不行啊。查了查资料7,应当将坐标轴刻度间隔调大。
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.pyplot import MultipleLocator
countN = dataI0['发布时间'].value_counts()
countN.sort_index(inplace=True)
sns.lineplot(countN.index,countN.values)
x_major_locator=MultipleLocator(100)
#把x轴的刻度间隔设置为100,并存在变量里
ax=plt.gca()
#ax为两条坐标轴的实例
ax.xaxis.set_major_locator(x_major_locator)
#把x轴的主刻度设置为10的倍数
plt.show()
# 按天统计的发布趋势图
其他的图,大概如下:
- 新闻类别统计条形图
- 各类新闻在月的粒度上发布量时序图
- 发布量最大那天的新闻类别条形图
- 发布量最大那天的新闻类别饼图
- 根据keyword绘制词云
贴一下部分代码吧
# 21年4月20日的新闻类别饼图
plt.pie(countN.values,startangle = 90,counterclock = False)
plt.legend(countN.index, loc=0)
plt.show()
# 按照关键词绘制词云
from wordcloud import WordCloud
#用来正常显示中文
plt.rcParams["font.sans-serif"]=["SimHei"]
wordK = dataI0['keywords'].values
wordK = ','.join(wordK)
wordK = wordK.replace(',',' ')
from PIL import Image
import numpy as np
mask_pic = Image.open("pic.jpg")
mask_pic_array = np.array(mask_pic)
wc=WordCloud(
font_path='C:\\Windows\\Font\\simkai.ttf',
background_color="white",
mask = mask_pic_array)
wc.generate(wordK)
plt.imshow(wc,interpolation="bilinear")
plt.axis("off")
plt.show()
# 可惜并没有按照图片显示出来
噢,对了,这个地方还需要调一下设置,要不然中文会乱码
plt.rcParams['font.sans-serif'] = ['SimHei'] #用来显示中文标签
plt.rcParams['axes.unicode_minus'] = False #用来正常显示负号
以上,大概就是我提交的报名作品,然后就没过…嗯,只能去玩儿别的了
在以上的基础上,还想过做一个情感分析,等于说每一条新闻增加一列数据,然后再进行一些分析。或者是做embedding去训练一个新闻分类的模型,但意义不大,数据量太小。
这份报告大概是从当天中文的12:53开始做,然后在截止时间20:00的19:40完成,然后检查检查改了几个小错误,还是准备的不够啊。据说这个报名从06月初就开始了,到07月10日左右结束…而我是当天才开始做的。
然后关于狗熊会的培训和选拔也有点迷惑。怎么说呢,就是,人才计划貌似是培养数据分析能力的,是先想选题再去找数据?大概吧,我感觉选题在前。然后此次选拔呢,说的是,“根据附件提供的数据(news.csv),自行确定选题,完成一份数据分析报告。”,就有种数据在选题之前的感觉。当然事实上,数据肯定在选题之前,我们必然是要根据数据做选题的。但是,真实情况下的数据远比选拔附件提供的数据丰富详实,从而能做更多的分析。或许考核的也有其他的一些考虑,例如对数据集的补充能力?先确定选题,然后自己去爬新闻数据,大大扩充数据集,然后再做分析?可能是我的思路太过于局限所给的数据集了,当然也有时间不足的原因。
再贴一遍数据集的描述:
# <class 'pandas.core.frame.DataFrame'>
# RangeIndex: 4492 entries, 0 to 4491
# Data columns (total 7 columns):
# # Column Non-Null Count Dtype
# --- ------ -------------- -----
# 0 标题 4417 non-null object
# 1 标题链接 4492 non-null object
# 2 brief 4483 non-null object
# 3 keywords 3272 non-null object
# 4 发布时间 3817 non-null object
# 5 新闻类别 4492 non-null object
# 6 采集时的时间 4492 non-null object
# dtypes: object(7)
# memory usage: 245.8+ KB
纵观全局,我好像只做了新闻类别、发布时间这两个特征的分析。拿标题抽取了keyword,然后做了词云,这俩特征就没用了。标题链接?作为标识去了个重,然后拿它补充了标题,没了。采集时间?好像没啥思路。主要是感觉没意义吧,还要对采集人的行为做一做分析?或许选拔也有这方面的考虑吧,时间紧,没有过多思考。
此外,“人才计划的选拔,考察个人能力,而非团队合作能力。”,嗯,考察对指定数据集的分析能力?如果是这样就好了。我提交的报告属于探索性数据分析,开始做的时候是没有选题的,但是探索之后有结论,极其囿于数据集本身。说起来,这选拔好像考核的是传统的数据分析能力啊,要选题要汇报的那种,是带着目的去分析数据…而我提交的是探索性数据分析报告,凉凉。其实一开始也想到了这个问题,但是想了想,这能选啥题?带着啥目的去分析这个数据集?发布趋势?那不一张时序图就搞定了?类别分析?嗯…好像可以,哪个时段哪个类别发的最多,最多的类别又是怎样的发布趋势。但还是感觉不够,太少了,两张图就搞定。事实上,我在探索性数据分析的报告中,做的也就是发布趋势分析和类别分析。但是不是以明确选题的方式,而是混杂地统一扔进了探索性数据分析。或者应该叫数据挖掘,这或许就是区别了,某种程度上数据为先和选题为先的差别。
该博客记录了一次数据分析比赛中的数据预处理过程,包括新闻发布时间的格式转换、缺失值处理、重复值去重、标题与关键词的填充。作者通过XPath和jieba库抽取信息,构建词云并进行时间序列分析。尽管最终未通过选拔,但展示了数据清洗、特征提取和初步分析的能力。
531





