NLP 项目:维基百科文章爬虫和分类【01】 - 语料库阅读器

自然语言处理是机器学习和人工智能的一个迷人领域。这篇博客文章启动了一个具体的 NLP 项目,涉及使用维基百科文章进行聚类、分类和知识提取。灵感和一般方法源自《Applied Text Analysis with Python》一书。

一、说明

        该文是系列文章,揭示如何对爬取文本进行文本处理的全过程。在接下来的文章中,我将展示如何实现维基百科文章爬虫,如何将文章收集到语料库中,如何应用文本预处理、标记化、编码和矢量化,以及最后应用机器学习算法进行聚类和分类。

        本文的技术背景是Python v3.11和几个附加库,其中最重要的nltk v3.8.1是 和wikipedia-api v0.6.0。所有示例也应该适用于较新的版本。

二、项目概要

        该项目的目标是下载、处理和应用维基百科文章上的机器学习算法。首先,下载并存储来自维基百科的选定文章。其次,生成一个语料库,即所有文本文档的总和。第三,对每个文档文本进行预处理,例如通过删除停用词和符号,然后进行标记化。第四,将标记化文本转换为向量以接收数字表示。最后,应用不同的机器学习算法。

在第一篇文章中,解释了步骤一和步骤二。

2.1 先决条件

我喜欢在Jupyter Notebook中工作并使用优秀的依赖管理器Poetry。在您选择的项目文件夹中运行以下命令以安装所有必需的依赖项并在浏览器中启动 Jupyter 笔记本。

# Complete the interactive project creation
poetry init# Add core dependencies
poetry add nltk@^3.8.1 jupyterlab@^4.0.0 scikit-learn@^1.2.2 wikipedia-api@^0.5.8 matplotlib@^3.7.1 numpy@^1.24.3 pandas@^2.0.1# Add NLTK dependencies
python3 -c "import nltk; \nltk.download('punkt'); \nltk.download('averaged_perceptron_tagger'); \nltk.download('reuters'); \nltk.download('stopwords');"# Start jupyterhub
poetry run jupyterlab

浏览器中应该会打开一个新的 Jupyter Notebook。

2.2 Python 库

在这篇博文中,将使用以下 Python 库:

  • 维基百科-API:
    • Page代表维基百科文章及其标题、文本、类别和相关页面的对象。
  • NLTK
    • PlaintextCorpusReader用于提供对文档的访问、提供标记化方法并计算有关所有文件的统计信息的可遍历对象
    • sent_tokenizerword_tokenizer用于生成令牌

三、第 1 部分:维基百科文章爬虫

        该项目从创建自定义维基百科爬虫开始。尽管我们可以使用来自各种来源的维基百科语料库数据集(例如 NLTK 中的内置语料库),但自定义爬虫提供了对文件格式、内容和内容现实的最佳控制。

        下载和处理原始 HTML 可能非常耗时,尤其是当我们还需要从中确定相关链接和类别时。一个非常方便的图书馆可以帮助您。wikipedia -api为我们完成了所有这些繁重的工作。在此基础上,我们逐步开发核心功能。

        首先,我们创建一个基类,定义它自己的 Wikipedia 对象并确定存储文章的位置。

import os
import re
import wikipediaapi as wiki_apiclass WikipediaReader():def __init__(self, dir = "articles"):self.pages = set()self.article_path = os.path.join("./", dir)self.wiki = wiki_api.Wikipedia(language = 'en',extract_format=wiki_api.ExtractFormat.WIKI)try:os.mkdir(self.article_path)except Exception as e:pass

        这还定义了pages爬虫访问的一组页面对象。该page对象非常有用,因为它可以访问文章标题、文本、类别和其他页面的链接。

        其次,我们需要接收文章名称的辅助方法,如果存在,它将page向集合中添加一个新对象。我们需要将调用包装在一个try except块中,因为某些包含特殊字符的文章无法正确处理,例如Add article 699/1000 Tomasz Imieliński. 此外,还有一些我们不需要存储的元文章。

