网页聚类实现
网页聚类系统的设计与实现
该题目给出一组网页地址,然后对指定的网页按内容聚成3类。
网页地址如下:
|
编号 |
网址 |
|
1 |
http://blog.sina.com.cn/s/blog_59d698c90102zfct.html |
|
2 |
http://blog.sina.com.cn/s/blog_9c2fe26f0102z3ja.html?tj=fina |
|
3 |
http://blog.sina.com.cn/s/blog_6479dc5b0102ymb3.html |
|
4 |
http://blog.sina.com.cn/s/blog_59d698c90102zfd2.html |
|
5 |
http://blog.sina.com.cn/s/blog_59d698c90102zfcl.html |
|
6 |
http://blog.sina.com.cn/s/blog_14fdd56530102yknf.html?tj=tiyu |
|
7 |
http://blog.sina.com.cn/s/blog_62dc4e590102zot3.html?tj=fina |
|
8 |
http://blog.sina.com.cn/s/blog_1506181220102z9x9.html?tj=fina |
|
9 |
http://blog.sina.com.cn/s/blog_683c082b0102z3qc.html |
步骤:
- 抓取指定网址的源码;
- 解析网页文章内容;
- 对文章内容进行分词,并转换成向量表示;(可以考虑用词袋法或TF.IDF。)
- 选取合适的距离公式和聚类算法进行聚类,要求聚成3类。
要求:
- 显示解析后网页文章的内容;(步骤2)
- 显示文章对应的向量;(步骤3)
- 显示最终的分类结果和依据。(步骤4)
开发语言:Python
开发工具:Anaconda3,Pycharm 2022(所以其他版本可能会需要一些改动)
1.抓取源码,进行处理
使用urllib模块进行获取网页源码;使用urllib.request.urlopen函数进行获取url下的源码,然后进行UTF-8进行解码,获取正文内容。
然后通过正则语言和替换函数进行去除<p>标签、标点符号以及其他内容
2.分词与去除停用词
调用jieba库中的cut函数,对文本进行分词,但是此时的分词会有很多毫无含义、对于聚类没有实际影响的字词,于是就需要通过对比停用词字典,去除这些没有意义的字词。
3.计算TF.IDF
调用sklearn库中的CountVectorizer和TfidfTransformer,先把文本中的词语转换成词频矩阵,即文章内容转化成的向量,然后再计算每个词语的TF.IDF值
4.进行聚类
采用K-means法进行聚类,导入sklearn库中的K-means函数,直接对之前生成的TF.IDF进行聚类。首先是生成一个聚成3类的聚类器,再通过这个聚类器对TF.IDF值进行聚类。
5.计算文章到聚类中心的距离
K-means聚类后用主成分分析法降维,然后得到新的聚类中心,再用欧氏距离计算每个点的距离各样本到各簇中心的距离,最后比较每个样本到三个簇中心哪个距离最小,则属于哪个类。
源码如下:
import os
os.environ["OMP_NUM_THREADS"] = '1'
import urllib
import jieba
import numpy as np
import re
import urllib.request
from sklearn.decomposition import PCA
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.cluster import KMeans
jieba.setLogLevel(jieba.logging.INFO)
path = 'D:\python\project'
def Get_text(url,i):
text = urllib.request.urlopen(url).read().decode('utf-8')
start = text.find("<!-- 正文开始 -->")
end = text.find("<!-- 正文结束 -->")
text = text[start:end]
text = re.sub(re.compile('<p.*?>'),"",text)
text = re.sub(re.compile('<p>'),"",text)
text = re.sub(r'<(S*?)[^>]*>.*?|<.*? /> ','',text)
text = text.replace('\n','')
text = text.replace(' ','')
te = re.compile(r'[http|https]*://[a-zA-Z0-9.?/&=:]*', re.S)
text = re.sub(te, '', text)
text = text.strip()
name = str(i+1) + ".txt"
filepath = os.path.join(path, name)
with open(filepath, 'w+', encoding='utf8') as f:
f.write(text)
return text
def stopword(): # 停用词
stopword = [line.strip() for line in open('stopwords.txt', encoding='UTF-8').readlines()]
return stopword
def participles(url, i): # 分词与去除停用词
n_word = 0
result = ''
text = Get_text(url, i)
seg_list = jieba.cut(text)
stopwords = stopword()
for word in seg_list:
if word not in stopwords:
if word != '\t':
result += word
result += " "
n_word += 1
# print(seg_list)
return result
# 计算TF.IDF
def TF_IDF(ts):
vectorizer = CountVectorizer() # 创建词袋数据结构.该类会将文本中的词语转换为词频矩阵,矩阵元素a[i][j] 表示j词在i类文本下的词频
tf_idf_transformer = TfidfTransformer() # 该类会统计每个词语的tf-idf权值
tfidf = tf_idf_transformer.fit_transform(vectorizer.fit_transform(ts)) # 将文本转为词频矩阵并计算tf-idf
tfidf_matrix = tfidf.toarray() #将文本输入,得到词频矩阵
word = vectorizer.get_feature_names_out() # 获取词袋模型中的所有词语
tfidfpath = os.path.join(path, "tfidf_matrix.txt")
with open(tfidfpath, 'w+', encoding='utf8') as t:
t.write(str(tfidf_matrix))
return tfidf_matrix # tf-idf
def cluster(tfidf_matrix,n):
clf = KMeans(n_clusters=3, n_init='auto') # 生成聚类器 分成3类
s = clf.fit(tfidf_matrix) # 进行聚类
centers = clf.cluster_centers_ # 聚类中心
label = [] # 每个样本所属的簇
i = 1
while i <= len(clf.labels_):
label.append(clf.labels_[i - 1])
i = i + 1
y_pred = clf.labels_ # 获取标签聚类 (分别为 0,1,2 表示为X,Y,Z)
# pca降维,将数据转换成三维
pca = PCA(n_components=3) # 输出三维
newData = pca.fit_transform(tfidf_matrix) # 载入N维,输出三维
newcenter = pca.fit_transform(centers) # 三维的聚类中心
s = np.zeros((n, 3))
for i in range(0, n):
for j in range(0, 3):
vec1 = np.array(newData[i])
vec2 = np.array(newcenter[j])
distance = np.sqrt(np.sum(np.square(vec1-vec2)))
s[i][j] = distance
print("各样本距离聚类中心的距离为:" )
print(s)
h = 0
index_max = np.argmin(s, axis=1)
print("分类结果为:", index_max)
if __name__ == '__main__':
n = 9
URL = ['http://blog.sina.com.cn/s/blog_59d698c90102zfct.html',
'https://blog.sina.com.cn/s/blog_9c2fe26f0102z3ja.html?tj=fina',
'https://blog.sina.com.cn/s/blog_6479dc5b0102ymb3.html',
'http://blog.sina.com.cn/s/blog_59d698c90102zfd2.html',
'http://blog.sina.com.cn/s/blog_59d698c90102zfcl.html',
'http://blog.sina.com.cn/s/blog_14fdd56530102yknf.html?tj=tiyu',
'http://blog.sina.com.cn/s/blog_62dc4e590102zot3.html?tj=fina',
'http://blog.sina.com.cn/s/blog_1506181220102z9x9.html?tj=fina',
'http://blog.sina.com.cn/s/blog_683c082b0102z3qc.html']
texts = []
for i in range(n):
temp = URL[i]
txt = participles(temp, i)
texts.append(txt)
tpath = os.path.join(path, "texts.txt")
with open(tpath, 'w+', encoding='utf8') as f:
f.write(str(texts))
tfidf_matrix = TF_IDF(texts)
kmeans = cluster(tfidf_matrix,n)
PS:(1)停用词那里的stopwords.txt就是停用词表,自己去网上随便搜一个来用就行
(2)cluster函数里的clf = KMeans(n_clusters=3, n_init='auto')那一句,n_init='auto'是pycharm2022需要的,2019版需要删去这一句,其他版本没试过不知道
运行结果:
显示最终的分类结果和依据

第一类:0(体育类)

第二类:1(金融类)

第三类:2(教育类)

虽然有两篇文章的正文已无法显示,但通过查看这两篇博客的网页源码发现它们也属于教育类,证明分类没有错


该实现方案的优缺点
优点:由于K-means算法开始时会随机产生K个初始簇中心,也就是随机选择K个点座位初始簇中心,计算每个样本点到各簇中心的距离后再重新计算簇中心,初始簇中心的随机性会导致每次运行的结果可能不太一样,因此采用主成分分析法(PCA)进行改进。对K-means聚类算法的结果进行降维,让向量减少,最后再重新计算一遍每篇文章到聚类中心的距离,得到分类,以此达到让最终分类结果趋于稳定的效果。
缺点:初始簇中心对于分类的有着不小的影响,但由于时间等各种原因没有选择一开始就优化簇中心选择的算法,但实际上优化簇中心选择应该比聚类后再降维的失真要更小,可以采用K-means++等改进算法实现。
更多推荐


所有评论(0)