@ArrowLLL
2017-04-20T21:49:20.000000Z
字数 4096
阅读 4428
python
机器学习
爬虫
主页地址 : 月光森林
信息内容安全课杨老师讲了一个tf-idf算法,用于提取一篇文档的关键词。觉得蛮好玩又正好练习python,所以就试着写了一下,特此记录,各位看官请轻拍板砖。
偷个懒,直接贴维基百科的解释:
至于原理,同样贴图(没错,我就这么懒了怎么样吧。。→_→) :
如果看维基还是了解不清楚的话,推荐阮一峰大神的博客,这个也是我们的老师上课讲的东西。
至于其中的数学原理,从维基的原理就很容易看出来,基础还是“贝叶斯定理”,另外一点就是idf的值这里使用到了熵的概念。推荐这篇博客的介绍 : TF-IDF模型的概率解释
(少壮不努力, 老大学数学。。。囧rz)
操作系统 : ubuntu 16.04
python版本 :python3.5.2
引入的python组件 :
sudo pip3 install jieba3k
直接安装sudo pip3 install numpy
安装sudo pip3 install scipy
安装sudo pip3 install scikit-learn
安装语料库准备, 努努书坊-鲁迅-伪自由书 。当然是用爬虫爬喽~
如果对爬虫理解有问题可参考本人之前的博客 —— 第一个爬虫:爬去古诗中带‘月’的诗句 。爬取的方法并没有很大的改进 (太弱只能写基础的。。。orz)
三步走 :
附上代码
#!usr/bin/python3
#coding=utf-8
import re
import os
from urllib.request import urlopen
from urllib.error import HTTPError
from bs4 import BeautifulSoup
def geturl(url):
try :
html = urlopen(url)
except HTTPError as e :
return None
try :
bsObj = BeautifulSoup(html, 'lxml')
except AttributeError as e :
return None
return bsObj
def getArticle(url, f) :
articlePage = geturl(url)
article = articlePage.findAll('p')
for paragraph in article :
print (paragraph.get_text(), file = f)
start = "http://www.kanunu8.com/book/4433/"
page = geturl(start)
tr = page.findAll('tr', {'bgcolor' : '#ffffff'})
links = (re.findall(r'<td><a href="(.*?)">(.*?)</a></td>', str(tr)))
if not os.path.isdir('article') :
os.mkdir('article')
for link in links :
with open('./article/' + link[1] + '.txt', 'wt+') as f :
f.write(link[1])
getArticle(start + link[0], f)
print ('---< ' , link[1], ' > get ---')
这个爬虫只是在之前的基础上加了一点新功能,将爬取结果都放在了一个文件夹下,使用到了 os 模块的 os.path.isdir(str)
函数和 os.mkdir(str)
函数。 前者用于检查 str 这个文件夹是否存在,后者用于创建一个名为 str 的文件夹 。
爬取的结果如下:
然后是爬取结果的一部分 :
仍然是三步走 :
CountVectorizer()类
和 TfidfTransformer()类
完成对词语的词频分析并获得每篇文章对应的词语的idf值, 可参考[python] 使用scikit-learn工具计算文本TF-IDF值这篇文章的第三部分;先附上代码,然后作说明 :
#coding: utf-8
import os
import jieba
import jieba.posseg as pseg
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
import numpt as np
path = './article/'
titlelist, wordslist = [], []
for fileName in os.listdir(path) :
titlelist.append(''.join(fileName[:-4].split()))
with open(path + fileName) as f :
text = f.read()
text = ''.join(text.split())
seg_list = jieba.cut(text)
wordslist.append(' '.join(seg_list))
vec = CountVectorizer()
wordFrequence = vec.fit_transform(wordslist)
words = vec.get_feature_names()
trans = TfidfTransformer()
tfidf = trans.fit_transform(wordFrequence)
wordsWeight = tfidf.toarray()
n = int(input('输入关键字的个数 : '))
while(n > 5 or n < 0) :
n = int(input('输入数字应大于零并且小于等于5 : '))
for (title, weight) in zip(titlelist, wordsWeight) :
print (title, ' : ')
loc = np.argsort(-weight)
for i in range(n) :
print('\t#' + str(i + 1) + ':', words[loc[i]])
print()
input('any key to continue...')
对于初学者(比如我)其中可能会不太懂点:
os.listpath(path) 函数
返回一个list, 存的是path 文件夹的子目录中文件或文件夹的名字 ;''.join(text.split())
将所有文章中的回车、空格删除 ;对于这一段 :
vec = CountVectorizer()
wordFrequence = vec.fit_transform(wordslist)
words = vec.get_feature_names()
trans = TfidfTransformer()
tfidf = trans.fit_transform(wordFrequence)
wordsWeight = tfidf.toarray()
看过给出链接的第三部分也就能很好地理解了,不再解释;
zip()
用于要同时遍历两个或多个 list ,将 list 打包,使用迭代器迭代获取 list 中的值。for i in range(n)
中,每次取出当前处理文档的最大tf-idf值的索引 loc , 然后在获得的词语列表words 中取出对应的词语,取出一个删除一个,即 del
的作用,然后继续取词知道取满 n 个为止。以上です~
在结巴分词的文档里看到一个提取关键词的函数:
jieba.analyse.extract_tags(sentence,topK)
函数有3点要求:
- 需要先import jieba.analyse
- setence为待提取的文本
- topK为返回几个TF/IDF权重最大的关键词,默认值为20
由此可以直接对中文文档做关键字提取,附上代码:
#!usr/bin/python3
# -*- coding: utf-8 -*-
import os
import jieba.analyse as jiebanys
import os
import jieba.analyse as jiebays
def getTag(fileLocation, k) :
with open(fileLocation) as f :
txt = f.read()
# 重点 ----- 在 ----- 这里
tags = jiebays.extract_tags(txt, topK = k)
return (" ".join(tags)).split()
if __name__ == '__main__' :
n = int(input('输入关键字的个数 : '))
while(n > 5 or n < 0) :
n = int(input('输入数字应大于零并且小于等于5 : '))
path = './article/'
for fileName in os.listdir(path) :
tagList = getTag(path + fileName, n)
print (fileName + ' : ')
for i in range(n) :
print ('\t#' + str(i + 1) + ':', tagList[i])
print()
input('any key to continue...')
附上结果 :
显然前后两份的结果区别还是很大的,具体原因应该是语料库的原因。不知道 jieba 的语料库如何,但是本人之前写的代码,因为语料库只有40个文本,且均选自同一本书籍,显然文本本身的区别应该就不是很大。对于一些应该作为关键词的词语,可能会因为在这40个文本组成的语料库相似度的问题而导致计算得出的 idf值很大,从而导致算法结果不是很理想。