[譯] Web 爬蟲下的 Python 資料分析:中情局全球概況圖解

Starrier發表於2019-02-26

Web 爬蟲下的 Python 資料分析:中情局全球概況圖解

在本文章中,我將展示如何使用 Python 和 HTML 解析從網頁中獲取有價值的資訊,之後會回答一些重要的資料分析問題。

[譯] Web 爬蟲下的 Python 資料分析:中情局全球概況圖解

在資料科學專案中,資料採集和清洗幾乎總是最耗時、最麻煩的步驟。每個人都喜歡用 3D 互動式圖表來構建一兩個很酷的深度神經網路(或者 XGboost)模型,以此炫耀個人的技術。但這些模型是需要原始資料的,而且它們並不容易採集和清洗。

畢竟生活不像 Kaggle 一樣是一個 zip 格式檔案,等待您的解壓和建模 :-)

但為什麼我們要採集資料或者構建模型呢?最初的動機是回答商業、科學或者是社會上的問題。這是趨勢麼?事物間的關聯性?實體的測量可以預測出這種現象的結果麼?因為回答這個問題將會驗證您作為這個該領域的科學家/實踐者所提出的假設。您只是在使用資料(而不是像化學家使用試管或者物理學家使用磁鐵)來驗證您的假設,並且科學地證明/反駁它。這就是資料科學中的「科學」部分,名副其實……

相信我,提出一個需要一些資料科學技術應用來解決的高質量問題並不難。而且每一個這樣的問題都會成為您的一個小專案,您可以將它開源在 Gihub 這樣的平臺來和您的朋友們分享。即使您不是專業的資料專家,也沒有人可以阻止您通過編寫很酷的程式碼來回答一個高質量的資料問題。這也表明您是對資料敏感並且可以用資料講故事的人。

今天讓我們來解決這樣一個問題。。。

一個國家的 GDP(按購買力平價)與其網際網路使用者比例是否有任何關係?這種趨勢對於低收入/中等收入/高收入國家而言是否類似?

現在您可以想到許多原始資料可以採集來作為回答此問題的資料。我發現中情局(是的 ‘AGENCY’)的一個網站儲存了世界上所有國家的基本事實資訊,是一個採集資料的好地方。

因此我們將使用以下 Python 模組來構建我們的資料庫和視覺化,

  • Pandas, Numpy, matplotlib/seaborn
  • Python urllib (傳送 HTTP 請求)
  • BeautifulSoup (用於 HTML 解析)
  • Regular expression module (用於查詢要搜尋的精確匹配文字)

讓我們討論一下解決這個資料科學問題的程式結構。整個專案程式碼在我的 Github 倉庫中都可以找到。如果您喜歡的話,請 fork 或者給個 star。

閱讀 HTML 首頁並傳遞給 BeautifulSoup

這兒是中情局全球概況首頁

[譯] Web 爬蟲下的 Python 資料分析:中情局全球概況圖解

圖:中情局全球概況首頁

我們使用一個帶有 SSL 錯誤忽略上下文的簡單 urllib 請求來檢索這個頁面,然後將它傳遞給神奇的 BeautifulSoup,它將為我們解析 HTML 並生成一個漂亮的文字轉儲。對於那些不熟悉 BeautifulSoup 庫的人,他們可以觀以下視訊或者在 Medium 上閱讀這篇內容豐富的文章。

YouTube 視訊地址:https://youtu.be/aIPqt-OdmS0

以下是讀取的首頁 HTML 的程式碼片段,

ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE

# 從 URL 中讀取 HTML 並將其傳遞給 BeautifulSoup
url = 'https://www.cia.gov/library/publications/the-world-factbook/'
print("Opening the file connection...")
uh= urllib.request.urlopen(url, context=ctx)
print("HTTP status",uh.getcode())
html =uh.read().decode()
print(f"Reading done. Total {len(html)} characters read.")
複製程式碼

以下是我們如何將其傳遞給 BeautifulSoup 並使用 find_all 方法查詢 HTML 中嵌入的所有國家名稱和程式碼。基本上,這個想法是找到名為 ‘option’ 的 HTML 標籤。標籤中的文字是國家名,標籤值的 5 號和 6 號表示的是 2 個字元的國家程式碼。

現在您可能會問,您如何知道只需要提取第五和第六字元?簡單的答案是您必須親自檢查 soup 文字--即解析的 HTML 文字,並確定這些索引。沒有通用的方法來檢查這一點,因為每個 HTML 頁面和底層結構都是獨一無二的。