def add_article(self, article):try:page = self.wiki.page(self._get_page_title(article))if page.exists():self.pages.add(page)return(page)except Exception as e:print(e)

        第三,我们要提取一篇文章的类别。每篇维基百科文章都在页面底部的两个可见部分(请参阅以下屏幕截图)以及未呈现为 HTML 的元数据中定义类别。因此,最初的类别列表可能听起来令人困惑。看一下这个例子:

wr = WikipediaReader()
wr.add_article("Machine Learning")
ml = wr.list().pop()print(ml.categories)
# {'Category:All articles with unsourced statements': Category:All articles with unsourced statements (id: ??, ns: 14),
#  'Category:Articles with GND identifiers': Category:Articles with GND identifiers (id: ??, ns: 14),
#  'Category:Articles with J9U identifiers': Category:Articles with J9U identifiers (id: ??, ns: 14),
#  'Category:Articles with LCCN identifiers': Category:Articles with LCCN identifiers (id: ??, ns: 14),
#  'Category:Articles with NDL identifiers': Category:Articles with NDL identifiers (id: ??, ns: 14),
#  'Category:Articles with NKC identifiers': Category:Articles with NKC identifiers (id: ??, ns: 14),
#  'Category:Articles with short description': Category:Articles with short description (id: ??, ns: 14),
#  'Category:Articles with unsourced statements from May 2022': Category:Articles with unsourced statements from May 2022 (id: ??, ns: 14),
#  'Category:Commons category link from Wikidata': Category:Commons category link from Wikidata (id: ??, ns: 14),
#  'Category:Cybernetics': Category:Cybernetics (id: ??, ns: 14),
#  'Category:Learning': Category:Learning (id: ??, ns: 14),
#  'Category:Machine learning': Category:Machine learning (id: ??, ns: 14),
#  'Category:Short description is different from Wikidata': Category:Short description is different from Wikidata (id: ??, ns: 14),
#  'Category:Webarchive template wayback links': Category:Webarchive template wayback links (id: ??, ns: 14)}

        因此,我们根本不通过应用多个正则表达式过滤器来存储这些特殊类别。

def get_categories(self, title):page = self.add_article(title)if page:if (list(page.categories.keys())) and (len(list(page.categories.keys())) > 0):categories = [c.replace('Category:','').lower() for c in list(page.categories.keys())if c.lower().find('articles') == -1and c.lower().find('pages') == -1and c.lower().find('wikipedia') == -1and c.lower().find('cs1') == -1and c.lower().find('webarchive') == -1and c.lower().find('dmy dates') == -1and c.lower().find('short description') == -1and c.lower().find('commons category') == -1]return dict.fromkeys(categories, 1)return {}

        第四,我们现在定义抓取方法。这是一种可定制的广度优先搜索,从一篇文章开始,获取所有相关页面,将这些页面广告到页面对象,然后再次处理它们,直到文章总数耗尽或达到深度级别。说实话:我只用它爬过 1000 篇文章。

def crawl_pages(self, article, depth = 3, total_number = 1000):print(f'Crawl {total_number} :: {article}')page = self.add_article(article)childs = set()if page:for child in page.links.keys():if len(self.pages) < total_number:print(f'Add article {len(self.pages)}/{total_number} {child}')self.add_article(child)childs.add(child)depth -= 1if depth > 0:for child in sorted(childs):if len(self.pages) < total_number:self.crawl_pages(child, depth, len(self.pages))

        让我们开始爬取机器学习文章:

reader = WikipediaReader()
reader.crawl_pages("Machine Learning")print(reader.list())
# Crawl 1000 :: Machine Learning
# Add article 1/1000 AAAI Conference on Artificial Intelligence
# Add article 2/1000 ACM Computing Classification System
# Add article 3/1000 ACM Computing Surveys
# Add article 4/1000 ADALINE
# Add article 5/1000 AI boom
# Add article 6/1000 AI control problem
# Add article 7/1000 AI safety
# Add article 8/1000 AI takeover
# Add article 9/1000 AI winter

        最后,当一组page对象可用时,我们提取它们的文本内容并将它们存储在文件中,其中文件名代表其标题的清理版本。需要注意的是:文件名需要保留其文章名称的投降,否则我们无法再次获取页面对象,因为使用小写文章名称的搜索不会返回结果。

def process(self, update=False):for page in self.pages:filename = re.sub('\s+', '_', f'{page.title}')filename = re.sub(r'[\(\):]','', filename)file_path = os.path.join(self.article_path, f'{filename}.txt')if update or not os.path.exists(file_path):print(f'Downloading {page.title} ...')content = page.textwith open(file_path, 'w') as file:file.write(content)else:print(f'Not updating {page.title} ...')

        这是类WikipediaReader的完整源代码。

import os
import re
import wikipediaapi as wiki_apiclass WikipediaReader():def __init__(self, dir = "articles"):self.pages = set()self.article_path = os.path.join("./", dir)self.wiki = wiki_api.Wikipedia(language = 'en',extract_format=wiki_api.ExtractFormat.WIKI)try:os.mkdir(self.article_path)except Exception as e:passdef _get_page_title(self, article):return re.sub(r'\s+','_', article)def add_article(self, article):try:page = self.wiki.page(self._get_page_title(article))if page.exists():self.pages.add(page)return(page)except Exception as e:print(e)def list(self):return self.pagesdef process(self, update=False):for page in self.pages:filename = re.sub('\s+', '_', f'{page.title}')filename = re.sub(r'[\(\):]','', filename)file_path = os.path.join(self.article_path, f'{filename}.txt')if update or not os.path.exists(file_path):print(f'Downloading {page.title} ...')content = page.textwith open(file_path, 'w') as file:file.write(content)else:print(f'Not updating {page.title} ...')def crawl_pages(self, article, depth = 3, total_number = 1000):print(f'Crawl {total_number} :: {article}')page = self.add_article(article)childs = set()if page:for child in page.links.keys():if len(self.pages) < total_number:print(f'Add article {len(self.pages)}/{total_number} {child}')self.add_article(child)childs.add(child)depth -= 1if depth > 0:for child in sorted(childs):if len(self.pages) < total_number:self.crawl_pages(child, depth, len(self.pages))def get_categories(self, title):page = self.add_article(title)if page:if (list(page.categories.keys())) and (len(list(page.categories.keys())) > 0):categories = [c.replace('Category:','').lower() for c in list(page.categories.keys())if c.lower().find('articles') == -1and c.lower().find('pages') == -1and c.lower().find('wikipedia') == -1and c.lower().find('cs1') == -1and c.lower().find('webarchive') == -1and c.lower().find('dmy dates') == -1and c.lower().find('short description') == -1and c.lower().find('commons category') == -1]return dict.fromkeys(categories, 1)return {}

        让我们使用维基百科爬虫来下载与机器学习相关的文章。


reader = WikipediaReader()
reader.crawl_pages("Machine Learning")

print(reader.list())
# Downloading The Register ...
# Not updating Bank ...
# Not updating Boosting (machine learning) ...
# Not updating Ian Goodfellow ...
# Downloading Statistical model ...
# Not updating Self-driving car ...
# Not updating Behaviorism ...
# Not updating Statistical classification ...
# Downloading Search algorithm ...
# Downloading Support vector machine ...
# Not updating Deep learning speech synthesis ...
# Not updating Expert system ...s


