Python新闻联播词频统计

在本文中,我们将编写这样一个 Python 程序:从网络上抓取前一日《新闻联播》的文本稿,利用分词技术将它们拆散成词组,对拆散的词组按照出现频率统计,将统计值按从大到小顺序绘制柱形图。

本文介绍的程序,使用 Python2 来编写,借助于丰富的开源库:

  • mechanize BeautifulSoup 用来编写网页爬虫
  • jieba 中文分词库
  • matplotlib 科学绘图库

按照程序逻辑,我将全文分为以下三个部分:

  1. 获取《新闻联播》文本稿
  2. 文本分词统计
  3. 词频排序绘图

完整的源代码的Github地址

获取《新闻联播》文本稿

CCTV 的网站上提供了《新闻联播》的文本稿,我们只需要想办法将它们爬下来。

例如,这里是2014年12月10日的《新闻联播》网络视频的地址。

打开页面后,会发现里面列出很多视频地址:

  • 新闻联播完整版视频 20141210
  • [视频]【年终经济专稿】中国经济从要素驱动转向创新驱动
  • ……

新闻联播完整版视频这个链接,打开后是当日新闻联播的完整录像,并没有文本稿,因此不是我们需要的。

在它之后的,以[视频]打头的,是当日新闻联播中具体的每一条新闻,打开后会发现,视频下方就是文本稿。

因此,我们爬虫的目标就很明确了:首先爬取每天的具体新闻链接,之后依次进入链接爬取里面的文本稿。

首先,我们生成新闻列表页面的 URL :

# 昨天日期
yesterday = datetime.now() - timedelta(days=1)
ROOT_URL = 'http://cctv.cntv.cn/lm/xinwenlianbo/' + yesterday.strftime('20%y%m%d') + '.shtml'
print "新闻目录URL:", ROOT_URL

之后,我们首先爬取列表,再根据列表爬取具体新闻内容:

# 新闻联播中各条具体新闻链接
response = br.open(ROOT_URL)
soup = BeautifulSoup(response)
soup = soup.find('div', 'md_bd')
news_urls = []
count = 0

print '正在获取昨日新闻列表...'
# 抓取各条具体新闻链接
for tag_li in soup.find_all('li'):
    # 避开新闻联播全文
    if count == 0:
        count += 1
        continue
    news_urls.append(tag_li.a.get('href'))
    count += 1
print u"昨日共%d条新闻。" % len(news_urls)

print '正在获取新闻联播内容...'
news_count = 1
# 主要内容
content = ''
for url in news_urls:
    print "[%d/%d]获取中..." % (news_count, len(news_urls)),
    response = br.open(url)
    soup = BeautifulSoup(response)
    soup = soup.find('div', 'body')
    content += soup.get_text()
    print "ok!"
    news_count += 1

至此,文本稿已经爬取完成,它们保存在变量 content ,等待进一步处理。

文本分词统计

前一节中我们得到了文本稿 content ,在本节中我们需要对它分词,这就需要用到结巴中文分词库。

我们将一段文本传入结巴,他会根据内置的文本分析算法,将中文的段落拆散成一个一个的中文词语。

在分词结果中,含有许多助词、连词,它们单独不具备意义,且出现频率很高,所以我们需要建立一个黑名单来过滤掉这些不重要的词语和一些不需要的符号。

程序如下:

print "正在分词并进行词频统计..."
# 创建黑名单,用户过滤
blacklist = [u'看似', u'关系', u'这本', u'接连', u'其', u'》', u'第一', u'第二', u'第三', u'第四', u'应',
             u'是', u'也', u'上', u'后', u'前', u'我台', u'再', u',', u'以及', u'因为', u'从而', u'但', u'像',
             u'更', u'用', u'“', u'这', u'有', u'在', u'去', u'都', u'”', u'还', u'使', u',', u'把', u'向',
             u'中', u'新', u'对', u' ', u' ', u')', u'、', u'。', u';', u'%', u':', u'?', u'(', u'的',
             u'和', u'了', u'等', u'将', u'到', u'', u'央视网', u'新闻联播', u'正在', u'我国', u'通过',
             u'国际', u'从', u'年', u'今天', u'要', u'并', u'n', u'《', u'为', u'月', u'号', u'日', u'大']
hist = {}
for word in jieba.cut(content):
    if word in blacklist:
        continue
    hist[word] = hist.get(word, 0) + 1

我们首先建立了一个黑名单 blacklist ,之后建立了一个空的字典 hist ,它的 key 表示一个词, value 表示其出现的词频。

我们使用一个循环来分词,word 表示分出来的一个词,如果它不在黑名单中,就对它进行词频统计。

词频排序绘图

得到了词频统计信息 hist ,下面考虑如何将其表现出来。

我想的一个办法是对 hist 中的 key 按照 value 进行排序,之后使用 matplotlib 画柱状图,程序如下:

# 对词频排序
hist_sorted = sorted(hist.iteritems(), key=lambda d: d[1], reverse=True)
# 取频率最高的50个词绘制曲线图
print "正在绘制柱状图..."
bar_width = 0.35
pyplot.bar(range(20), [hist_sorted[i][1] for i in range(20)],bar_width)
pyplot.xticks(range(20), [hist_sorted[i][0] for i in range(20)], fontproperties=font,rotation=30)
pyplot.title(u"《新闻联播》词频分析" + yesterday.strftime('20%y%m%d') + u"by Maxiee",fontproperties=font)
pyplot.show()

总结

运行程序,得到结果:

result

嗯……好吧,果然没有什么好看的……

写这篇文章的目的是用到的技术都比较基础,组合起来却能做个有趣的小玩意。

对于程序还有几点要补充的:

  1. 文章列表是分页的,程序中没有考虑(30分钟有这么多话要说啊……)
  2. 黑名单很傻很暴力。不知道结巴的词典里面有没有对词性分类,只 cut 出名词效果应该会好很多吧。

不要问我为何造这么个坑:My left brain has nothing right, my right brain has nothing left. Between them, is a big hole.