1. 程式人生 > >維基百科中的資料科學:手把手教你用Python讀懂全球最大百科全書

維基百科中的資料科學:手把手教你用Python讀懂全球最大百科全書

編譯:狗小白、李佳、張弛、魏子敏

沒人否認,維基百科是現代最令人驚歎的人類發明之一。

幾年前誰能想到,匿名貢獻者們的義務工作竟創造出前所未有的巨大線上知識庫?維基百科不僅是你寫大學論文時最好的資訊渠道,也是一個極其豐富的資料來源。

從自然語言處理到監督式機器學習,維基百科助力了無數的資料科學專案。

維基百科的規模之大,可稱為世上最大的百科全書,但也因此稍讓資料工程師們感到頭疼。當然,有合適的工具的話,資料量的規模就不是那麼大的問題了。

本文將介紹“如何程式設計下載和解析英文版維基百科”。

在介紹過程中,我們也會提及以下幾個資料科學中重要的問題:

1、從網路中搜索和程式設計下載資料

2、運用Python庫解析網路資料(HTML, XML, MediaWiki格式)

3、多程序處理、並行化處理

這個專案最初是想要收集維基百科上所有的書籍資訊,但我之後發現專案中使用的解決方法可以有更廣泛的應用。這裡提到的,以及在Jupyter Notebook裡展示的技術,能夠高效處理維基百科上的所有文章,同時還能擴充套件到其它的網路資料來源中。

本文中運用的Python程式碼的筆記放在GitHub,靈感來源於Douwe Osinga超棒的《深度學習手冊》。前面提到的Jupyter Notebooks也可以免費獲取。

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# Library for parsing HTMLfrom bs4 import BeautifulSoupbase_url = 'https://dumps.wikimedia.org/enwiki/'index = requests.get(base_url).textsoup_index = BeautifulSoup(index, 'html.parser')# Find the links on the pagedumps = [a['href'] for a in soup_index.find_all('a') if  a.has_attr('href')]dumps['../', '20180620/', '20180701/', '20180720/', '20180801/', '20180820/', '20180901/', '20180920/', 'latest/']

這段程式碼使用了BeautifulSoup庫來解析HTML。由於HTML是網頁的標準標識語言,因此就處理網路資料來說,這個庫簡直是無價瑰寶。

本專案使用的是2018年9月1日的dump(有些dump資料不全,請確保選擇一個你所需的資料)。我們使用下列程式碼來找到dump裡所有的檔案。

dump_url = base_url + '20180901/'# Retrieve the htmldump_html = requests.get(dump_url).text# Convert to a soupsoup_dump = BeautifulSoup(dump_html, 'html.parser')# Find list elements with the class filesoup_dump.find_all('li', {'class': 'file'})[:3][
  • enwiki-20180901-pages-articles-multistream.xml.bz2 15.2 GB
  • ,
  • enwiki-20180901-pages-articles-multistream-index.txt.bz2 195.6 MB
  • ,
  • enwiki-20180901-pages-meta-history1.xml-p10p2101.7z 320.6 MB
  • ]

    我們再一次使用BeautifulSoup來解析網路找尋檔案。我們可以在https://dumps.wikimedia.org/enwiki/20180901/頁面裡手工下載檔案,但這就不夠效率了。網路資料如此龐雜,懂得如何解析HTML和在程式中與網頁互動是非常有用的——學點網站檢索知識,龐大的新資料來源便觸手可及。

    考慮好下載什麼

    上述程式碼把dump裡的所有檔案都找出來了,你也就有了一些下載的選擇:文章當前版本,文章頁以及當前討論列表,或者是文章所有歷史修改版本和討論列表。如果你選擇最後一個,那就是萬億位元組的資料量了!本專案只選用文章最新版本。

    所有文章的當前版本能以單個文件的形式獲得,但如果我們下載解析這個文件,就得非常費勁地一篇篇文章翻看,非常低效。更好的辦法是,下載多個分割槽文件,每個文件內容是文章的一個章節。之後,我們可以通過並行化一次解析多個文件,顯著提高效率。

    “當我處理文件時,我更喜歡多個小文件而非一個大文件,這樣我就可以並行化執行多個文件了。”

    分割槽文件格式為bz2壓縮的XML(可擴充套件標識語言),每個分割槽大小300~400MB,全部的壓縮包大小15.4GB。無需解壓,但如果你想解壓,大小約58GB。這個大小對於人類的全部知識來說似乎並不太大。

    維基百科壓縮檔案大小

    下載檔案

    Keras 中的get_file語句在實際下載檔案中非常好用。下面的程式碼可通過連結下載檔案並儲存到磁碟中:

    from keras.utils import get_filesaved_file_path = get_file(file, url)

    下載的檔案儲存在~/.keras/datasets/,也是Keras預設儲存設定。一次性下載全部檔案需2個多小時(你可以試試並行下載,但我試圖同時進行多個下載任務時被限速了)

    解析資料

    我們首先得解壓檔案。但實際我們發現,想獲取全部文章資料根本不需要這樣。我們可以通過一次解壓執行一行內容來迭代文件。當記憶體不夠執行大容量資料時,在檔案間迭代通常是唯一選擇。我們可以使用bz2庫對bz2壓縮的檔案迭代。

    不過在測試過程中,我發現了一個更快捷(雙倍快捷)的方法,用的是system utility bzcat以及Python模組的subprocess。以上揭示了一個重要的觀點:解決問題往往有很多種辦法,而找到最有效辦法的唯一方式就是對我們的方案進行基準測試。這可以很簡單地通過%%timeit Jupyter cell magic來對方案計時評價。

    迭代解壓檔案的基本格式為:

    data_path = '~/.keras/datasets/enwiki-20180901-pages-articles15.xml-p7744803p9244803.bz2# Iterate through compressed file one line at a timefor line in subprocess.Popen(['bzcat'],  stdin = open(data_path),  stdout = subprocess.PIPE).stdout: # process line

    如果簡單地讀取XML資料,並附為一個列表,我們得到看起來像這樣的東西:

    維基百科文章的源XML

    上面展示了一篇維基百科文章的XML檔案。每個檔案裡面有成千上萬篇文章,因此我們下載的檔案裡包含百萬行這樣的語句。如果我們真想把事情弄複雜,我們可以用正則表示式和字串匹配跑一遍文件來找到每篇文章。這就極其低效了,我們可以採取一個更好的辦法:使用解析XML和維基百科式文章的定製化工具。

    解析方法

    我們需要在兩個層面上來解析文件:

    1、從XML中提取文章標題和內容

    2、從文章內容中提取相關資訊

    好在,Python對這兩個都有不錯的應對方法。

    解析XML

    解決第一個問題——定位文章,我們使用SAX(Simple API for XML) 語法解析器。BeautifulSoup語句也可以用來解析XML,但需要記憶體載入整個文件並且建立一個文件物件模型(DOM)。而SAX一次只執行XML裡的一行字,完美符合我們的應用場景。