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

news2024/11/24 11:43:44

塞巴斯蒂安

一、说明

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

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

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

        本文最初出现在我的博客admantium.com上。

二、项目概要

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

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

三、先决条件

我喜欢在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。

四、Python 库

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

维基百科-API:

  • Page代表维基百科文章及其标题、文本、类别和相关页面的对象。

NLTK

  • PlaintextCorpusReader用于提供对文档的访问、提供标记化方法并计算有关所有文件的统计信息的可遍历对象
  • sent_tokenizerword_tokenizer用于生成令牌

五、(第 1 部分)维基百科文章爬虫

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

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

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

import os
import re
import wikipediaapi as wiki_api

class 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') == -1
                and c.lower().find('pages') == -1
                and c.lower().find('wikipedia') == -1
                and c.lower().find('cs1') == -1
                and c.lower().find('webarchive') == -1
                and c.lower().find('dmy dates') == -1
                and c.lower().find('short description') == -1
                and 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 -= 1
    if 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.text
            with 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_api

class 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

    def _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.pages

    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.text
                with 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 -= 1
        if 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') == -1
                   and c.lower().find('pages') == -1
                   and c.lower().find('wikipedia') == -1
                   and c.lower().find('cs1') == -1
                   and c.lower().find('webarchive') == -1
                   and c.lower().find('dmy dates') == -1
                   and c.lower().find('short description') == -1
                   and 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 ...

六、(第 2 部分)维基百科语料库

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

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

import nltk
from  nltk.corpus.reader.plaintext import PlaintextCorpusReader
from time import time

class WikipediaCorpus(PlaintextCorpusReader):
    pass

corpus = 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 = 0
    for doc in self.fileids():
        l = len(self.words(doc))
        max = l if l > max else max
    return 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 time

class 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 = 0
        for doc in self.fileids():
            l = len(self.words(doc))
            max = l if l > max else max
        return max
    
    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

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

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通过名称查找文章的类,并可以提取其标题、内容、类别和提到的链接。爬虫由两个变量控制:爬取的文章总数和爬取的深度。其次,WikipediaCorpusNLTK 的扩展PlaintextCorpusReader。该对象可以方便地访问单个文件、句子和单词,以及总语料库数据,例如文件数量或词汇、唯一标记的数量。下一篇文章将继续构建文本处理管道。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

c++堆排序-建堆-插入-删除-排序

本文以大根堆为例&#xff0c;用数组实现&#xff0c;它的nums[0]是数组最大值。 时间复杂度分析&#xff1a; 建堆o(n) 插入删除o(logn) 堆排序O(nlogn) 首先上代码 #include<bits/stdc.h>using namespace std; void down(vector<int>&nums, int idx, i…

ThreeJS-3D教学四-光源

three模拟的真实3D环境&#xff0c;一个非常炫酷的功能便是对光源的操控&#xff0c;之前教学一中已经简单的描述了多种光源&#xff0c;这次咱们就详细的讲下一些最常见的光源&#xff1a; AmbientLight 该灯光在全局范围内平等地照亮场景中的所有对象。 该灯光不能用于投射阴…

【CAN信号解析】使用python-can/cantools解析CAN数据

文章目录 1. 如何解析CAN消息1.1 简介1.2 python-can库使用2. python-can库介绍2.1 完整解析流程2.2 简单示例3. 总结与坑4. 代码示例1. 解析一个DBC2. 生成一个DBC3. 解析.asc数据 保存为.csv格式1. 如何解析CAN消息 关于CAN的基础知识,可阅读如下链接: CAN协议详解CAN消息…

作为SiteGPT替代品,HelpLook的优势是什么?

在当今快节奏的数字化世界中&#xff0c;企业不断寻求创新方式来简化运营并增强客户体验。由于聊天机器人能够自动化任务、提供快速响应并提供个性化互动&#xff0c;它们在业务运营中的使用变得非常重要。因此&#xff0c;企业越来越意识到像SiteGPT和HelpLook这样高效的聊天机…

分享5个自动生成PPT的网站

1、Mindshow 之前公众号里分享过这款做PPT的AI工具&#xff0c;这次再拿出来分享一次。 没别的原因&#xff0c;确实好用。 而且也是目前能够和ChatGPT结合使用最好的PPT工具之一。 直接在ChatGPT生成PPT内容&#xff0c;转成Markdown格式复制进来一键排版&#xff0c;一份P…

MySQL ——多表连接查询

一、&#xff08;左、右和全&#xff09;连接概念 内连接&#xff1a; 假设A和B表进行连接&#xff0c;使用内连接的话&#xff0c;凡是A表和B表能够匹配上的记录查询出来。A和B两张表没有主付之分&#xff0c;两张表是平等的。 关键字&#xff1a;inner join on 语句&#xf…

更好用的的MybatisPlus:MybatisFlex(上)

更好用的的MybatisPlus&#xff1a;MybatisFlex&#xff08;上&#xff09; 前言 Mybatis 是我们常用的一个 ORM 框架&#xff0c;而 MybatisPlus &#xff08;以下简称 MP&#xff09; 则是对 Mybatis 进行了一层封装&#xff0c;便捷了我们的开发工作&#xff0c;但是由于其…

进程管理--进程调度基本概念