soup = BeautifulSoup(html, 'html.parser')
country_codes=[]
country_names=[]

for tag in soup.find_all('option'):
    country_codes.append(tag.get('value')[5:7])
    country_names.append(tag.text)

temp=country_codes.pop(0) # To remove the first entry 'World'
temp=country_names.pop(0) # To remove the first entry 'World'
複製程式碼

爬取:將所有國家的文字資料逐個抓取到字典中

這一步就是他們所說的爬取或者抓取。要實現這一點,關鍵是要確定每個國家資訊頁面的 URL 是如何構造的。現在的一般情況是,這將很難獲得。特殊情況下,快速檢查顯示了一個非常簡單並且有規律的結構,以澳大利亞截圖為例。

[譯] Web 爬蟲下的 Python 資料分析:中情局全球概況圖解

這意味著有一個固定的URL,您必須附加兩個字元的國家程式碼,並獲得該國家的頁面網址。因此,我們只需遍歷國家程式碼列表,使用 BeautifulSoup 提取所有文字並儲存在本地詞典中。這是程式碼片,

# 基礎 URL
urlbase = 'https://www.cia.gov/library/publications/the-world-factbook/geos/'
#  空資料字典
text_data=dict()

# 遍歷每個國家
for i in range(1,len(country_names)-1):
    country_html=country_codes[i]+'.html'
    url_to_get=urlbase+country_html
    # 從 URL 中讀取 HTML 並將其傳遞給 BeautifulSoup
    html = urllib.request.urlopen(url_to_get, context=ctx).read()
    soup = BeautifulSoup(html, 'html.parser')
    txt=soup.get_text()
    text_data[country_names[i]]=txt
    print(f"Finished loading data for {country_names[i]}")
    
print ("\n**Finished downloading all text data!**")
複製程式碼

如果您喜歡,可以存放在一個 Pickle dump 中

另外,我偏向於序列化並將資料儲存在Python pickle 物件中。這樣我下次開啟 Jupyter 筆記本時,就可以直接讀取資料而無需重複網路爬行步驟。

import pickle
pickle.dump(text_data,open("text_data_CIA_Factobook.p", "wb"))

# 取消選擇,下次從本地儲存區讀取資料。
text_data = pickle.load(open("text_data_CIA_Factobook.p", "rb"))
複製程式碼

使用正規表示式從文字轉儲中提取 GDP/人均資料

這是程式的核心文字分析部分,我們藉助正規表示式模組來查詢我們在龐大文字字串中尋找的內容,並提取相關的數字資料。現在,正規表示式是 Python(或者幾乎是所有的高階程式語言)中的一個豐富資源。它允許在大量文字中以特定模式搜尋/匹配字串。這裡我們使用非常簡單的正規表示式方法來匹配精確的單詞,如“GDP — per capita (PPP):”然後讀取幾個字元,提取諸如 $ 和 () 等特定符號的位置,最後提取 GDP/人均數值。這是一個用數字說明的想法。

[譯] Web 爬蟲下的 Python 資料分析:中情局全球概況圖解

圖:文字分析圖示。

這個筆記本中還有其他一些常用的表達方式,例如,不管這個數字是以數十億還是數萬億美元計算出來的,都可以正確地提取出 GDP 總量。

# 'b' 去捕捉 'billions', 't' 去捕捉 'trillions'
start = re.search('\$',string)
end = re.search('[b,t]',string)
if (start!=None and end!=None):
    start=start.start()
    end=end.start()
    a=string[start+1:start+end-1]
    a = convert_float(a)
    if (string[end]=='t'):
    # 如果 GDP 數值在 萬億中,則乘以 1000
        a=1000*a
複製程式碼

以下是程式碼片段的示例。注意放置在程式碼中的多個錯誤處理檢查。這是必要的,因為 HTML 頁面具有極不可預測性。並非所有國家都有 GDP 資料,並非所有頁面的資料措辭都完全相同,並非所有數字看起來都一樣,並非所有字串放置方式都類似於 $ 和 ()。任何事都可能出錯。

為所有的場景規劃和編寫程式碼幾乎是不可能,但至少要有程式碼來處理可能出現的異常,這樣您的程式才不會停止,並且可以繼續優雅地進行下一頁處理。