四、第 2 部分:维基百科语料库

        所有文章均以文本文件形式下载到article文件夹中。为了提供所有这些单独文件的抽象,NLTK 库提供了不同的语料库阅读器对象。该对象不仅提供对单个文件的快速访问,还可以生成统计信息,例如词汇量、单个标记的总数或单词量最多的文档。

        让我们使用该类PlaintextCorpusReader作为起点,然后初始化它,使其指向文章:

import nltk
from  nltk.corpus.reader.plaintext import PlaintextCorpusReader
from time import timeclass WikipediaCorpus(PlaintextCorpusReader):passcorpus = WikipediaCorpus('articles', r'[^\.ipynb].*', cat_pattern=r'[.*]')
print(corpus.fileids())# ['2001_A_Space_Odyssey.txt',
#  '2001_A_Space_Odyssey_film.txt',
#  '2001_A_Space_Odyssey_novel.txt',
#  '3D_optical_data_storage.txt',
#  'A*_search_algorithm.txt',
#  'A.I._Artificial_Intelligence.txt',
#  'AAAI_Conference_on_Artificial_Intelligence.txt',
#  'ACM_Computing_Classification_System.txt',

        好的,这已经足够好了。让我们用两种方法来扩展它来计算词汇量和最大单词数。对于词汇,我们将使用 NLTK 辅助类FreqDist,它是一个包含所有单词出现的字典对象,此方法使用简单辅助类消耗所有文本corpus.words(),从中删除非文本和非数字。

def vocab(self):return nltk.FreqDist(re.sub('[^A-Za-z0-9,;\.]+', ' ', word).lower() for word in corpus.words())

        为了得到最大单词数,我们遍历所有带有 的文档fileids(),然后确定 的长度words(doc),并记录最高值

def max_words(self):max = 0for doc in self.fileids():l = len(self.words(doc))max = l if l > max else maxreturn max

        最后,我们添加一个describe生成统计信息的方法(这个想法也源于上面提到的《Applied Text Analysis with Python》一书)。

        该方法启动一个计时器来记录校园处理持续了多长时间,然后使用语料库阅读器对象的内置方法和刚刚创建的方法来计算文件数、段落数、句子数、单词数、词汇量和文档中的最大字数。

def describe(self, fileids=None, categories=None):started = time()return {'files': len(self.fileids()),'paras': len(self.paras()),'sents': len(self.sents()),'words': len(self.words()),'vocab': len(self.vocab()),'max_words': self.max_words(),'time': time()-started}pass

        这是最后一WikipediaCorpus堂课:

import nltk
from  nltk.corpus.reader.plaintext import PlaintextCorpusReader
from time import timeclass WikipediaCorpus(PlaintextCorpusReader):def vocab(self):return nltk.FreqDist(re.sub('[^A-Za-z0-9,;\.]+', ' ', word).lower() for word in corpus.words())def max_words(self):max = 0for doc in self.fileids():l = len(self.words(doc))max = l if l > max else maxreturn maxdef describe(self, fileids=None, categories=None):started = time()return {'files': len(self.fileids()),'paras': len(self.paras()),'sents': len(self.sents()),'words': len(self.words()),'vocab': len(self.vocab()),'max_words': self.max_words(),'time': time()-started}pass

        在撰写本文时,爬取维基百科有关人工智能和机器学习的文章后,可以获得以下统计数据:

corpus = WikipediaCorpus('articles', r'[^\.ipynb].*', cat_pattern=r'[.*]')
corpus.describe()
{'files': 1163,'paras': 96049,'sents': 238961,'words': 4665118,'vocab': 92367,'max_words': 46528,'time': 32.60307598114014}

五、结论

        本文是 NLP 项目在维基百科文章上下载、处理和应用机器学习算法的起点。本文涵盖了两个方面。首先,创建WikipediaReader通过名称查找文章的类,并可以提取其标题、内容、类别和提到的链接。爬虫由两个变量控制:爬取的文章总数和爬取的深度。其次,WikipediaCorpus在NLTK 的扩展PlaintextCorpusReader。该对象可以方便地访问单个文件、句子和单词,以及总语料库数据,例如文件数量或词汇、唯一标记的数量。下一篇文章将继续构建文本处理管道。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/154202.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SQL多表设计--一对多(外键)

