Hệ Thống Phân Loại Tin Tức (Python): Crawler (bs+rq) + Xử Lý Dữ Liệu (Phân Tích Từ Jieba) + Bộ Phân Loại (Bayes)
ệ Thống Phân Loại Tin Tức (Python): Crawler (bs+rq) + Xử Lý Dữ Liệu (Phân Tích Từ Jieba) + Bộ Phân Loại (Bayes)
Giới Thiệu
Hệ thống phân loại tin tức này có khả năng tự động phân loại tin tức thành 10 loại và hiển thị độ chính xác của kết quả. (Độ chính xác kiểm tra chéo nằm trong khoảng 65%~70%, tập dữ liệu gồm 3183 mẫu. Có thể tăng cường tập dữ liệu để cải thiện độ chính xác).
Hệ thống gồm 3 phần chính:
Phần Crawler:
- Sử dụng thư viện Requests để xử lý các yêu cầu HTTP và POST.
- Sử dụng Beautiful Soup để xử lý các thẻ HTML và trích xuất thông tin.
Website mục tiêu: 谣言百科 - Baike Tin Đồn. Đây là một phần trong hệ thống xử lý tin đồn của tôi. Tuy nhiên, hiện tại hệ thống này đang gặp vấn đề trong việc nâng cao độ chính xác.
Ý tưởng cải thiện hiện tại:
- Tăng kích thước tập dữ liệu: Vì hầu hết các tin đồn trên mạng (đặc biệt là về sức khỏe và lịch sử) thường trùng lặp hoặc có tỷ lệ tương đồng rất cao. Đây là một cách tiếp cận dựa trên đặc điểm của dữ liệu.
- Xây dựng đồ thị tri thức: Phương pháp này khá phức tạp, có thể thử nghiệm trên một phần dữ liệu cụ thể để đánh giá hiệu quả.
- Nghiên cứu “dữ liệu nhỏ”: Có thể xem như một bài toán phân cụm. Trong trường hợp có nhiều dữ liệu chưa được gắn nhãn, phương pháp học bán giám sát có thể được áp dụng để học từ tập dữ liệu nhỏ trước khi mở rộng.
Gợi ý tài liệu tham khảo: Luận án tiến sĩ của Marcin Olof Szummer (cựu thành viên Google Brain) - “Learning from Partially Labeled Data”.
- Phần Xử Lý Dữ Liệu:
- Tiền xử lý tập dữ liệu, bao gồm chuẩn hóa và loại bỏ từ dừng (từ dừng lấy từ các nguồn công khai trên mạng).
- Phần Bộ Phân Loại:
- Sử dụng SVM (Hỗ Trợ Vector) từ thư viện sklearn để phân loại tập dữ liệu.
Code đầy đủ đã được tải lên GitHub:
🔗 Link GitHub
Môi Trường Phát Triển
- Beautiful Soup 4.4.0: Tài liệu
- Requests: Tài liệu
- Python 3
- Sklearn: Trang chủ
- Windows 10 (CPU: 4GB - Thời gian chạy phần phân loại là 51 giây).
- Sublime Text
- Jieba Phân Tích Từ
Website Thu Thập Dữ Liệu
URL: 谣言百科 - Baike Tin Đồn
Phân Tích Chiến Lược Crawler Website
Mục Tiêu Website:
- Đây là một website động với các thông tin được phân loại rõ ràng.
- Không chứa thông tin nhạy cảm hoặc bảo mật, có thể thực hiện crawler một cách an toàn.
Lợi Ích Dữ Liệu Thu Thập:
- Giá trị thực tiễn: Tập dữ liệu từ website này có thể áp dụng vào các hệ thống phân tích tin tức, xử lý tin đồn, hoặc các bài toán nghiên cứu tương tự.
Hình 2: Phân loại 10 loại thông tin. - Chắc là 10 chuyên mục tin tức ấy
Hình 3: Một trang thông tin tin tức riêng lẻ, có tiêu đề, trang web nguồn hoặc tác giả, thời gian cập nhật và nội dung.
Hình 4: Thông tin trang HTML tương ứng . Các lớp tiêu đề và nội dung được đặt tên rõ ràng.
Hình 5: Thư mục tin tức theo một danh mục thông tin nhất định.
Hình 6: Click vào địa chỉ của trang thông tin tin tức tiếp theo.
Hình 7: Địa chỉ thẻ html của địa chỉ trang tiếp theo.
Crawler, chương trình thu thập dữ liệu.
# 2017年7月4日00:11:42
# silei
# 爬虫目标网站:http://www.yaoyanbaike.com/
# 获取信息BeautifulSoup+request
# -*- coding:UTF-8 -*-
from urllib import request
from bs4 import BeautifulSoup
import re
import sys
import codecs
if __name__ == "__main__":
text_file_number = 0 # 同一类新闻下的索引数
number = 1 # 同类别新闻不同页面下的索引数
while (number <= 2):
if number==1: # 第一个新闻下地址是baby不是baby_数字所以要区分判断一下
get_url = 'http://www.yaoyanbaike.com/category/baby.html'
else:
get_url = 'http://www.yaoyanbaike.com/category/baby_'+str(number)+'.html' #这个是baby_数字,number就是目录索引数
head = {} #设置头
head['User-Agent'] = 'Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19'
# 模拟浏览器模式,定制请求头
download_req_get = request.Request(url = get_url, headers = head)
# 设置Request
download_response_get = request.urlopen(download_req_get)
# 设置urlopen获取页面所有内容
download_html_get = download_response_get.read().decode('UTF-8','ignore')
# UTF-8模式读取获取的页面信息标签和内容
soup_texts = BeautifulSoup(download_html_get, 'lxml')
# BeautifulSoup读取页面html标签和内容的信息
for link in soup_texts.find_all(["a"]):
print(str(text_file_number)+" "+str(number)+" "+link.get('href'))
# 打印文件地址用于测试
s=link.get('href')
if s.find("/a/") == -1:
print("错误网址") # 只有包含"/a/"字符的才是有新闻的有效地址
else:
download_url = link.get('href')
head = {}
head['User-Agent'] = 'Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19'
download_req = request.Request(url = "http://www.yaoyanbaike.com"+download_url, headers = head)
print("http://www.yaoyanbaike.com"+download_url)
download_response = request.urlopen(download_req)
download_html = download_response.read().decode('UTF-8','ignore')
soup_texts = BeautifulSoup(download_html, 'lxml')
texts = soup_texts.find_all('article')
soup_text = BeautifulSoup(str(texts), 'lxml')
p = re.compile("<[^>]+>")
text=p.sub("", str(soup_text))
# 去除页面标签
f1 = codecs.open('../data/baby/'+str(text_file_number)+'.txt','w','UTF-8')
# 将信息存储在本地
f1.write(text)
f1.close()
text_file_number = text_file_number + 1
number = number + 1
Phần Xử Lý Dữ Liệu
Phần này được chia thành hai bước chính:
- Phân Tích Từ và Loại Bỏ Từ Dừng:
- Phân tích từ (tokenization) và loại bỏ các từ dừng.
- Danh sách từ dừng được sử dụng là một danh sách mà tôi tìm thấy trên mạng, bao gồm các từ như “的”, “得”, “地” trong tiếng Trung. Đây là những từ không mang nhiều ý nghĩa ngữ nghĩa trong câu.
- Tạo Danh Sách Từ và Sắp Xếp Từ Xuất Hiện:
- Tạo danh sách các từ xuất hiện trong dữ liệu.
- Thực hiện sắp xếp các từ theo tần suất xuất hiện.
- Đây là bước chuẩn bị để tạo vector từ (word vector) trong quá trình xử lý sau.
Phần xử lý dữ liệu này đóng vai trò quan trọng trong việc tiền xử lý thông tin, giúp mô hình hiểu rõ hơn về các đặc điểm chính của dữ liệu.
# 2017年7月4日00:13:40
# silei
# jieba分词,停用词,数据可视化,知识图谱
# 数据文件数一共3183个
# baby,car,food,health,legend,life,love,news,science,sexual
# 129,410,409,406,396,409,158,409,409,38
# -*- coding:UTF-8 -*-
import jieba
dir = {'baby': 129,'car': 410,'food': 409,'health': 406,'legend': 396,'life': 409,'love': 158,'news': 409,'science': 409,'sexual': 38}
# 设置词典,分别是类别名称和该类别下一共包含的文本数量
data_file_number = 0
# 当前处理文件索引数
for world_data_name,world_data_number in dir.items():
# 将词典中的数据分别复制到world_data_name,world_data_number中
while (data_file_number < world_data_number):
print(world_data_name)
print(world_data_number)
print(data_file_number)
# 打印文件索引信息
file = open('../data/raw_data/'+world_data_name+'/'+str(data_file_number)+'.txt','r',encoding= 'UTF-8')
file_w = open('../data/train_data/'+world_data_name+'/'+str(data_file_number)+'.txt','w',encoding= 'UTF-8')
for line in file:
stoplist = {}.fromkeys([ line.strip() for line in open("../data/stopword.txt",encoding= 'UTF-8') ])
# 读取停用词在列表中
seg_list = jieba.lcut(line,cut_all=False)
# jieba分词精确模式
seg_list = [word for word in list(seg_list) if word not in stoplist]
# 去除停用词
print("Default Mode:", "/ ".join(seg_list))
for i in range(len(seg_list)):
file_w.write(str(seg_list[i])+'\n')
# 分完词分行输入到文本中
# file_w.write(str(seg_list))
# print(line, end='')
file_w.close()
file.close()
data_file_number = data_file_number + 1
data_file_number = 0
Tiếp Tục :
# 2017年7月4日17:08:15
# silei
# 训练模型,查看效果
# 数据文件数一共3183个
# baby,car,food,health,legend,life,love,news,science,sexual
# 129,410,409,406,396,409,158,409,409,38
# -*- coding:UTF-8 -*-
dir = {'baby': 129,'car': 410,'food': 409,'health': 406,'legend': 396,'life': 409,'love': 158,'news': 409,'science': 409,'sexual': 38}
# 设置词典,分别是类别名称和该类别下一共包含的文本数量
data_file_number = 0
# 当前处理文件索引数
def MakeAllWordsList(train_datasseg):
# 统计词频
all_words = {}
for train_dataseg in train_datasseg:
for word in train_dataseg:
if word in all_words:
all_words[word] += 1
else:
all_words[word] = 1
# 所有出现过的词数目
print("all_words length in all the train datas: ", len(all_words.keys()))
# key函数利用词频进行降序排序
all_words_reverse = sorted(all_words.items(), key=lambda f:f[1], reverse=True) # 内建函数sorted参数需为list
for all_word_reverse in all_words_reverse:
print(all_word_reverse[0], "\t", all_word_reverse[1])
all_words_list = [all_word_reverse[0] for all_word_reverse in all_words_reverse if len(all_word_reverse[0])>1]
return all_words_list
if __name__ == "__main__":
for world_data_name,world_data_number in dir.items():
while (data_file_number < world_data_number):
print(world_data_name)
print(world_data_number)
print(data_file_number)
file = open('../data/raw_data/'+world_data_name+'/'+str(data_file_number)+'.txt','r',encoding= 'UTF-8')
MakeAllWordsList(file)
for line in file:
print(line+'\n', end='')
file.close()
Phần Phân Loại và Nhận Dạng
Trong phần này, có hai lựa chọn cho bộ phân loại Naive Bayes:
- Naive Bayes trong thư viện NLTK:
- Đây là một bộ phân loại tích hợp sẵn trong thư viện NLTK (Natural Language Toolkit), được thiết kế để xử lý các tác vụ xử lý ngôn ngữ tự nhiên.
- Naive Bayes trong thư viện sklearn:
- Đây là bộ phân loại từ thư viện scikit-learn, nổi bật với hiệu suất cao và tính linh hoạt.
Tôi đã chọn sử dụng sklearn vì khả năng tối ưu hóa tốt hơn và hỗ trợ các chức năng mạnh mẽ trong việc phân loại dữ liệu.
Phần phân loại này giúp mô hình dự đoán chính xác loại tin tức dựa trên các đặc trưng đã được tiền xử lý trong các bước trước đó.
#coding: utf-8
import os
import time
import random
import jieba
import nltk
import sklearn
from sklearn.naive_bayes import MultinomialNB
import numpy as np
import pylab as pl
import matplotlib.pyplot as plt
def MakeWordsSet(words_file):
words_set = set()
with open(words_file, 'r', encoding='UTF-8') as fp:
for line in fp.readlines():
word = line.strip()
if len(word)>0 and word not in words_set: # 去重
words_set.add(word)
return words_set
def TextProcessing(folder_path, test_size=0.2):
folder_list = os.listdir(folder_path)
data_list = []
class_list = []
# 类间循环
for folder in folder_list:
new_folder_path = os.path.join(folder_path, folder)
files = os.listdir(new_folder_path)
# 类内循环
j = 0
for file in files:
if j > 410: # 每类text样本数最多100
break
with open(os.path.join(new_folder_path, file), 'r', encoding='UTF-8') as fp:
raw = fp.read()
# print raw
## --------------------------------------------------------------------------------
## jieba分词
# jieba.enable_parallel(4) # 开启并行分词模式,参数为并行进程数,不支持windows
word_cut = jieba.cut(raw, cut_all=False) # 精确模式,返回的结构是一个可迭代的genertor
word_list = list(word_cut) # genertor转化为list,每个词unicode格式
# jieba.disable_parallel() # 关闭并行分词模式
# print word_list
## --------------------------------------------------------------------------------
data_list.append(word_list)
class_list.append(folder)
j += 1
## 划分训练集和测试集
# train_data_list, test_data_list, train_class_list, test_class_list = sklearn.cross_validation.train_test_split(data_list, class_list, test_size=test_size)
data_class_list = list(zip(data_list, class_list))
random.shuffle(data_class_list)
index = int(len(data_class_list)*test_size)+1
train_list = data_class_list[index:]
test_list = data_class_list[:index]
train_data_list, train_class_list = zip(*train_list)
test_data_list, test_class_list = zip(*test_list)
# 统计词频放入all_words_dict
all_words_dict = {}
for word_list in train_data_list:
for word in word_list:
if word in all_words_dict:
all_words_dict[word] += 1
else:
all_words_dict[word] = 1
# key函数利用词频进行降序排序
all_words_tuple_list = sorted(all_words_dict.items(), key=lambda f:f[1], reverse=True) # 内建函数sorted参数需为list
all_words_list = list(zip(*all_words_tuple_list))[0]
return all_words_list, train_data_list, test_data_list, train_class_list, test_class_list
def words_dict(all_words_list, deleteN, stopwords_set=set()):
# 选取特征词
feature_words = []
n = 1
for t in range(deleteN, len(all_words_list), 1):
if n > 1000: # feature_words的维度1000
break
# print all_words_list[t]
if not all_words_list[t].isdigit() and all_words_list[t] not in stopwords_set and 1
Cảm Nhận
Ban đầu, tôi dự định xây dựng một hệ thống nhận dạng tin đồn, nhưng kết quả lại không đạt kỳ vọng. Cuối cùng, tôi phát hiện có thể biến dự án này thành một hệ thống phân loại tin tức để kết thúc công việc.
Để tiếp tục phát triển hệ thống nhận dạng tin đồn, tôi hiện đang nghiên cứu sâu về:
Đồ thị tri thức (Knowledge Graph):
Xây dựng mối liên kết giữa các thông tin để nhận diện tin đồn chính xác hơn.
Phân cụm (Clustering):
Phân loại dữ liệu không gắn nhãn và tìm ra các nhóm tin đồn có đặc điểm tương đồng.
Học bán giám sát (Semi-Supervised Learning):
Kết hợp dữ liệu có nhãn và không có nhãn để cải thiện hiệu quả mô hình.
Bắt đầu từ những tập dữ liệu nhỏ ("dữ liệu nhỏ") để giải quyết các vấn đề thực tế là bước đi phù hợp và đầy tiềm năng. Đây sẽ là hướng nghiên cứu lâu dài mà tôi đang theo đuổi.