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

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

在本文章中,我將展現如何使用 Python 和 HTML 解析從網頁中獲取有價值的信息,以後會回答一些重要的數據分析問題。

在數據科學項目中,數據採集和清洗幾乎老是最耗時、最麻煩的步驟。每一個人都喜歡用 3D 交互式圖表來構建一兩個很酷的深度神經網絡(或者 XGboost)模型,以此炫耀我的的技術。但這些模型是須要原始數據的,並且它們並不容易採集和清洗。html

畢竟生活不像 Kaggle 同樣是一個 zip 格式文件,等待您的解壓和建模 :-)前端

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

相信我,提出一個須要一些數據科學技術應用來解決的高質量問題並不難。並且每個這樣的問題都會成爲您的一個小項目,您能夠將它開源在 Gihub 這樣的平臺來和您的朋友們分享。即便您不是專業的數據專家,也沒有人能夠阻止您經過編寫很酷的代碼來回答一個高質量的數據問題。這也代表您是對數據敏感而且能夠用數據講故事的人。android

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

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

如今您能夠想到許多原始資料能夠採集來做爲回答此問題的數據。我發現中情局(是的 ‘AGENCY’)的一個網站保存了世界上全部國家的基本事實信息,是一個採集數據的好地方。github

所以咱們將使用如下 Python 模塊來構建咱們的數據庫和可視化,web

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

讓咱們討論一下解決這個數據科學問題的程序結構。整個項目代碼在個人 Github 倉庫中均可以找到。若是您喜歡的話,請 fork 或者給個 star。正則表達式

閱讀 HTML 首頁並傳遞給 BeautifulSoup

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

圖:中情局全球概況首頁

咱們使用一個帶有 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 是如何構造的。如今的通常狀況是,這將很難得到。特殊狀況下,快速檢查顯示了一個很是簡單而且有規律的結構,以澳大利亞截圖爲例。

這意味着有一個固定的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/人均數值。這是一個用數字說明的想法。

圖:文本分析圖示。

這個筆記本中還有其餘一些經常使用的表達方式,例如,無論這個數字是以數十億仍是數萬億美圓計算出來的,均可以正確地提取出 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 發達國家。這可能意味着,與發達國家相比,互聯網接入有助於低收入國家更快地增加,並更好地改善其公民的平均情況

總結

本文經過一個 Python 筆記本演示來講明如何經過使用 BeautifulSoup 進行 HTML 解析來抓取用於下載原始信息的網頁。在此基礎上,闡述瞭如何利用正則表達式模塊來搜索和提取用戶所須要的重要信息。

最重要的是,它演示了在挖掘雜亂的HTML解析文本時,如何或爲何不可能有簡單、通用的規則或程序結構。咱們必須檢查文本結構,並設置適當的錯誤處理檢查,以便恰當地處理全部狀況,以維護程序的流程(而不是崩潰),即便它沒法提取全部這些場景的數據。

我但願讀者能從提供的筆記本文件中獲益,並根據本身的需求和想象力在此基礎上構建。更多 Web 數據分析筆記 請查看個人倉庫


若是您有任何問題和想法能夠分享,請聯繫做者 tirthajyoti@gmail.com。固然您也能夠查看做者的 GitHub 倉庫中的 Python, R, 或者 MATLAB 和機器學習的資源。若是你像我同樣熱衷於機器學習/數據科學,請隨時在 LinkedIn 上添加我或者在 Twitter 上關注我。


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

相關文章
相關標籤/搜索