# 初始化儲存資料的字典
GDP_PPP = {}
# 遍歷每個國家
for i in range(1,len(country_names)-1):
    country= country_names[i]
    txt=text_data[country]       
    pos = txt.find('GDP - per capita (PPP):')
    if pos!=-1: #If the wording/phrase is not present
        pos= pos+len('GDP - per capita (PPP):')
        string = txt[pos+1:pos+11]
        start = re.search('\$',string)
        end = re.search('\S',string)
        if (start!=None and end!=None): #If search fails somehow
            start=start.start()
            end=end.start()
            a=string[start+1:start+end-1]
            #print(a)
            a = convert_float(a)
            if (a!=-1.0): #If the float conversion fails somehow
                print(f"GDP/capita (PPP) of {country}: {a} dollars")
                # 在字典中插入資料
                GDP_PPP[country]=a
            else:
                print("**Could not find GDP/capita data!**")
        else:
            print("**Could not find GDP/capita data!**")
    else:
        print("**Could not find GDP/capita data!**")
print ("\nFinished finding all GDP/capita data")
複製程式碼

不要忘記使用 pandas inner/left join 方法

需要記住的一點是,所有這些分本分析都將產生具有略微不同的國家集的資料。因為不同的國家可能無法獲得不同型別的資料。人們可以使用一個 Pandas left join 來建立一個與所有可獲得/可以提取的所有資料片段的所有公共國家相交的資料。

df_combined = df_demo.join(df_GDP, how='left')
df_combined.dropna(inplace=True)
複製程式碼

啊,現在是很酷的東西,建模。。。但等等!還是先過濾吧!

在完成了所有的 HTML 解析、頁面爬取和文字挖掘後,現在您已經可以享受這些好處了--渴望執行迴歸演算法和很酷的視覺化指令碼!但是等等,在生成這些東西之前,通常您需要清洗您的資料(特別是針對這種社會經濟問題)。基本上,您需要過濾掉異常值,例如非常小的國家(比如島嶼國家),它們可能對您要繪製的引數值造成極大的偏差,且不遵循您想要研究的主要基本動態。對這些過濾器來說,幾行程式碼是很好的。可能有更多的 Pythonic 方法來實現他們,但我儘量保持它極其簡單且易於遵循。例如,下面的程式碼建立過濾器,將 GDP 小於五百億的小國拒之門外,低收入和高收入的界限分別為 5000 美元和 25000 美元(GDP/人均 GDP)。

# 建立過濾後的資料幀、x 和 y 陣列
filter_gdp = df_combined['Total GDP (PPP)'] > 50
filter_low_income=df_combined['GDP (PPP)']>5000
filter_high_income=df_combined['GDP (PPP)']<25000

df_filtered = df_combined[filter_gdp][filter_low_income][filter_high_income]
複製程式碼

最後是視覺化

我們使用 seaborn regplot 函式建立線性迴歸擬合的散點圖(網際網路使用者數量比上人均 GDP)和顯示 95% 置信區間帶。他們看起來就像下面一樣。可以將結果解釋為

一個國家的網際網路使用者數量與人均 GDP 之間存在著很強的正相關關係。此外,低收入/低 GDP 國家的相關強度明顯高於高 GDP 已開發國家。這可能意味著,與已開發國家相比,網際網路接入有助於低收入國家更快地增長,並更好地改善其公民的平均狀況

[譯] Web 爬蟲下的 Python 資料分析:中情局全球概況圖解

總結

本文通過一個 Python 筆記本演示來說明如何通過使用 BeautifulSoup 進行 HTML 解析來抓取用於下載原始資訊的網頁。在此基礎上,闡述瞭如何利用正規表示式模組來搜尋和提取使用者所需要的重要資訊。

最重要的是,它演示了在挖掘雜亂的HTML解析文字時,如何或為什麼不可能有簡單、通用的規則或程式結構。我們必須檢查文字結構,並設定適當的錯誤處理檢查,以便恰當地處理所有情況,以維護程式的流程(而不是崩潰),即使它無法提取所有這些場景的資料。

我希望讀者能從提供的筆記本檔案中獲益,並根據自己的需求和想象力在此基礎上構建。更多 Web 資料分析筆記 請檢視我的倉庫


如果您有任何問題和想法可以分享,請聯絡作者 tirthajyoti@gmail.com。當然您也可以檢視作者的 GitHub 倉庫中的 Python, R, 或者 MATLAB 和機器學習的資源。如果你像我一樣熱衷於機器學習/資料科學,請隨時在 LinkedIn 上新增我或者在 Twitter 上關注我。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章