數據與智能 本公眾號關注大數據與人工智能技術。由一批具備多年實戰經驗的技術極客參與運營管理,持續輸出大數據、數據分析、推薦系統、機器學習、人工智能等方向的原創文章,每周至少輸出7篇精品原創。同時,我們會關注和分享大數據與人工智能行業動態。歡迎關注。
來源 | Data Science from Scratch, Second Edition
作者 | Joel Grus
譯者 | cloverErna
校對 | gongyouliu
編輯 | auroral-L
全文共6018字,預計閱讀時間40分鐘。
第九章 獲取數據
9.1 stdin 和 stdout
9.2 讀取文件
9.2.1 文本文件基礎
9.2.2 分割的(delimited)文件
9.3 網絡抓取
9.3.1 HTML和解析方法
9.3.2 例:密切關注國會
9.4 使用API
9.4.1 JSON(和XML)
9.4.2 使用無驗證的API
9.4.3 尋找API
9.5 案例:使用 Twitter API
9.6 延伸學習
寫作本書我用了三個月的時間;構思只用了三分鐘;而收集書中的數據,則用了我的一生。
——F. 斯科特 · 菲茲杰拉德
為了成為一名數據科學家,你需要數據。事實上,作為數據科學家,你會花很大一部分時間來獲取、清理和轉換數據。必要時,你總可以自己輸入數據(或者可以讓你的助手來做),但通常這樣做比較浪費時間。本章我們來看看利用 Python 獲取數據并得到正確格式的不同方法。
9.1 stdin和stdout
如果在命令行運行 Python 腳本,你可以用 sys.stdin 和 sys.stdout 以管道(pipe)方式傳遞數據。例如,以下腳本按行讀入文本,然后輸出與一個正則表達式匹配的行:
然后對收到的行計數并輸出計數結果:
你可以用這種方法來計數文件中有多少行包含數字。在 Windows 中,你可以用:
而在 Unix 系統中,你可以用:
“|”運算符是個管道字符,它的意思是“使用左邊命令的輸出作為右邊命令的輸入”。可以使用這種方法精心設計數據處理的管道。
注意
如果你使用的是 Windows,你可以在這行命令中去掉所包含的 python 部分:
如果你在Unix系統上,那么這樣做需要更多的步驟。首先添加一個“Shebang”作為腳本#!/usr/bin/env python的第一行。然后,在命令行中,使用chmod x egrep.py使文件可執行。
類似地,下面的這個腳本計算了單詞的數量并給出了最常用的單詞:
之后你可以做這樣的事情:
(如果你正在使用Windows,那么使用type而不是cat。)
注意
如果你是個 Unix 編程老手,你可能會很熟悉系統里許多內置的命令行工具 (比如 egrep),但你自己從零開始創建這些工具或許更好一些。畢竟,用到的時候剛好知道,總是好的。
9.2 讀取文件
可以顯式地用代碼來讀寫文件。用 Python 處理文件非常簡便。
9.2.1 文本文件基礎
處理文本文件的第一步是通過 open 命令來獲取一個文件對象:
因為非常容易忘記關閉文件,所以你應該在 with 程序塊里操作文件,這樣在結尾處文件會被自動關閉:
如果需要讀取一個完整的文本文件,可以使用 for 語句對文件的行進行迭代:
按這種方法得到的每一行會用換行符來結尾,所以在對讀入的行操作之前會經常需要用strip() 來進行處理。
例如,假設你有一個寫滿電子郵件地址的文件,每個地址一行,你想利用這個文件生成域名的直方圖。正確地提取域名的規則有些微妙--如公共后綴列表,但一個好的近似方案是只取出電子郵件地址中 @ 后面的部分。(對于像 joel@mail.
datasciencester.com 這樣的郵件地址,會給出錯誤的答案,但為了本例,我們愿意接受):
9.2.2 分割的(delimited)文件
我們剛剛處理的假想電子郵件地址文件每行只有一個地址。更常見的情況是你會處理每一行包含許多數據的文件。這種文件通常是用逗號分割或tab分割的:每一行有許多字段,用逗號或tab來表示一個字段的結束和另一個字段的開始。
這開始變得復雜了,各字段中帶有逗號、tab 和換行符(這是你不可避免地要處理的)。因為這個原因,幾乎總是會犯的一個錯誤是你自己嘗試去解析它們。相反,你應該使用Python的csv模塊(或者pandas庫,或其他一些設計用來讀取逗號分隔或制表符分隔的文件的庫)。
警告
不要自己解析逗號分隔的文件。 你會把一些特殊情況搞砸的!
如果文件沒有頭部(意味著你可能想把每一行作為一個列表,這帶來的麻煩是你需要知道每一列是什么),你可以使用 csv.reader 對行進行迭代,每一行都會被處理成恰當劃分的列表。
例如,如果有這樣一個用 tab 劃分的股票價格文件:
我們可以用下面的程序塊來處理:
如果文件存在頭部:
你既可以利用對 read.next() 的初始調用跳過頭部的行,也可以利用 csv.DictReader 把每一行讀成字典(把頭部作為關鍵字):
即使你的文件缺少頭部,你仍可以通過把關鍵字作為文件名參數傳輸來使用 DictReader。
同樣,你可以用 csv.writer 來寫限制的文件:
如果行中的各字段本身包含逗號,csv.writer 可以正確處理。但你自己手動寫成的則很可能不會正確處理。比如,如果你嘗試這樣做:
你最終會得到像下面這樣一個csv文件,它看起來像這樣:
沒人能看懂它的意思。
9.3 網絡抓取
另一種獲取數據的方法是從網頁抓取數據。事實證明,獲取網頁很容易;但從網頁上抓取有意義的結構化信息就不那么容易了。
9.3.1 HTML和解析方法
網絡上的頁面是由 HTML 寫成的,其中文本被(理想化地)標記為元素和它們的屬性:
在理想的情況下,所有的網頁按語義標記,這對我們很方便,我們可以使用類似這樣的規則來提取數據:找到 id 是 subject 的 <p> 元素并返回它所包含的文本。但在真實的世界中,HTML 并不總是具有很好的格式的,更不用說注解了。這意味著如果我們想搞清其含義,需要一些幫助。
為 了 從 HTML 里 得 到 數 據, 我 們 需 要 使 用 BeatifulSoup 庫,它對來自網頁的多種元素建立了樹結構,并提供了簡單的接口來獲取它們。本書寫作時,最新的版本是 Beatiful Soup 4.6.0,我們即將用到的就是這個版本。我們也會用到 requests 庫,它與內置在 Python 中的其他方法相比,是一種發起HTTP 請求的更好的方式。
Python 內置的 HTML 解析器是有點嚴格的,這意味著它并不總是能處理那些沒有很好地格式化的 HTML。因此,我們需要使用另外一種解析器,它需要先安裝:
確保你處于正確的虛擬環境中,安裝庫:
為了使用 Beatiful Soup,我們要把一些 HTML 傳遞給 BeautifulSoup() 函數。在我們的例子中,這些 HTML 是對 requests.get 進行調用的結果:
完成這個步驟之后,我們可以用一些簡單的方法得到完美的解析。
通常我們會處理一些 Tag 對象,它們對應于 HTML 頁面結構的標簽表示。
比如,找到你能用的第一個 <p> 標簽(及其內容):
可以對 Tag 使用它的 text 屬性來得到文本內容:
另外可以把標簽當作字典來提取其屬性:
可以一次得到多個標簽:
通常你會想通過一個類(class)來找到標簽:
此外,可以把這些方法組合起來運用更復雜的邏輯。比如,如果想找出包含在一個 <div>元素中的每一個 <span> 元素,可以這么做:
僅僅上述幾個特性就可以幫助我們做很多事。如果你需要做更復雜的事情(或僅僅是出于好奇),那就去查看文檔吧。
當然,無論多重要的數據,通常也不會標記成。你需要仔細檢查源HTML,通過你選擇的邏輯進行推理,并多考慮邊界情況來確保數據的正確性。接下來我們看一個例子。
9.3.2 例:密切關注國會
數據科學公司的政策副總裁擔心對數據科學行業的潛在監管,并要求你量化國會對這個話題的看法。他特別希望你能找到所有有發布“數據”新聞稿的代表。
在發布時,有一個頁面有所有代表網站的鏈接
如果你“查看來源”,所有網站的鏈接看起來都像:
讓我們從收集從該頁面鏈接到的所有URL開始:
這將返回太多的URL。如果你看它們,我們從http://或者https://開始,中間是某種名字,并以 .house.gov 或者 .house.gov/. 結束。
這是一個使用正則表達式的好地方:
這仍然太多,因為只有435名代表。如果你看一下清單,有很多重復。我們可以用set來克服這些問題:
總是有幾個眾議院的座位空著,或者可能有一些沒有網站的代表。無論如何,這已經足夠好了。當我們查看這些網站時,大多數網站都有新聞稿的鏈接。例如:
請注意,這是一個相對鏈接,這意味著我們需要記住原始站點。讓我們來抓取一下:
注意
通常情況下,像這樣隨意地爬一個網站是不禮貌的。 大多數網站都會有一個robots.txt文件,該文件表明你可以頻繁地抓取站點(以及你不應該抓取哪些路徑),但由于這是國會,我們不需要特別禮貌。
如果你通過滾動來查看它們,你將會看到大量/媒體/新聞稿和媒體中心/新聞稿,以及各種其他地址。其中一個URL是https://jayapal.house.gov/media/press-releases.
記住,我們的目標是找出哪些國會議員提到“數據”。“我們會寫一個稍微更通用的功能,檢查一頁新聞稿中是否提到任何給定的術語。
如果你訪問該網站并查看源代碼,似乎在<p>標簽中有來自每個新聞稿的片段,因此我們將使用它作為我們的第一次嘗試:
讓我們為它寫一個快速測試:
最后我們準備找到相關國會議員,并告知他們的姓名給政策副總裁:
當我運行這個時,我得到了大約20名代表的名單。你的結果可能會不同。
注意
如果你查看不同的“新聞稿”頁面,它們中的大多數都是分頁的,每頁只有5或10個新聞稿。 這意味著我們只檢索了每位國會議員最近的幾份新聞稿。 一個更徹底的解決方案將在頁面上迭代并檢索每個新聞稿的全文。
9.4 使用API
許多網站和網絡服務提供相應的應用程序接口(Application Programming Interface,APIS),允許你明確地請求結構化格式的數據。這省去了你不得不抓取數據的麻煩!
9.4.1 JSON(和XML)
因為 HTTP 是一種轉換文本的協議,你通過網絡 API 請求的數據需要序列化(serialized)地 轉 換 為 字 符 串 格 式。通 常 這 種 串 行 化 使 用JavaScript 對 象 符 號(JavaScript Object Notation,JSON)。JavaScript 對象看起來和 Python 的字典很像,使得字符串表達非常容易解釋:
我們可以使用 Python 的 json 模塊來解析 JSON。尤其是,我們會用到它的 loads 函數,這個函數可以把一個代表JSON對象的字符串反序列化(deserialize)為 Python 對象:
有時候 API 的提供者可能會不那么友好,只給你提供 XML 格式的響應:
我們也可以仿照從 HTML 獲取數據的方式,用 BeautifulSoup 從 XML 中獲取數據;更多細節可查閱文檔。
9.4.2 使用無驗證的API
現在大多數的 API 要求你在使用之前先驗證身份。而若我們不愿勉強自己屈就這種政策,API 會給出許多其他的陳詞濫調來阻止我們的瀏覽。因此,先來看一下 GitHub 的API,利用它我們可以做一些簡單的無需驗證的事情:
此處 repos 是一個 Python 字典的列表,其中每一個字典表示我的 GitHub 賬戶的一個代碼倉庫。(可以隨意替換成你的用戶名,以獲取你的代碼倉庫的數據。你有 GitHub 賬號,對吧?)
我們可以使用它來找出一周中最有可能創建哪些月份和天數的存儲庫。唯一的問題是,響應中的日期是字符串:
Python 本身沒有很強大的日期解析器,所以我們需要安裝一個:
其中你需要的可能只是 dateutil.parser.parse 函數:
類似地,你可以獲取我最后五個代碼倉庫所用的語言:
通常我們無需在“做出請求而且自己解析響應”這種低層次上使用 API。使用 Python 的好處之一是已經有人建好了庫,方便你訪問你感興趣的幾乎所有 API。這些庫可以把事情做好,為你省下查找 API 訪問的諸多冗長細節的麻煩。(如果這些庫不能很好地完成任務,或者它們依賴的是對應的 API 已失效的版本,那就會給你帶來巨大的麻煩。)
盡管如此,偶爾你還是需要操作你自己的 API 訪問庫(或者,更常見的,去調試別人不能順利操作的庫),所以了解一些細節是很有好處的。
9.4.3 尋找API
如果你需要一個特定網站的數據,可以查看它的開發者部分或 API 部分的細節,然后以關鍵詞“python <站點名> api”在網絡上搜索相應的庫。
有Yelp API、Instagram API、Spotify API等庫。
如果你想查看有 Python 封裝的 API 列表,那么在GitHub上有一個來自Real Python的不錯的API列表(https://github.com/realpython/list-of-python-api-wrappers)。
如果最終還是找不到你需要的 API,還是可以通過抓取獲得的。這是數據科學家最后的絕招。
9.5 案例:使用Twitter API
Twitter 是一個非常好的數據源。你可以從它得到實時的新聞,可以用它來判斷對當前事件的反應,可以利用它找到與特定主題有關的鏈接。使用 Twitter 可以做幾乎任何你能想到的事,只要你能獲得它的數據。可以通過它的 API 來獲得數據。
為了和 Twitter API 互動,我們需要使用 Twython 庫(python -m pip install twython)。實際上有很多 Python Twitter 的庫,但這一個是我用過的庫中最好用的一個。你也可以嘗試一下其他的庫。
獲取憑據
為了使用 Twitter 的 API,需要先獲取一些證明文件(為此你無論如何都要有一個 Twitter的賬戶,這樣你就能成為一個活躍友好的 Twitter #datascience 社區的一部分)。
注意
就像那些所有我不能控制的網站的指令一樣,它們會在某個時刻過時,但是現在還是能發揮一段時間的作用的。(盡管在我寫作本書的這段時間里,它們至少已經變更過一次了,所以祝你好運!)
以下是步驟:
1. 找到鏈接 https://apps.twitter.com/。
2. 如果你還沒有注冊,點擊“注冊”,并輸入你的 Twitter 用戶名和密碼。
3.單擊Apply申請開發人員帳戶。
4.請求訪問以供你自己使用。
5.填寫申請書。它需要填寫300字(真的)說明清楚你為什么需要訪問數據,所以為了通過審核,你可以告訴他們這本書以及你有多喜歡它。
6.等一段不確定的時間。
7.如果你認識在Twitter上工作的人,給他們發郵件,問他們是否可以加快你的申請。否則,請繼續等待。
8.獲得批準后,請返回到developer.twitter.com,找到“應用程序”部分,然后單擊“創建應用程序。”
9.填寫所有必需的字段(同樣,如果描述需要額外的字符,你可以討論這本書以及如何找到它)。
10.單擊“創建”。
現在你的應用程序應該有一個“鍵和令牌”選項卡,其中包含“消費者API公鑰”部分,其中列出了“API公鑰”和“API密鑰”。“注意這些鍵;你需要它們。(而且,對他們保密!它們就像是密碼一樣。)
小心
不要分享它們,不要把它們印在書里,也不要把它們記錄在 GitHub 公共代碼庫里。一種簡單的方法是把它們存儲在不會被簽入(checked in)的 credentials.json文件里,而且可以使用 json.loads 取回它們。另一個解決方案是將它們存儲在環境變量中,并使用os.environ檢索它們。
使用Twython
使用Twitter API最棘手的部分是認證。(事實上,這是使用大量API最棘手的部分。) API提供者希望確保你被授權訪問他們的數據,并且你不會超過他們的使用限制。他們還想知道誰在訪問他們的數據。
身份驗證有點令人痛苦。有一個簡單的方法,OAuth 2,當你只想做簡單的搜索時就足夠了。還有一種復雜的方式,OAuth 1,當你想要執行操作(例如推特)或(特別是對我們)連接到推特流時,這是必需的。
所以我們堅持了更復雜的方式,我們將盡可能多地實現自動化。
首先,你需要API公鑰和API密鑰(有時分別稱為消費公鑰和消費密鑰)。我可以從環境變量中獲得,如果你愿意的話,你可以隨時替換它們:
現在我們可以實例化客戶端:
提示
在這一點上,你可能想考慮把ACCESS_TOKEN和ACCESS_TOKEN_SECRET保存在安全的地方,這樣下一次你就不用經歷這嚴格的過程了。
一旦我們有了一個經過驗證的Twython實例,我們就可以開始執行搜索:
如果你運行上面這個,你應該得到一些推文,比如:
這并不那么有趣,主要是因為Twitter搜索API只是向你顯示了一些最近的結果。當你在做數據科學時,你經常想要很多推文。這就是流媒體API有用的地方。它允許你連接到一個偉大的Twitter“消防水管”。若要使用它,你需要使用訪問令牌進行身份驗證。
為了使用Twython訪問流API,我們需要定義一個從TwythonStreamer繼承并覆蓋它的on_success方法,也可能是它的on_error方法:
MyStreamer 會連接到 Twitter 流并等待 Twitter 給它發送數據。它每收到一些數據(在這里,一條推文表示為一個 Python 對象)就傳遞給 on_success 方法,如果推文是英文的,這個方法會把推文附加到 tweets 列表中,在收集到 1000 條推文后會斷開和流的連接。
剩下的工作就是初始化和啟動運行了:
它會一直運行下去直到收集 1000條推文為止(或直到遇到一個錯誤為止),此時就可以著手分析這些推文了。比如,你可以用下面的方法尋找最常見的標簽:
每條推文都包含許多數據。你可以自己嘗試一下各種方法,或仔細查閱 Twitter API 的文檔。
注意
在一個正式的項目中,你可能并不想依賴內存中的列表來存儲推文。相反,你可能想把推文保存在文件或者數據庫中,這樣就可以永久地擁有它們。
9.6 延伸學習
? pandas是數據科學用來處理(特別是導入)數據的一個主要的庫。
? Scrapy是一個功能很健全的庫,可用來構建更復雜的網絡抓取器,來執行類似跟蹤未知鏈接等任務。
? Kaggle擁有大量數據集。
用BeautifulSoup庫解析 HTML 或 XML 數據可以按照以下步驟進行:
首先,確保你已經安裝了BeautifulSoup庫。可以使用pip命令進行安裝:pip install beautifulsoup4。
導入BeautifulSoup庫和相關的解析庫,通常是html.parser或其他適合的解析器。
使用BeautifulSoup的parse方法將 HTML 或 XML 數據解析為一個BeautifulSoup對象。
通過find或find_all等方法在BeautifulSoup對象中查找特定的標簽或屬性。
對找到的元素進行進一步的操作,例如提取文本、獲取屬性值等。
下面是一個簡單的示例,演示如何使用BeautifulSoup解析 HTML 數據:
收起
python
from bs4 import BeautifulSoup
html_data = '''
The Dormouse's story
Once upon a time there were three little sisters; and their names were
Elsie,
Lacie and
Tillie;
and they lived at the bottom of a well.
'''
# 解析 HTML 數據
soup = BeautifulSoup(html_data, 'html.parser')
# 查找所有包含"sister"類的鏈接
sister_links = soup.find_all('a', class_='sister')
# 打印鏈接的文本和鏈接地址
for link in sister_links:
print(link.text, link.get('href'))
在上述示例中,首先定義了一段 HTML 數據。然后,使用BeautifulSoup的parse方法將 HTML 數據解析為一個soup對象。接下來,使用find_all方法查找所有具有sister類的鏈接,并將它們存儲在sister_links列表中。最后,通過遍歷sister_links列表,打印每個鏈接的文本和鏈接地址。
TML:完成頁面的內容展示
CSS:完成頁面樣式的控制,美化頁面,完成頁面的布局。
表單:用于采集用戶輸入的數據。用于和服務器進行交互。
form:用于定義表單的。可以定義一個范圍(代表用戶采集數據的范圍)
屬性:action:指定提交數據的url(指的就是把數據提交到哪里)
method:指定提交方式
分類:一共有7種,2種比較常用。
get:1.請求參數會在地址欄顯示
2.請求參數的長度是有限制的。
3.請求不安全
post:1.請求參數不會在地址欄顯示,會封裝在請求體中。
2.請求參數的長度沒有限制
3.較為安全
表單里面的數據要想被提交,必須指定它的name屬性
*請認真填寫需求信息,我們會在24小時內與您取得聯系。