网页聚类系统的设计与实现

该题目给出一组网页地址,然后对指定的网页按内容聚成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

步骤:

  1. 抓取指定网址的源码;
  2. 解析网页文章内容;
  3. 对文章内容进行分词,并转换成向量表示;(可以考虑用词袋法或TF.IDF。)
  4. 选取合适的距离公式和聚类算法进行聚类,要求聚成3类。

要求:

  1. 显示解析后网页文章的内容;(步骤2)
  2. 显示文章对应的向量;(步骤3)
  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('&nbsp','')
    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++等改进算法实现。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