各位老铁们,大家好,今天由我来为大家分享维基百科中的数据科学:使用Python 阅读世界上最大百科全书的分步指南,以及的相关问题知识,希望对大家有所帮助。如果可以帮助到大家,还望关注收藏下本站,您的支持是我们最大的动力,谢谢大家了哈,下面我们开始吧!
编译:苟小白、李佳、张驰、魏子民
没有人否认维基百科是现代最令人惊奇的人类发明之一。
几年前谁会想到匿名贡献者的志愿工作会创建有史以来最大的在线知识库?维基百科不仅是您撰写大学论文时最好的信息来源,而且还是极其丰富的数据来源。
从自然语言处理到监督机器学习,维基百科为无数数据科学项目提供支持。
维基百科太大了,堪称世界上最大的百科全书,但也让数据工程师有点头疼。当然,如果使用正确的工具,数据的大小并不是什么大问题。
在介绍过程中,我们还将提到数据科学中的以下重要问题:
1. 从网络搜索并以编程方式下载数据
2.使用Python库解析网络数据(HTML、XML、MediaWiki格式)
3、多进程处理和并行处理
这个项目最初的目的是收集维基百科上所有书籍的信息,但后来我发现该项目中使用的解决方案可以有更广泛的应用。这里描述并在Jupyter Notebook 中演示的技术可以有效地处理维基百科上的所有文章,并且还可以扩展到其他网络数据源。
GitHub 链接:
https://github.com/WillKoehrsen/wikipedia-data-science/blob/master/notebooks/Downloading%20and%20Parsing%20Wikipedia%20Articles.ipynb
免费访问地址:
https://github.com/DOsinga/deep_learning_cookbook
以编程方式搜索和下载数据
任何数据科学项目的第一步都是获取数据。当然,我们可以逐一进入维基百科页面,将搜索结果打包下载,但下载很快就会受到限制,同时也会给维基百科服务器带来压力。另一种方法是通过网站dumps.wikimedia.org(也称为dump)获取所有维基百科数据的定期快照结果。
通过以下代码,我们可以看到数据库的可用版本:
import requests# 用于解析HTML 的库from bs4 import BeautifulSoupbase_url='https://dumps.wikimedia.org/enwiki/'index=requests.get(base_url).textsoup_index=BeautifulSoup(index, 'html.parser')# 查找pagedumps=上的链接[a['href'] for a in soup_index.find_all('a') if a.has_attr('href')]dumps['./', '20180620/', '20180701/', '20180720/' , '20180801/', '20180820/', '20180901/', '20180920/', '最新/']
此代码使用BeautifulSoup 库来解析HTML。由于HTML 是网页的标准标记语言,因此该库在处理网络数据时是无价之宝。
本项目使用2018年9月1日的dump(部分dump数据不完整,请务必选择您需要的)。我们使用以下代码来查找转储中的所有文件。
dump_url=base_url + '20180901/'# 检索htmldump_html=requests.get(dump_url).text# 转换为soupsoup_dump=BeautifulSoup(dump_html, 'html.parser')# 查找类为file 的列表元素soup_dump.find_all('li ', {'class': '文件'})[:3][enwiki-20180901-pages-articles-multistream.xml.bz215.2 GB,enwiki-20180901-pages-articles-multistream-index.txt.bz2195.6 MB,enwiki-20180901-pages-meta-history1.xml-p10p2101.7z320.6 MB]
我们再次使用BeautifulSoup 来解析网络搜索文件。我们可以手动下载https://dumps.wikimedia.org/enwiki/20180901/页面中的文件,但这不够高效。网络数据如此之多,了解如何在程序中解析HTML并与网页交互是非常有用的——点网站检索知识,海量的新数据源触手可及。
考虑一下要下载什么
上面的代码找到转储中的所有文件,并且您有一些下载选项:文章的当前版本、文章页面和当前讨论列表,或者文章和讨论列表的所有历史修改版本。如果您选择最后一个,那就是TB 级的数据!本项目仅使用文章的最新版本。
可以通过单个文档的形式获取所有文章的当前版本,但是如果我们下载并解析这个文档,我们就必须一篇一篇地翻阅文章,效率非常低。更好的方法是下载多个分区文档,每个文档包含文章的一个章节。然后,我们可以通过并行化同时解析多个文档来显着提高效率。
“当我处理文档时,我更喜欢多个小文档而不是一个大文档,这样我就可以并行运行多个文档。”
分区文档格式为bz2压缩XML(可扩展标记语言),每个分区大小为300~400MB,压缩包总大小为15.4GB。无需解压,但如果你愿意的话,大小约为58GB。对于所有人类知识来说,这个尺寸似乎并不算太大。
维基百科压缩文件大小
下载文件
Keras 中的get_file 语句对于实际下载文件非常有用。以下代码通过链接下载文件并将其保存到磁盘:
从keras.utils 导入get_filesaved_file_path=get_file(文件, url)
下载的文件保存在~/.keras/datasets/中,这也是Keras的默认保存设置。一次下载所有文件需要2个多小时(可以尝试并行下载,但我尝试同时执行多个下载任务时被限速)
解析数据
我们首先必须解压缩该文件。但事实上,我们发现这并不是获取所有文章数据所必需的。我们可以通过一次一行运行解压来迭代文档。当没有足够的内存来运行大量数据时,迭代文件通常是唯一的选择。我们可以使用bz2 库迭代bz2 压缩文件。
然而,在测试过程中,我发现了一种更快(快两倍)的方法,使用系统实用程序bzcat 和Python 模块的子进程。上面揭示了一个重要的观点:解决一个问题往往有很多种方法,找到最有效的解决方案的唯一方法就是对我们的解决方案进行基准测试。使用%%timeit Jupyter cell magic 来计算解决方案的时间可以轻松完成此操作。
迭代解压文件的基本格式为:
data_path='~/.keras/datasets/enwiki-20180901-pages-articles15.xml-p7744803p9244803.bz2# 一次一行地遍历压缩文件for line in subprocess.Popen(['bzcat'], stdin=open(data_path ), stdout=subprocess.PIPE).stdout: # 流程行
如果我们只是读取XML 数据并将其附加为列表,我们会得到如下所示的内容:
维基百科文章的源XML
上面显示了Wikipedia 文章的XML 文件。每个文件中有数千篇文章,因此我们下载的文件包含数百万行此类语句。如果我们真的想让事情变得复杂,我们可以使用正则表达式和字符串匹配来遍历文档来查找每篇文章。这是极其低效的,我们可以采取更好的方法:使用解析XML 和维基百科风格文章的自定义工具。
解析方法
我们需要在两个层面上解析文档:
1.从XML中提取文章标题和内容
2.从文章内容中提取相关信息
幸运的是,Python 有很好的方法来处理这两个问题。
解析XML
为了解决第——篇定位文章中的第一个问题,我们使用SAX(Simple API for XML)语法解析器。 BeautifulSoup 语句也可用于解析XML,但需要内存来加载整个文档并构建文档对象模型(DOM)。 SAX一次只运行一行XML,非常适合我们的应用场景。
基本思想是我们搜索XML 文档并提取特定标签之间的相关信息。例如,给定以下XML 语句:
卡罗尔·F·尼克斯利\'\'\'卡罗尔·F·尼克斯利\'\'\'(约1929 年出生于[[弗吉尼亚州斯汤顿]] - 2006 年11 月2 日卒于[[肯塔基州格拉斯哥]]) [编辑|编辑]]和[[出版|出版商]]在“[[格拉斯哥每日时报]]”工作了近20年(后来成为其所有者),并在三任[[格拉斯哥日报]州长的领导下任职肯塔基州|肯塔基州州长]] 担任专员,后来担任商务部长。\n'
我们要过滤掉和标签之间的内容(这个title是维基百科文章标题,text是文章内容)。 SAX允许我们通过解析器和ContentHandler语句直接实现这样的功能——,以控制信息如何通过解析器然后被处理。每次我们将一行XML 句子扫描到解析器中时,内容处理程序都会帮助我们提取相关信息。
如果不尝试的话可能有点难以理解,但是Content handler的思想就是查找开始标签和结束标签之间的内容,并将找到的字符添加到缓存中。然后将缓存的内容保存到字典中,并以相应的标签作为相应的键。最后我们得到一个字典,其键是标签,其值是标签的内容。接下来,我们将这个字典传递给另一个函数,该函数将解析字典的内容。
我们需要编写的SAX 的唯一部分是内容处理程序。全文如下:
在此代码中,我们正在寻找标记为标题和文本的标签。每次解析器遇到其中之一时,都会将该字符保存到缓存中,直到遇到相应的结束标记()。然后它将缓存的内容保存到字典中——self._values。文章通过标签来区分。如果Content Handler遇到结束标签,它会将self._values添加到文章列表(self._pages)中。如果你感到困惑,练习和观察可能会有所帮助。
下面的代码展示了如何通过XML 文件查找文章。现在,我们只需将它们保存到handler._pages 中,稍后我们会将文章发送到另一个函数进行解析。
# 用于处理的对象xmlhandler=WikiXmlHandler()# 解析objectparser=xml.sax.make_parser()parser.setContentHandler(handler)# 迭代处理subprocess 中的filefor line.Popen(['bzcat'], stdin=open(data_path), stdout=subprocess.PIPE).stdout: parser.feed(line) # 当找到3 篇文章时停止if len(handler._pages) 2: break
如果我们观察handler._pages,我们将看到一个列表,其中每个元素都是一个包含文章标题和内容的元组:
handler._pages[0][('Carroll Knicely', ''''Carroll F. Knicely''' (约1929 年出生于[[弗吉尼亚州斯汤顿]] - 2006 年11 月2 日卒于[[肯塔基州格拉斯哥] ])是[[编辑|编辑]]和[[出版|出版商]] .)]
至此,我们已经编写了成功识别XML 中的文章的代码。现在我们已经解析了文件的一半,下一步是处理文章以查找特定的页面和信息。同样,我们使用专门为此工作创建的工具。
解析维基百科文章
维基百科运行在名为MediaWiki 的软件上,该软件用于构建wiki。这使得文章遵循标准格式,其中的信息可以通过编程轻松访问。尽管文章的文本可能看起来只是一个字符串,但由于其格式,它实际上编码了更多信息。为了有效地获取这些信息,我们引入了强大的mwparserfromhell,这是一个为处理MediaWiki 内容而构建的库。
如果我们将维基百科文章的文本传递给mwparserfromhell,我们会得到一个Wikicode 对象,其中包含许多用于对数据进行排序的方法。例如,以下代码从文章创建wikicode 对象并检索文章中的wikilinks()。这些链接指向其他维基百科文章:
import mwparserfromhell# 创建wiki 文章wiki=mwparserfromhell.parse(handler._pages[6][1])# 查找wikilinkswikilinks=[x.title for x in wiki.filter_wikilinks()]wikilinks[:5]['Provo, Utah' 、“瓦萨奇前线”、“兆赫兹”、“当代流行电台”、“瓦特”]
有许多有用的方法可以应用于维基代码,例如查找评论或搜索特定关键字。如果您想获取文章正文的最终修改版,可以拨打:
wiki.strip_code().strip()'KENZ(94.9 FM,'Power 94.9')是向犹他州盐湖城广播的顶级40/CHR 广播电台'
由于我的最终目标是找到有关书籍的所有文章,有没有办法使用解析器来识别特定类别的文章?幸运的是,答案是肯定的—— 使用MediaWiki 模板。
文章模板
模板是记录信息的标准方式。维基百科上有无数的模板,但与我们的目的最相关的是信息框。某些模板对文章的摘要信息进行编码。例如,战争与和平信息框是:
维基百科上的每种类型的文章,例如电影、书籍或广播电台,都有自己的信息框。在书籍示例中,信息框模板名为Infobox book。同样,wiki 对象有一个名为filter_templates() 的方法,它允许我们从文章中提取特定的模板。因此,如果我们想知道一篇文章是否是关于一本书的,我们可以通过书籍信息框进行过滤。如下图所示:
# 过滤书籍模板的文章wiki.filter_templates('Infobox book')
如果匹配成功,我们就找到了一本书!要查找您感兴趣的文章类别的信息框模板,请参阅信息框列表。
如何将用于解析文章的mwparserfromhell与我们编写的SAX解析器结合起来?我们修改了Content Handler中的endElement方法,将包含文章标题和文本值的字典发送到通过指定模板搜索文章文本的函数。如果该函数找到我们想要的文章,它就会从文章中提取信息并将其返回给处理程序。首先,我将展示更新后的endElement。
def endElement(self, name): '''元素的结束标签''' if name==self._current_tag: self._values[name]=' '.join(self._buffer) if name=='page': self ._article_count +=1 # 将页面发送到流程article function book=process_article(**self._values, template='Infobox book') # 如果文章是一本书,则追加到图书列表if book: self._books.append (书)
一旦解析器到达文章末尾,我们将文章传递给函数process_article ,如下所示:
def process_article(title, text, timestamp, template='Infobox book'): '''处理一篇维基百科文章寻找模板''' # 创建一个解析对象wikicode=mwparserfromhell.parse(text) # 在模板中搜索模板matches=wikicode.filter_templates(matches=template) if len(matches)=1: # 从信息框提取信息properties={param.name.strip_code().strip(): param.value.strip_code().strip() for param in matches[0].params if param.value.strip_code().strip()} # 提取内部维基链接
虽然我正在寻找有关书籍的文章,但此功能可用于搜索维基百科上任何类别的文章。只需将模板替换为指定类别的模板(例如Infobox语言用于查找语言),它就会只返回符合条件的文章信息。
我们可以在文件上测试这个函数和新的ContentHandler。
搜索了427481 篇文章。在1055 秒内找到1426 本书。
我们看一下搜索书籍的结果:
books[10]['战争与和平', {'name': '战争与和平', '作者': '列夫·托尔斯泰', '语言': '俄语,带有一些法语', '国家': '俄罗斯' , 'genre': '小说(历史小说)', 'publisher': '俄罗斯信使(连载)', 'title_orig': ' ', 'orig_lang_code': 'ru', 'translator': '第一个将《战争与和平》翻译成英文的是美国人内森·哈斯克尔·多尔(Nathan Haskell Dole),于1899 年', 'image': '托尔斯泰- 战争与和平- 第一版,1869.jpg', 'caption': '战争与和平的首页,第一版,1869 年(俄语)', 'release_date': '连载18651867; book 1869', 'media_type': '印刷版', 'pages': '1,225(首次出版版)'}, ['列夫·托尔斯泰', '小说', '历史小说', '俄罗斯信使', '连载(出版)', '类别:1869 俄罗斯小说', '类别: 史诗小说', '类别: 以19 世纪俄罗斯为背景的小说', '类别: 改编成电影的俄罗斯小说', '类别: 俄罗斯哲学小说'], ['https://books.google.com /?id=c4HEAN-ti1MC'、'https://www.britannica.com/art/English-literature'、'https://books.google.com/books?id=xf7umXHGDPcC'、'https://books.google.com/?id=E5fotqsglPEC'、'https://books。 google.com/?id=9sHebfZIXFAC'], '2018-08-29T02:37:35Z']
对于维基百科上的每一本书,我们将信息框中的信息组织成字典、该书在维基百科中的wikilinks信息、该书的外部链接以及最新的编辑时间戳。 (我将精力集中在这些信息上,为我的下一个项目构建图书推荐系统)。您可以修改process_article 函数和WikiXmlHandler 类来查找您需要的任何信息和文章!
如果您查看处理一个文件所需的时间(1055 秒),然后将其乘以55,您会发现处理所有文件需要超过15 个小时!当然,我们可以通宵运行它,但如果可以的话,我不想浪费额外的时间。这给我们带来了本项目中将介绍的最后一项技术:使用多处理和多线程的并行化。
并联运行
我们可以同时处理多个文件,而不是一次解析一个文件(这就是我们下载分区的原因)。我们可以通过多线程或多处理来使用并行化。
多线程和多处理
多线程和多处理是在一台计算机或多台计算机上同时执行许多任务的方法。我们在磁盘上有很多文件,每个文件都需要以相同的方式进行解析。一种简单的方法是一次解析一个文件,但这并没有充分利用我们的资源。因此,我们可以使用多线程或多处理来同时解析多个文件,这将大大加快整个过程的速度。
一般来说,多线程对于输入/输出绑定任务(例如读取文件或发出请求)更好(更快)。多重处理对于CPU 密集型任务更好(更快)。我不确定哪种方法最适合解析文章,因此我使用不同的参数再次对这两种方法进行了基准测试。
了解如何测试并找到解决问题的不同方法,您将在数据科学或任何技术领域的职业生涯中走得更远。
相关报道:
https://towardsdatascience.com/wikipedia-data-science-working-with-the-worlds-largest-encyclopedia-c08efbac5f5c
用户评论
哇!这篇文章也太赞了吧!我一直想学习数据科学,但感觉门槛很高。没想到可以用 Python 阅读维基百科,简直太酷了!我马上试试看!
有16位网友表示赞同!
这个标题很有吸引力啊,我一直觉得维基百科的内容很丰富,如果能用 Python 挖掘出来,那真是个宝藏!
有7位网友表示赞同!
说得对呀,数据科学在各个领域都有应用,学习一下Python 能给未来带来很多机会。这篇教程看起来深入浅出,我很期待看看具体步骤。
有20位网友表示赞同!
我之前尝试过用 Python 处理文本信息,但是维基百科的庞大规模让我望而却步,这个手把手教程正好解决我的痛点!
有11位网友表示赞同!
说的这么简单好像很容易上手啊! 不过我Python基础不太好,应该先学习一下相关知识才能跟着学吧?
有17位网友表示赞同!
维基百科确实是个资源宝库,用Python分析它的数据可以帮助我们更深入地理解世界各个领域的知识体系。期待看到更多精彩的应用!
有19位网友表示赞同!
我喜欢这种能将专业知识解释成易于理解的教程的形式,希望作者能够发布更多类似的内容!
有12位网友表示赞同!
说实话,我一直觉得维基百科很难用传统的搜索的方式找到真正想找的信息。如果可以用Python分析数据,也许可以发现很多隐藏的知识!
有17位网友表示赞同!
学习 Python 的确是很有必要的,特别是对于想要从事数据科学领域的工作的人来说。不过这篇教程看起来有点理论性,能不能提供更多的实战案例呢?
有19位网友表示赞同!
维基百科的信息有时候过于主观,用数据分析的方式或许可以帮助我们更客观地看待事物。期待看到作者的探究结果!
有8位网友表示赞同!
讲道理,我平时很少去使用维基百科,这篇文章让我了解到原来还可以用Python来做这样的事情!感觉很有潜力!
有13位网友表示赞同!
我对数据科学一直很感兴趣,但还没找到合适的机会学习。这篇教程正好对我来说非常有帮助,期待学习 Python 的基础知识,然后开始探索维基百科的数据!
有17位网友表示赞同!
我虽然不是数据科学从业者,但也对维基百科上的各种信息很有兴趣。如果能用Python分析出来更精准的知识点,那真是太棒了。
有14位网友表示赞同!
这篇博文讲解得非常详细,对于初学者来说也很容易理解!学习 Python 的道路上确实需要找到一些好的资源和教程,这篇文章就让我感觉很惊喜!
有16位网友表示赞同!
文章标题很有吸引力,但是我不明白Python和维基百科之间有什么相关性?能不能解释一下?谢谢!
有20位网友表示赞同!
讲真,我感觉用 Python 分析维基百科数据有点浪费时间。维基百科上的信息很多都是从官方的渠道获取来的,没有太多深度分析的空间。与其这样做,不如去寻找更优质的数据源!
有10位网友表示赞同!
我觉得这个教程太过于简化了,并没有给出足够的实战技巧和例子。对于想要深入学习Python处理数据的人来说,可能有些不足。
有9位网友表示赞同!
我虽然对数据科学很感兴趣,但并不想使用 Python。我认为其他的编程语言也可以完成类似的任务,甚至更方便!
有18位网友表示赞同!
维基百科上的信息很多时候都是经过了大量编辑和修改的,并不完全准确可靠。用Python分析这样的数据可能会导致偏差的结果。
有13位网友表示赞同!