进程调度 进程调度的核心代码实现参考 kernel/sched/ 目录文件&#xff0c;主要包含以下几个部分&#xff1a; 调度算法&#xff1a;Linux 中实现了多种不同的进程调度算法&#xff0c;如 CFS&#xff08;Completely Fair Scheduler&#xff09;、O(1) 调度算法、实时调度算法…

RocketMQ Dashboard说解

RocketMQ Dashboard 是 RocketMQ 的管控利器&#xff0c;为用户提供客户端和应用程序的各种事件、性能的统计信息&#xff0c;支持以可视化工具代替 Topic 配置、Broker 管理等命令行操作。 介绍​ 功能概览​ 面板功能运维修改nameserver 地址; 选用 VIPChannel驾驶舱查看 …

python编程:加速计算机,优化性能的关键一步——清理临时文件夹Temp

引言&#xff1a; 随着时间的推移&#xff0c;您可能会注意到计算机的性能开始变慢。这可能是由于许多因素导致的&#xff0c;其中一个常见的问题是临时文件的积累。临时文件是由操作系统和应用程序生成的临时性文件&#xff0c;它们在使用后往往被遗忘或忽视。在本篇博客中&am…

[移动通讯]【Carrier Aggregation-4】【LTE-6】

前言&#xff1a; 这里主要介绍一下CA 技术里面&#xff0c;物理层主要工作。 参考&#xff1a; 载波聚合&#xff08;CA&#xff09; 目录&#xff1a; 1&#xff1a; CA 总体分析流程 2&#xff1a; CA 物理层 一 CA 总体分析流程 二 CA物理层 2.1 载波聚合LOG分析的相关包…

基于微信小程序的英语互助小程序设计与实现(亮点:小组制打卡、模拟考试答题、错题本、学习论坛)

文章目录 前言系统主要功能&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计…

如何看待中小企业实现数字化转型难的问题?_光点科技

随着科技的飞速发展和市场的竞争日益激烈&#xff0c;传统制造型企业不得不迎头赶上数字化时代的步伐&#xff0c;以保持竞争力并实现可持续增长。数字化转型已成为企业生存和成功的必经之路&#xff0c;但对于那些长期依赖传统方法的企业来说&#xff0c;这个过程可能会充满挑…

积加ERP与金蝶云星空对接集成日期范围报告查询打通销售出库新增

积加ERP与金蝶云星空对接集成日期范围报告查询打通销售出库新增 对接源平台:积加ERP 积加创始人及核心产品技术团队&#xff0c;深耕于跨境电商技术领域十余年&#xff0c;深刻领悟卖家在Amazon业务运营各环节的核心诉求&#xff0c;结合多年技术管理经验&#xff0c;全面考虑中…

18年互联网老兵:技术人如何快速成长?!

见字如面&#xff0c;我是军哥。 先问你几个问题。 你是不是每天996&#xff0c;但技术能力还是没有提高&#xff1f; 你是不是明明很努力却进步缓慢&#xff1f; 你是不是在同一家公司干了三年&#xff0c;感觉自己就有三年工作经验了&#xff1f; 你是不是明明知道想要什么&a…

【调度算法】进程调度算法、内存页面置换算法、LRU算法、LFU算法、磁盘调度算法等重点知识汇总

目录 进程调度算法 内存页面置换算法 LRU算法实现 LFU算法实现 磁盘调度算法 进程调度算法 当 CPU 空闲时&#xff0c;操作系统就选择内存中的某个「就绪状态」的进程&#xff0c;并给其分配 CPU。 什么时候会发生 CPU 调度呢&#xff1f;通常有以下情况&#xff1a; 当…

数字孪生燃气可视化系统的九问九答

关键词&#xff1a;数字孪生燃气、智慧燃气、数字孪生燃气系统、智慧燃气平台、智慧燃气场站 谈谈数字孪生燃气系统&#xff1f; 数字孪生燃气是将数字孪生技术应用于燃气运营中&#xff0c;与燃气系统物理实体、数据中心、信息系统等相结合&#xff0c;从而完成辅助管网设计…

想要在手机上查看三维模型、正射影像、激光点云?快来试试这款app

「四维轻云」是一款轻量化的地理空间数据管理云平台&#xff0c;支持地理空间数据的在线管理、编辑及分享。平台有项目管理、场景搭建、发布分享、素材库等功能模块&#xff0c;支持多用户在线协作管理&#xff0c;实现了轻量化、便捷化的空间数据应用。 很多行业用户想要在手…

Observability:使用 OpenTelemetry 自动检测 Java 应用程序

作者&#xff1a;David Hope 在快节奏的软件开发领域&#xff0c;特别是在云原生领域&#xff0c;DevOps 和 SRE 团队日益成为应用程序稳定性和增长的重要合作伙伴。 DevOps 工程师不断优化软件交付&#xff0c;而 SRE 团队则充当应用程序可靠性、可扩展性和顶级性能的管理者。…

linux使用操作[2]

文章目录 版权声明网络传输ping命令wget命令curl命令端口linux端口端口命令和工具 进程管理查看进程关闭进程 主机状态top命令内容详解磁盘信息监控 版权声明 本博客的内容基于我个人学习黑马程序员课程的学习笔记整理而成。我特此声明&#xff0c;所有版权属于黑马程序员或相…