-- 完成部门和员工的-- 选择当前db03 这个数据库use db03;-- 查看当前选中的数据库select database();-- 创建员工表create table tb_emp (id int unsigned primary key auto_increment comment ID,username varchar(20) not null unique comment 用户名,password varchar(32)…

多线程(线程互斥)

抢票代码编写 学习了前面有关线程库的操作后&#xff0c;我们就可以模拟抢票的过程 假设我们创建四个线程&#xff0c;分别代表我们的用户 然后设定总票数为1000张&#xff0c;四个线程分别将进行循环抢票操作&#xff0c;其实就是循环对票数进行打印&#xff0c;并进行对应的…

粘性文本整页滚动效果

效果展示 CSS 知识点 background 相关属性综合运用position 属性的 sticky 值运用scroll-behavior 属性运用scroll-snap-type 属性运用scroll-snap-align 属性运用 整体页面效果实现 <div class"container"><!-- 第一屏 --><div class"sec&qu…

高级深入--day29

入门案例 学习目标 创建一个Scrapy项目定义提取的结构化数据(Item)编写爬取网站的 Spider 并提取出结构化数据(Item)编写 Item Pipelines 来存储提取到的Item(即结构化数据)一. 新建项目(scrapy startproject) 在开始爬取之前,必须创建一个新的Scrapy项目。进入自定义的项目目…

Redis三种模式(主从复制,哨兵,集群)

Redis三种模式&#xff08;主从复制&#xff0c;哨兵&#xff0c;集群&#xff09; 一、主从复制1.1、主从复制概述1.2、 Redis主从复制流程1.3、 Redis主从复制作用1.4 、部署Redis 主从复制 二、Redis 哨兵模式2.1、哨兵模式的原理2.2、哨兵模式的作用2.3、哨兵的结构组成2.4…

PHP聊天系统源码 在线聊天系统网站源码 后台自适应PC与移动端

程序前台与后台自适应PC与移动端&#xff0c;支持一对多交流&#xff0c;可以自由创建新的房间与解散创建的房间&#xff0c;集成签到功能&#xff0c;等级功能&#xff0c;房间创建者可以对用户进行禁言、拉黑处理&#xff0c;房间可以由房间创建者自由设置进入密码&#xff0…

【软件测试】JUnit详解

文章目录 一. Junit是什么?二.Junit中常见的注解1. Test2. BeforeAll & AfterAll3. BeforeEach & AfterEach4. ParameterizedTest参数化5. Disabled6. Order 三. 测试套件1. 通过class运行测试用例2. 通过包运行测试用例 四. 断言 一. Junit是什么? JUnit是一个用于…

TCP协议总结

一、TCP协议概念。 TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;是一种面向连接的、可靠的传输层协议。它主要用于在计算机网络中&#xff0c;通过建立可靠的通信连接来进行数据传输。 TCP协议的特点如下&#xff1a; 可靠性&#xf…

unity操作_光源组件 c#

准备工作 添加资源导入后先不管&#xff0c;现在主要学习自带Directional Light 我们首先创建一个平面Plane 然后重置一下位置 然后创建一个Cube 也重置一下位置然后修改y0.5刚好在这个平面上 ctrl d复制一个Cube 修改位置和旋转角度 给物体一个颜色 接下来创建一个点光源 我们…

大型语言模型:RoBERTa — 一种鲁棒优化的 BERT 方法

一、介绍 BERT模型的出现导致了NLP的重大进展。BERT的架构源自Transformer&#xff0c;在各种下游任务上实现了最先进的结果&#xff1a;语言建模&#xff0c;下一句预测&#xff0c;问答&#xff0c;NER标记等。 大型语言模型&#xff1a;BERT — 来自变压器的双向编码器表示 …

用于物体识别和跟踪的下游任务自监督学习-2-背景

2.1用于现实世界应用的计算机视觉的基本概念 有许多中间步骤涉及应用计算机视觉算法来解决现实世界中的问题。机器视觉算法从光学传感器的图像采集开始,并最终解决现实世界的决策任务,如自动驾驶汽车、机器人自动化和监控。设计现代计算机视觉算法包括传感器数据编码、解码、…

php单独使用think-rom数据库 | thinkphp手动关闭数据库连接

背景&#xff08;think-orm2.0.61&#xff09; 由于需要长时间运行一个php脚本&#xff0c;而运行过程并不是需要一直与数据库交互&#xff0c;但thinkphp主要是为web站点开发的框架&#xff0c;而站点一般都是数据获取完则进程结束&#xff0c;所以thinkphp没提供手动关闭数据…

底部Taber的抽取

1.会抽取一个布局样式 2.布局样式里面抽取一个底部样式 这个是layout的代码 <template><view class"layout-wrapper"><view class"layout-content"><slot></slot></view><!-- 底部 --><Tabbar :activeInde…

Go 语言切片扩容规则是扩容2倍?1.25倍?到底几倍

本次主要来聊聊关于切片的扩容是如何扩的&#xff0c;还请大佬们不吝赐教 切片&#xff0c;相信大家用了 Go 语言那么久这这种数据类型并不陌生&#xff0c;但是平日里聊到关于切片是如何扩容的&#xff0c;很多人可能会张口就来&#xff0c;切片扩容的时候&#xff0c;如果老…

李沐深度学习记录5:13.Dropout

Dropout从零开始实现 import torch from torch import nn from d2l import torch as d2l# 定义Dropout函数 def dropout_layer(X, dropout):assert 0 < dropout < 1# 在本情况中&#xff0c;所有元素都被丢弃if dropout 1:return torch.zeros_like(X)# 在本情况中&…

electronjs入门-聊天应用程序,与Electron.js通信

随着第一章中构建的应用程序&#xff0c;我们将开始将其与Electron框架中的模块集成&#xff0c;并以此为基础&#xff0c;以更实用的方式了解它们。 过程之间的通信 根据第二章中的解释&#xff0c;我们将发送每个进程之间的消息&#xff1b;具体来说联系人和聊天&#xff1…

C++构造函数

在本文中&#xff0c;您将学习C 中的构造函数。您将学习什么是构造函数&#xff0c;如何创建它以及C 中的构造函数类型。 构造函数是成员函数的一种特殊类型&#xff0c;它在创建对象时会自动对其进行初始化。编译器通过其名称和返回类型将给定的成员函数标识为构造函数。构造函…

RabbitMQ开启消息发送确认和消费手动确认

开启RabbitMQ的生产者发送消息到RabbitMQ服务端的接收确认&#xff08;ACK&#xff09;和消费者通过手动确认或者丢弃消费的消息。 通过配置 publisher-confirm-type: correlated 和publisher-returns: true开启生产者确认消息。 server:port: 8014spring:rabbitmq:username: …

Reactor网络模式

文章目录 1. 关于Reactor模式的了解2. 基于Reactor模式实现epoll ET服务器2.1 EventItem类的实现2.2 Reactor类的实现Dispatcher函数AddEvent函数DelEvent函数EnableReadWrite函数 2.3 四个回调函数的实现acceptor回调函数recver回调函数sender回调函数errorer回调函数 3. epol…

mac使⽤nginx

⽅法1&#xff1a;homebrew 默认本地已经安装homebrew&#xff1b; 安装与启动 brew install nginx 安装nginx&#xff1b; brew services start nginx 启动nginx nginx⽂件⽬录 1. nginx安装⽂件⽬录/usr/local/Cellar/nginx 2. nginx配置⽂件⽬录/usr/local/etc/nginx 3. con…