本人菜鸡一只!
本篇文章,主要是记录《【python】爬虫篇:通过文章内容使用TF-IDF算法对文章进行分类(五)》中所说的具体代码,具体处理方向和思路见下文:
【python】爬虫篇:通过文章内容使用TF-IDF算法对文章进行分类(五):https://blog.csdn.net/lsr40/article/details/87281966
代码如下(但是由于代码可能有些年代了,我稍微整理过,我增加了很多注释,如果不能运行报错也方便大家调错):
第一段:生成字典和生成已有类别的文章的向量
#该方法是将从数据库中查出来的所有的已有分类的数据生成该次数据字典,
#保存该字典,并将所有数据根据该字典做成向量插回数据库
#这里传入的words是多篇文章的数据是一个list
def produceDictionary(words):
#创建数据库连接
conn_word_1 = psycopg2.connect(database="数据库名", user="用户名", password="密码", host="ip",port="端口")
cur1 = conn_word_1.cursor()
#用来存放所有词的list
merged_tag = []
#t是把每一条数据的id和生成的分词情况保存下来的list
t = []
#遍历每一篇文章
for index in range(len(words)):
#为每一篇文章分词(名词),为每个词打上权重,取权重最大的前5个词语
tag = jieba.analyse.extract_tags(words[index][1], withWeight=True, allowPOS='n',topK=5)
#把词和权重分开
tag_dict = {s[0]: s[1] for s in tag}
#讲词放入set中进行去重
merged_tag = set(tag_dict.keys()) | set(merged_tag)
#把该条数据加入到t中
t.append((words[index][0],tag_dict))
merged_tag_str=','.join(merged_tag)
#插入字典的词汇,通过逗号分隔
sql1="INSERT INTO 库.表 VALUES('字典编号id','{merged_tag}')".format(merged_tag=merged_tag_str)
cur1.execute(sql1)
conn_word_1.commit()
print('词典写入完毕')
conn_word_2 = psycopg2.connect(database="数据库名", user="用户名", password="密码", host="ip",port="端口")
cur2 = conn_word_2.cursor()
print('根据生成的字典,生成已有分类的文章向量,开始向数据库插入向量')
for row in t:
v = []
for i in merged_tag:
if i in row[1].keys():
#在有该词的位置上放入对应的权重值
v.append(row[1][i])
else:
#否则为0
v.append(0)
sql2="INSERT INTO 库.表 VALUES({row},'{v}')".format(row=row[0],v=v)
cur2.execute(sql2)
conn_word_2.commit()
if __name__ == '__main__':
#上面的代码都是在书写方法,这里才是实际调用运行
conn = psycopg2.connect(database="数据库名", user="用户名", password="密码", host="ip",port="端口")
cur3 = conn.cursor()
cur3.execute("SELECT 文章id,文章类型,正文内容 FROM 库.表 " \
"WHERE 正文内容 is not null and 正文内容 <> '' and length(正文内容 ) > 10")
rows = cur3.fetchall() # all rows in table
words=[]
print('拉取到数据')
for index in range(len(rows)):
words.append((rows[index][0],rows[index][2]))
print("============")
print('words数据拉取并且遍历完毕')
print("============")
#运行方法
produceDictionary(words)
第二段:生成已有类别文章的平均向量
#设置打印时显示方式,threshold=np.nan意思是输出数组的时候完全输出,不需要省略号将中间数据省略
np.set_printoptions(threshold=np.nan)
#不使用科学计数法
np.set_printoptions(suppress=True)
def loadAvgVector(index,art_type):
conn = psycopg2.connect(database="数据库名", user="用户名", password="密码", host="ip",port="端口")
cur = conn.cursor()
select_sql="SELECT 类型,向量 FROM 库.表 where 类型='{art_type}'".format(art_type=art_type)
print('select_sql:',select_sql)
cur.execute(select_sql)
rows = cur.fetchall() # all rows in table
words=[]
#创造一个一维,长度为95206的零向量(因为我字典有95206个单词,所以每个向量长度也那么长)
b = np.zeros(95206)
#遍历每一条数据
for i in range(len(rows)):
#取出对应的数据(去头去尾的原因应该是前面有大括号)
arr=rows[i][1][1:-1].split(',')
#print('row',rows[index][1][1:-1].split(','))
#words.append(rows[index][1])
#把取出来的字符串,做成向量
arr_f = list(map(float, arr))
a = np.array(arr_f)
#与零向量相加
b=b+a
#str('{:.10f}'.format(item)
#这里对于数值做了些简单的处理,具体什么我也记不清楚了,大家可以把注释去掉,打印出来看看
#获得平均向量
c=(b / len(rows)).tolist()
#str('{:.10f}'.format(item) if item != 0.0 else item
#这段好像是在处理数值为0的数据
result = '['+','.join([str('{:.10f}'.format(item) if item!=0.0 else item) for item in c])+']'
# print(result)
sql = "INSERT INTO 库.保存每个类型平均值的表 VALUES({index},'{result}','{art_type}')".format(index=index, result=result,art_type=art_type)
#print(sql)
cur.execute(sql)
conn.commit()
# print('['+result+']')
if __name__ == '__main__':
dictionary=['类型1','类型2','类型3',.....]
for index in range(len(dictionary)):
loadVector(index,dictionary[index])
第三段:为文章分类
#求两向量之间的夹角的函数,传入两个向量
def cosine_similarity(vector1, vector2):
#print('进入计算')
dot_product = 0.0
normA = 0.0
normB = 0.0
for a, b in zip(vector1, vector2):
if(a!=0 or b!=0):
#计算分母
dot_product += a * b
#计算分子
normA += a ** 2
normB += b ** 2
#如果分子中其中有一个是0
if normA == 0.0 or normB == 0.0:
return 0
else:
#计算两个向量的夹角
return round(dot_product / ((normA ** 0.5) * (normB ** 0.5))*100, 2)
#求两个向量的cos值
#传入的参数分别是未分类的文章,多个类型的平均向量,字典
def compareCos(tuple):
result=tuple[0]
vectors=tuple[1]
dictionary=tuple[2]
#获取当前数据的id
number_id=result[0]
#获取当前数据的文本内容
word = result[1]
tag = jieba.analyse.extract_tags(word, withWeight=True, allowPOS='n', topK=20)
tag_dict = {s[0]: s[1] for s in tag}
print('tag_dict : ', tag_dict)
print("============")
#通过字典获得未分类文章的向量
set_row = ','.join(dictionary).split(',')
vector = []
for i in set_row:
if i in tag_dict:
vector.append(tag_dict[i])
else:
vector.append(0)
# 新输入的文章向量
print(vector)
#将该文章的向量和所有类别的平均向量比较,获取前三的类别
topK=[]
index=-1
cos=()
for v in vectors:
sub_v=v[1][1:-1].split(',')
sub_f_v=list(map(float, sub_v))
#print('v1',sub_f_v)
now=cosine_similarity(vector, sub_f_v)
cos=(now,v[0])
print('cos',cos)
if (len(topK) < 3):
topK.append(cos)
topK.sort(reverse=True)
else:
if (cos[0] > topK[0][0]):
tmp = topK[1]
topK[1] = topK[0]
topK[0] = cos
topK[2] = tmp
print('topK',number_id,':',topK)
conn = psycopg2.connect(database="数据库", user="用户名", password="密码",host="ip",port="端口")
cur = conn.cursor()
print('创建连接')
cur.execute("INSERT INTO 库.表(文章id,文章类别) VALUES(%s,%s)",(number_id,topK))
conn.commit()
print('提交')
conn.close()
#加载所有文章类型的平均向量
def loadVector():
conn = psycopg2.connect(database="数据库", user="用户名", password="密码",host="ip",port="端口")
cur = conn.cursor()
cur.execute(
"SELECT 类型,平均向量 FROM 库.表")
rows = cur.fetchall() # all rows in table
vectors = []
for index in range(len(rows)):
vectors.append((rows[index][0], rows[index][1]))
return vectors
if __name__ == '__main__':
#获得所有类型的平均向量
vectors=loadVector()
conn = psycopg2.connect(database="数据库", user="用户名", password="密码",host="ip",port="端口")
cur = conn.cursor()
#将字典查询出来
cur.execute("SELECT 字典 FROM 库.表")
dictionary = cur.fetchone()
# rows = cur3.fetchone() # all rows in table
#查询需要做分类的文章的sql(可以使用left join来关联查出还未跑过的数据)
cur.execute("select 文章id,文章内容 from 库.表")
results = cur.fetchall()
print('拉取到数据')
#这里是个多线程!大家还需要自己设置调整下
with ThreadPoolExecutor(1) as executor:
for result in results:
executor.submit(compareCos, (result,vectors,dictionary))
conn.close()
这些代码写的时候,考虑的东西太少了,是有许多基础的地方可以优化的(我自己都觉得这些代码写的很杂很烂)
我暂时能想到的几个优化的点,比如:
1、写一个方法专门创建(获得)数据库连接
2、更好的调用多线程的方法,加快程序运行速度
3、方法,变量的命名还是要规范点
大概就是这样的一个情况,大家如果有兴趣可以爬取各种各样的文章来尝试下这样的分类方式,个人觉得还蛮准,挺有意思的~