《Python 網絡數據采集》第一部分筆記

第一部分 創建爬蟲

重點介紹網絡數據采集的基本原理 :

如何用 Python 從網絡服務器 請求信息,如何對服務器的響應進行基本處理,以及如何以自動化手段與網站進行交互。

思考“網絡爬蟲”時通常的想法:

? 通過網站域名獲取 HTML 數據
? 根據目標信息解析數據
? 存儲目標信息
? 如果有必要,移動到另一個網頁重復這個過程

第 1 章 初見網絡爬蟲

1.1 網絡連接

網絡數據采集需要拋開一些接口的遮擋,不僅是在瀏覽器層(它如何解釋所有的 HTML、CSS 和 JavaScript),有時也包括網絡連接層。

1.2 BeautifulSoup 簡介

1.2.1 安裝 BeautifulSoup

1.2.2 運行 BeautifulSoup

1.2.3 可靠的網絡連接

可能會發生兩種異常:
? 網頁在服務器上不存在(或者獲取頁面的時候出現錯誤)
? 服務器不存在

第 2 章 復雜 HTML 解析

2.1 不是一直都要用錘子

在開始解析網頁之前,看一些在解析復雜的 HTML 頁面時需要避免的問題。
? 尋找“打印此頁”的鏈接,或者看看網站有沒有 HTML 樣式更友好的移動版(把自己 的請求頭設置成處于移動設備的狀態,然后接收網站移動版)。
? 尋找隱藏在 JavaScript 文件里的信息。可能需要查看網頁加載的 JavaScript 文件。
? 雖然網頁標題經常會用到,但是這個信息也許可以從網頁的 URL 鏈接里獲取。
? 如果你要找的信息只存在于一個網站上,別處沒有,那你確實是運氣不佳。如果不只限 于這個網站,那么你可以找找其他數據源。有沒有其他網站也顯示了同樣的數據?網站
上顯示的數據是不是從其他網站上抓取后攢出來的?

2.2 再端一BeautifulSoup

什么時候使用 get_text() 與什么時候應該保留標簽?
.get_text() 會把你正在處理的 HTML 文檔中所有的標簽都清除,然后返回一個只包含文字的字符串。假如你正在處理一個包含許多超鏈接、段落和標 簽的大段源代碼,那么 .get_text() 會把這些超鏈接、段落和標簽都清除掉, 只剩下一串不帶標簽的文字。
用 BeautifulSoup 對象查找你想要的信息,比直接在 HTML 文本里查找信息要簡單得多。通常在你準備打印、存儲和操作數據時,應該最后才使 用 .get_text()。一般情況下,你應該盡可能地保留 HTML 文檔的標簽結構。

2.2.1 BeautifulSoup 的 find() 和 findAll()

BeautifulSoup 文檔里兩者的定義就是這樣:
findAll(tag, attributes, recursive, text, limit, keywords)
find(tag, attributes, recursive, text, keywords)
標簽參數 tag ——可以傳一個標簽的名稱或多個標簽名稱組成的 Python列表做標簽參數
屬性參數 attributes 是用一個 Python 字典封裝一個標簽的若干屬性和對應的屬性值。
遞歸參數 recursive 是一個布爾變量。
文本參數 text 有點不同,它是用標簽的文本內容去匹配,而不是用標簽的屬性。
范圍限制參數 limit,顯然只用于 findAll 方法。find 其實等價于 findAll 的 limit 等于 1 時的情形。
關鍵詞參數 keyword,可以讓你選擇那些具有指定屬性的標簽。

2.2.2 其他 BeautifulSoup 對象

? BeautifulSoup 對象
? 標簽 Tag 對象
? NavigableString 對象
用來表示標簽里的文字,不是標簽(有些函數可以操作和生成 NavigableString 對象, 而不是標簽對象)。
? Comment 對象
用來查找 HTML 文檔的注釋標簽,

2.2.3 導航樹

  1. 處理子標簽和其他后代標簽
    所有的子標簽都是后代標 簽,但不是所有的后代標簽都是子標簽。
    一般情況下,BeautifulSoup 函數總是處理當前標簽的后代標簽。
    如果你只想找出子標簽,可以用 .children 標簽。

  2. 處理兄弟標簽
    BeautifulSoup 的 next_siblings() 函數可以讓收集表格數據成為簡單的事情,尤其是處理帶標題行的表格

from urllib.request import urlopen
from bs4 import BeautifulSoup


html = urlopen("http://www.pythonscraping.com/pages/page3.html") 
bsObj = BeautifulSoup(html)

for sibling in bsObj.find("table",{"id":"giftList"}).tr.next_siblings: 
    print(sibling)

這段代碼會打印產品列表里的所有行的產品,第一行表格標題除外。為什么標題行被跳過 了呢?有兩個理由。
首先,對象不能把自己作為兄弟標簽。任何時候你獲取一個標簽的兄弟標簽,都不會包含這個標簽本身。其次,這個函數只調用后面的兄弟標簽。例如,如果我們選擇一組標簽中位于中間位置的一個標簽,然后用 next_siblings() 函數,那么它就只會返回在它后面的兄弟標簽。
和 next_siblings 一樣,如果你很容易找到一組兄弟標簽中的最后一個標簽,那么 previous_siblings 函數也會很有用。
當然,還有 next_sibling 和 previous_sibling 函數,與 next_siblings 和 previous_siblings 的作用類似,只是它們返回的是單個標簽,而不是一組標簽。

  1. 父標簽處理
    在抓取網頁的時候,查找父標簽的需求比查找子標簽和兄弟標簽要少很多。通常情況 下,如果以抓取網頁內容為目的來觀察 HTML 頁面,我們都是從最上層標簽開始的,然 后思考如何定位我們想要的數據塊所在的位置。但是,偶爾在特殊情況下你也會用到 BeautifulSoup 的父標簽查找函數,parent 和 parents。

2.3 正則表達式

嘗試正則表達式
在學習書寫正則表達式的時候,做一些實驗感受一下它們如何工作,這是至關重要的。
如果你不想打開代碼編輯器,寫完再運行程序檢查正則表達式的運行是否符合預期,那么你可以去 RegexPal(http://regexpal.com/)這類網站上在線測試 正則表達式。
正則表達式在實際中的一個經典應用是識別郵箱地址。雖然不同郵箱服務器的郵箱地址的具體規則不盡相同,但是我們還是可以創建幾條通用規則。每條規則對應的正則表達式如下表第 2 列所示。

規 則 正則表達式
1. 郵箱地址的第一部分至少包括一種內容:大寫字母、小寫字母、數字 0~9、點號(.)、加號 (+)或下劃線(_) [A-Za-z0-9._+]+:這個正則表達式簡寫非常智慧。例 如,它用“A-Z”表示“任意 A~Z 的大寫字母”。把 所有可能的序列和符號放在中括號(不是小括號)里 表示“括號中的符號里任何一個”。要注意后面的加 號,它表示“這些符號都可以出現多次,且至少出現 1 次”
2. 之后,郵箱地址會包含一個 @ 符號 @:這個符號很直接。@ 符號必須出現在中間位置, 有且僅有 1 次
3. 在符合 @ 之后,郵箱地址還必須至少包含一個大寫或小寫字母 [A-Za-z]+:可能只在域名的前半部分、符號 @ 后面 用字母。而且,至少有一個字母
4. 之后跟一個點號(.) \.:在域名前必須有一個點號(.)
5. 最后郵箱地址用 com、org、edu、net 結尾 (實際上,頂級域名有很多種可能,但是作為示例演示這四個后綴夠用了)。 (com|org|edu|net): 這樣列出了郵箱地址中可能出現在點號之后的字母序列

把上面的規則連接起來,就獲得了完整的正則表達式:

[A-Za-z0-9\._+]+@[A-Za-z]+\.(com|org|edu|net)

當我們動手開始寫正則表達式的時候,最好先寫一個步驟列表描述出你的目標字符串結構。 還要注意一些細節的處理。
表 2-1 用簡單的說明和例子列舉了正則表達式的一些常用符號。這個列表并不是全部符 號,另外就像之前所說的,可能在不同編程語言中會遇到一些變化。但是,這 12 個符號 是 Python 的正則表達式中最常用的,可以用來查找和收集絕大多數數據類型。

表2-1:正則表達式常用符號

符 號 含 義 例 子 匹配結果
* 匹配前面的字符、子表達式或括號里的字符 0 次或多次 a*b* aaaaaaaa,aaabbbbb, bbbbbb
+ 匹配前面的字符、子表達式或括號里的字符至少 1次 a+b+ aaaaaaab,aaabbbbb, abbbbbb
[] 匹配任意一個字符(相當于“任選一個”) [A-Z]* APPLE,CAPIT ALS, QWERTY
() 表達式編組(在正則表達式的規則里編組會優先運行) (a*b)* aaabaab,abaaab, ababaaaaab
{m,n} 匹配前面的字符、子表達式或括號里的字符 m 到 n 次(包含 m 或 n) a{2,3}b{2,3} aabbb,aaabbb,aabb
[^] 匹配任意一個不在中括號里的字符 [^A-Z]* apple,lowercase, qwerty
| 匹配任意一個由豎線分割的字符、子表達式(注 意是豎線,不是大字字母 I) b(a|i|e)d bad,bid,bed
. 匹配任意單個字符(包括符號、數字和空格等) b.d bad,bzd,b$d,b d
^ 指字符串開始位置的字符或子表達式 ^a apple,asdf,a
\ 轉義字符(把有特殊含義的字符轉換成字面形式) \.\ | \\ .| \
$ 經常用在正則表達式的末尾,表示“從字符串的末端匹配”。如果不用它,每個正則表達式實際都 帶著“.*”模式,只會從字符串開頭進行匹配。這 個符號可以看成是 ^ 符號的反義詞 [A-Z]*[a-z]*$ ABCabc,zzzyx,Bob
?! “不包含”。這個奇怪的組合通常放在字符或正則表達式前面,表示字符不能出現在目標字符串里。 這個符號比較難用,字符通常會在字符串的不同部位出現。如果要在整個字符串中全部排除某個字符,就加上 ^ 和 $ 符號 ^((?![A-Z]).)*$ no-caps-here,$ymb0ls a4e f!ne

2.4 正則表達式和BeautifulSoup

正則表達式可以作為 BeautifulSoup 語句的任意一個參數,讓你的目標元素查找工作極具靈 活性。

2.5 獲取屬性

對于一個標簽對象,可以用下面的代碼獲取它的全部屬性:

    myTag.attrs

要注意這行代碼返回的是一個 Python 字典對象,可以獲取和操作這些屬性。比如要獲取圖 片的資源位置 src,可以用下面這行代碼:

    myImgTag.attrs["src"]

2.6 Lambda 表達式

BeautifulSoup 允許我們把特定函數類型當作 findAll 函數的參數。唯一的限制條件是這些 函數必須把一個標簽作為參數且返回結果是布爾類型。BeautifulSoup 用這個函數來評估它 遇到的每個標簽對象,最后把評估結果為“真”的標簽保留,把其他標簽剔除。
例如,下面的代碼就是獲取有兩個屬性的標簽:

     soup.findAll(lambda tag: len(tag.attrs) == 2)

這行代碼會找出下面的標簽:

<div class="body" id="content"></div>
<span style="color:red" class="title"></span>

如果你愿意多寫一點兒代碼,那么在 BeautifulSoup 里用 Lambda 表達式選擇標簽,將是正 則表達式的完美替代方案。

2.7 超越 BeautifulSoup

如果 BeautifulSoup 不能滿足你的需求,你可以看看其他的庫。
? lxml
這個庫(http://lxml.de/)可以用來解析 HTML 和 XML 文檔,以非常底層的實現而聞名于世,大部分源代碼是用 C 語言寫的。雖然學習它需要花一些時間(其實學習曲線越陡峭,表明你可以越快地學會它),但它在處理絕大多數 HTML 文檔時速度都非常快。
? HTML parser
這是 Python 自帶的解析庫(https://docs.python.org/3/library/html.parser.html)。因為它不用安裝(只要裝了 Python 就有),所以可以很方便地使用。

第 3 章 開始采集

3.1 遍歷單個域名

3.2 采集整個網站

深網和暗網
深網是網絡的一部分,與淺網(surface Web)對立。淺網是互聯網上搜索引擎可以抓 到的那部分網絡。據不完全統計,互聯網中其實約 90% 的網絡都是深網。因為谷歌不能做像表單提交這類事情,也找不到那些沒有直接鏈接到頂層域名上的網頁,或者因為有 robots.txt 禁止而不能查看網站,所以淺網的數量相對深網還是比較少的。
暗網,也被稱為 Darknet 或 dark Internet,完全是另一種“怪獸”。它們也建立在已有 的網絡基礎上,但是使用 Tor 客戶端,帶有運行在 HTTP 之上的新協議,提供了一個 信息交換的安全隧道。
和暗網不同,深網是相對容易采集的。實際上,本書的很多工具都是在教你如何采集那些 Google 爬蟲機器人不能獲取的深網信息。
遍歷整個網站的網絡數據采集有許多好處。
? 生成網站地圖
? 收集數據
關于遞歸的警告
如果遞歸運 行的次數非常多,前面的遞歸程序就很可能崩潰。
Python 默認的遞歸限制(程序遞歸地自我調用次數)是 1000 次。

3.3 通過互聯網采集

在你寫爬蟲隨意跟隨外鏈跳轉之前,請問自己幾個問題。
? 我要收集哪些數據?這些數據可以通過采集幾個已經確定的網站(永遠是最簡單的做法) 完成嗎?或者我的爬蟲需要發現那些我可能不知道的網站嗎?
? 當我的爬蟲到了某個網站,它是立即順著下一個出站鏈接跳到一個新網站,還是在網站上呆一會兒,深入采集網站的內容?
? 有沒有我不想采集的一類網站?我對非英文網站的內容感興趣嗎?
? 如果我的網絡爬蟲引起了某個網站網管的懷疑,我如何避免法律責任?


采集外鏈的程序流程圖
收集內鏈和外鏈的程序流程圖

寫代碼之前擬個大綱或畫個流程圖是很好的編程習慣,這么做不僅可以為你后期處理節省 很多時間,更重要的是可以防止自己在爬蟲變得越來越復雜時亂了分寸。
處理網頁重定向
重定向(redirect)允許一個網頁在不同的域名下顯示。重定向有兩種形式:
? 服務器端重定向,網頁在加載之前先改變了 URL;
? 客戶端重定向,有時你會在網頁上看到“10 秒鐘后頁面自動跳轉到......”之類的消息,
表示在跳轉到新 URL 之前網頁需要加載內容。
本節處理的是服務器端重定向的內容。更多關于客戶端重定向的細節,通常用 JavaScript 或 HTML 來實現,請看第 10 章。
服務器端重定向,你通常不用擔心。如果你在用 Python 3.x 版本的 urllib 庫,它會自動處理重定向。不過要注意,有時候你要采集的頁面的 URL 可能并不是你當前所在頁面的 URL。

3.4 用 Scrapy 采集

Scrapy 就是一個幫你大幅度降低網頁鏈接查找和識別工作復雜度的 Python 庫,它可以讓你輕松地采集一個或多個域名的信息。
Scrapy 日志處理
Scrapy 生成的調試信息非常有用,但是通常太羅嗦。你可以在 Scrapy 項目中
的 setting.py 文件中設置日志顯示層級: LOG_LEVEL = 'ERROR'
Scrapy 日志有五種層級,按照范圍遞增順序排列如下:
? CRITICAL
? ERROR
? WARNING
? DEBUG
? INFO
如果日志層級設置為 ERROR,那么只有 CRITICAL 和 ERROR 日志會顯示出來。
如果日志層級設置為 INFO,那么所有信息都會顯示出來,其他同理。 日志不僅可以顯示在終端,也可以通過下面命令輸出到一個獨立的文件中:

$ scrapy crawl article -s LOG_FILE=wiki.log

如果目錄中沒有 wiki.log,那么運行程序會創建一個新文件,然后把所有的日志都保存到里面。如果已經存在,會在原文后面加入新的日志內容。
Scrapy 用 Item 對象決定要從它瀏覽的頁面中提取哪些信息。Scrapy 支持用不同的輸出格式來保存這些信息,比如 CSV、JSON 或 XML 文件格式,對應命令如下所示:

      $ scrapy crawl article -o articles.csv -t csv
      $ scrapy crawl article -o articles.json -t json
      $ scrapy crawl article -o articles.xml -t xml

當然,你也可以自定義 Item 對象,把結果寫入你需要的一個文件或數據庫中,只要在爬蟲的 parse 部分增加相應的代碼即可。
Scrapy 是處理網絡數據采集相關問題的利器。它可以自動收集所有 URL,然后和指定的規則進行比較;確保所有的 URL 是唯一的;根據需求對相關的 URL 進行標準化;以及到更 深層的頁面中遞歸查找。

第 4 章 使用 API

一般情況下,程序員可以用 HTTP 協議向 API 發起請求以獲取某種信息,API 會用 XML (eXtensible Markup Language, 可擴展標記語言 ) 或 JSON(JavaScript Object Notation, JavaScript 對象表示)格式返回服務器響應的信息。盡管大多數 API 仍然在用 XML,但是
JSON 正在快速成為數據編碼格式的主流選擇。

4.1 API 概述

想要體育 信息? ESPN 提供的 API 包括運動員信息、比賽分數等。
Google 的開發者社區也提供了一堆 API 用于獲取語言翻譯、分析、地理位置等 信息。
究竟 API 和普通的網址訪問有什么區別呢?
如果不考慮 API 高大上的名稱,其實兩者沒啥區別。API 可以通過 HTTP 協議下載文件,和 URL 訪問網站獲取數據的協議一 樣,它幾乎可以實現所有在網上干的事情。API 之所以叫 API 而不是叫網站的原因,其實 是首先 API 請求使用非常嚴謹的語法,其次 API 用 JSON 或 XML 格式表示數據,而不是 HTML 格式。

4.2 API 通用規則

API 用一套非常標準的規則生成數據,而且生成的數據也是按照非常標準的方式組織的。
第一次使用一個 API 時,建議閱讀文檔

4.2.1 方法

利用 HTTP 從網絡服務獲取信息有四種方式:
? GET
? POST
? PUT
? DELETE
GET 就是你在瀏覽器中輸入網址瀏覽網站所做的事情。當你訪問 http://freegeoip.net/ json/50.78.253.58 時,就會使用 GET 方法。可以想象成 GET 在說:“喂,網絡服務器,請按照這個網址給我信息。”
POST 基本就是當你填寫表單或提交信息到網絡服務器的后端程序時所做的事情。每次當你登錄網站的時候,就是通過用戶名和(有可能加密的)密碼發起一個 POST 請求。如果你用 API 發起一個 POST 請求,相當于說“請把信息保存到你的數據庫里”。
PUT 在網站交互過程中不常用,但是在 API 里面有時會用到。PUT 請求用來更新一個對象或信息。例如,API 可能會要求用 POST 請求創建新用戶,但是如果你要更新老用戶的郵箱 地址,就要用 PUT 請求了。
DELETE 用于刪除一個對象。例如,如果我們向 http://myapi.com/user/23 發出一個 DELETE 請 求,就會刪除 ID 號是 23 的用戶。DELETE 方法在公共 API 里面不常用,它們主要用于創建信息,不能隨便讓一個用戶去刪掉數據庫的信息。但是,和 PUT 方法一樣,DELETE 方法也值得了解一下。
雖然在 HTTP 規范里還有一些信息處理方式,但是這四種基本是你使用 API 過程中可能遇到的全部。

4.2.2 驗證

有些 API 要求客戶驗證是為了計算 API 調用的費用,或者是提供了包月的服務。
有些驗證是為了“限制”用戶使用 API(限制每秒鐘、每小時或每天 API 調用的次數),或者是限 制一部分用戶對某種信息或某類 API 的訪問。
還有一些 API 可能不要求驗證,但是可能會為了市場營銷而跟蹤用戶的使用行為。
通常 API 驗證的方法都是用類似令牌(token)的方式調用,每次 API 調用都會把令牌傳遞到服務器上。這種令牌要么是用戶注冊的時候分配給用戶,要么就是在用戶調用的時候才提供,可能是長期固定的值,也可能是頻繁變化的,通過服務器對用戶名和密碼的組合處理后生成。
令牌除了在 URL 鏈接中傳遞,還會通過請求頭里的 cookie 把用戶信息傳遞給服務器。

4.3 服務器響應

API 有一個重要的特征是它們會反饋格式友好的數據。大多數反饋的數據格式都是 XML 和 JSON。
這幾年,JSON 比 XML 更受歡迎,主要有兩個原因。
首先,JSON 文件比完整的 XML 格式小。
比如下面的 XML 數據用了 98 個字符:

<user><firstname>Ryan</firstname><lastname>Mitchell</lastname><username>Kludgist</username></user>

同樣的 JSON 格式數據:

{"user":{"firstname":"Ryan","lastname":"Mitchell","username":"Kludgist"}}

只要用 73 個字符,比表述同樣內容的 XML 文件要小 36%。 當然有人可能會說,XML 也可以表示成這種形式:

<user firstname="ryan" lastname="mitchell" username="Kludgist"></user>

不過這么做并不好,因為它不支持深層嵌入數據。而且它也用了 71 個字符,和 JSON 差 不多。
JSON 格式比 XML 更受歡迎的另一個原因是網絡技術的改變。過去,服務器端用 PHP 和 .NET 這些程序作為 API 的接收端?,F在,服務器端也會用一些 JavaScript 框架作為 API 的發送和接收端,像 Angular 或 Backbone 等。雖然服務器端的技術無法預測它們即將收到的數據格式,但是像 Backbone 之類的 JavaScript 庫處理 JSON 比處理 XML 要更簡單。

API調用

不同 API 的調用語法大不相同,但是有幾條共同準則。
當使用 GET 請求獲取數據時,用 URL 路徑描述你要獲取的數據范圍,查詢參數可以作為過濾器或附加請求使用。
例如,下面這個虛擬的 API,可以獲取 ID 是 1234 的用戶在 2014 年 8 月份發表的所有博文:

    http://socialmediasite.com/users/1234/posts?from=08012014&to=08312014

有許多 API 會通過文件路徑(path)的形式指定 API 版本、數據格式和其他屬性。例如,下面的鏈接會返回同樣的結果,但是使用虛擬 API 的第四版,反饋數據為 JSON 格式:

    http://socialmediasite.com/api/v4/json/users/1234/posts?from=08012014&to=08312014

還有一些 API 會通過請求參數(request parameter)的形式指定數據格式和 API 版本:

    http://socialmediasite.com/users/1234/posts?format=json&from=08012014&to=08312014

4.4 Echo Nest

The Echo Nest 音樂數據網站是一個用網絡爬蟲建立的超級給力的企業級案例。雖然像 Pandora 之類的音樂公司都是通過人工干預完成音樂的分類與說明,但是 The Echo Nest 是通過自動智能技術,以及博客與新聞信息的采集,來完成藝術家、歌曲和專輯的分類工作的。
更給力的是,它的 API 可以經非商業用途免費使用。使用 API 得有一個 key,你可以在 The Echo Nest 的注冊頁面填入名稱、郵箱和用戶名來注冊賬號。
The Echo Nest 的 API 的響應結果由四個部分組成:藝術家(artist)、歌曲(song)、專輯 (track)和風格(genre)。除了風格之外,所有信息都帶有唯一的 ID 號,可以通過 API 調 用把信息展示成不同的形式。假如我想獲取 Monty Python 喜劇樂團的歌曲,可以用下面的
鏈接獲取歌曲的 ID(記得把 < 你的 api_key> 替換成你自己的 API key):

    http://developer.echonest.com/api/v4/artist/search?api_key=<你的api_key>&name=monty%20python

響應的結果是:

    {"response": {"status": {"version": "4.2", "code": 0, "message": "Suc cess"},
     "artists": [{"id": "AR5HF791187B9ABAF4", "name": "Monty Pytho n"}, 
     {"id": "ARWCIDE13925F19A33", "name": "Monty Python's SPAMALOT"},
     {"id": "ARVPRCC12FE0862033", "name": "Monty Python's Graham Chapman" }]}}

具體文檔請參考 The Echo Nest API 概述。
The Echo Nest 資助了很多技術與音樂交叉領域的黑客松項目(hackathon,也叫黑客馬拉 松、編程馬拉松)和編程項目。如果你想從中獲取靈感,The Echo Nest 示例頁面是一個好的起點。

4.5 Twitter API

Twitter 的 API 請求限制有兩種方法:每 15 分鐘 15 次和每 15 分鐘 180 次,由請求類型決定。比如你可以 1 分鐘獲取 12 次(每 15 分鐘 180 次的平均數)Twitter 用戶基本信息,但是 1 分鐘只能獲取 1 次(每 15 分鐘 15 次的平均數)這些用戶的關注者(follower)。

4.5.1 開始

除了流量限制,Twitter 的 API 驗證方式也比 The Echo Nest 要復雜,既要有 API 的 key,
也要用其他 key。要獲取 API 的 key,你需要注冊一個 Twitter 賬號;可以在注冊頁面直接注冊。另外,還需要在 Twitter 的開發者網站注冊一個新應用。 完成注冊之后,你會在一個新頁面看到你應用的基本信息,包括自定義的 key。
如果你單擊“manage keys and access tokens”頁面,就會跳轉到一個包含更多信息的頁面上。
這個頁面還包括一個自動生成加密 key 的按鈕,可以使得應用被公開訪問。

4.5.2 幾個示例

Twitter 的驗證系統用 OAuth 驗證,非常復雜;最好找一個成熟穩定的 Python 庫來處理它,不要自己從頭寫代碼來實現。因為手工處理 Twitter 的 API 是非常復雜的工作,所以本節內容的重點是用 Python 代碼來實現 API 的交互,不是親手實現這個 API。
最好的一個 Python Twitter 庫(名字也叫 Twitter)。你可以從 Python Twitter Tools(PTT) 網站下載并安裝這個庫( pip 安裝也可以,pip install twitter):

     $cd twitter-x.xx.x
     $python setup.py install

Twitter 訪問權限
應用的默認訪問權限(credential permissions)是只讀(read-only)模式,除了讓你的應用發推文之外,這樣的權限可以滿足大部分需求。
如果想把令牌的權限改成讀 / 寫(read/write)模式,你可以在 Twitter 的應用控制面板的權限欄進行修改。改變權限后令牌會重新生成。
如果有需要你也可以更新應用的令牌權限,用它登錄你的 Twitter 賬號直接收發推文。不過要注意信息安全。通常,應該對不同的應用授予不同的權 限,而不是給那些不需要太多權限的應用過多的訪問權限。
完整的文檔請在 GitHub 上查看。

4.6 Google API

Google 是目前為網民提供最全面、最好用的網絡 API 套件(collection)的公司之一。無論你想處理哪種信息,包括語言翻譯、地理位置、日歷,甚至基因數據,Google 都提供了 API。Google 還為它的一些知名應用提供 API,比如 Gmail、YouTube 和 Blogger 等。
查看 Google API 有兩種方式。一種方式是通過產品頁面查看,里面有許多 API、軟件開發工具包,以及其他軟件開發者感興趣的項目。 另一種方式是 API 控制臺,里面提供了方便的接口來開啟和關閉 API 服務,查看流量限制和使用情況,還可以和 Google 強大的云計算平臺的開發實例結合使用。
Google 的大多數 API 都是免費的,不過有些需要付費,比如搜索 API 需要一個付費的授權。Google 的免費 API 套件對普通版的賬號也是非常慷慨的,允許每天進行 250 次到 20 000 000 次的訪問。還有一些 API 可以通過驗證信用卡提高流量上限(不需要支付費用)。比如,Google 的地點查詢 API 每 24 小時的流量限制是 1000 次,但是如果你通過了信用卡驗證,就可以提高到 150 000 次。更多的信息請參考 Google 的 API 使用限額和計費方式頁面。

4.6.1 開始

如果你有 Google 賬號,可以查看自己可用的 API 列表,并通過 Google 開發者控制臺創建 API 的 key。如果你沒有 Google 賬號,請在創
Google 賬號頁面建立自己的賬號。
當你登錄賬號或賬號創建完成后,就能在 API 控制臺頁面看到一些賬號信息,包含 API 的 key。
在憑證頁面,你可以單擊“Create new Key”按鈕創建新的 API key。為了你的賬號安全,建議限制 API 使用的 IP 地址或 URL 鏈接。你也可以創建一個可用于任意 IP 地址或 URL 的 API key,只要把“Accept Request From These Server IP Addresses”(接受這些服務器 IP 地址發出的請求)這一欄空著就行了。但是,請記住保證 API key 的安全性是非常重要的事情,如果你不限制允許使用 API 的 IP 地址——任何使用你的 API key 調用你的 API 都算成是你的消費,即使你并不知情。
你也可以建立多個 API key。比如,你可以為每個項目都分配一個單獨的 API key,也可以為每個網站域名都分配一個 API key。但是,Google 的 API 流量是按照每個賬號分配的,不是按照每個 key 分配的,所以這樣做雖然可以方便地管理 API 權限,但是并不會提高你的可用流量!

4.6.2 幾個示例

Google 最受歡迎的(個人認為也是最有趣的)API 都在 Google 地圖 API 套件中。你可能見過很多網站都在用嵌入式 Google 地圖,覺得自己對這類功能很熟悉。但是,地圖 API 遠比嵌入式地圖的功能豐富得多——你可以把街道地址解析成經 / 緯度(longitude/latitude) 坐標值,地球上任意點的海拔高度,做出基于位置的可視化圖形,獲取任意位置的時區信息,以及其他一些地圖相關的事情。

4.7 解析 JSON 數據

用過 freegeoip.net 網站 IP 查詢的例子,可以把 IP 地址解析轉換成地
http://freegeoip.net/json/50.78.253.58
我可以獲取這個請求的反饋數據,然后用 Python 的 JSON 解析函數來解碼:

import json
from urllib.request import urlopen


def getCountry(ipAddress):
    response = urlopen("http://freegeoip.net/json/"+ipAddress).read()
                           .decode('utf-8')
    responseJson = json.loads(response)

return responseJson.get("country_code") print(getCountry("50.78.253.58"))

這段代碼可以打印出 IP 地址為 50.78.253.58 的國家代碼。
這里用的 JSON 解析庫是 Python 標準庫的一部分。只需要在代碼開頭寫上 import json, 不同于那些需要先把 JSON 解析成一種 JSON 對象或 JSON 節點的語言,Python 使用了一種更加靈活的方式,把 JSON 轉換成字典,JSON 數組轉換成列表, JSON 字符串轉換成 Python 字符串。通過這種方式,就可以讓 JSON 的獲取和操作變得非常簡單。

4.8 回到主題

Python 的集合類型簡介
到現在為止,我用已經用過兩個 Python 的數據結構來儲存不同類型的數據:列表和詞典。已經有了兩種數據類型,為什么還要用集合(set)?
Python 的集合是無序的,就是說你不能用位置來獲得集合元素對應的值。數據加入集合的順序,和你重新獲取它們的順序,很可能是不一樣的。如果你要存儲一個已有的值到集合中,集合會自動忽略它。
對于未來可能需要擴展的代碼,在決定使用集合還是列表時,有兩件事情需要考慮: 雖然列表迭代速度比集合稍微快一點兒,但集合查找速度更快(確定一個對象是否在集合中),因為 Python 集合就是值為 None 的詞典,用的是哈希表結構,查詢速度為 O(1)。

4.9 再說一點 API

Leonard Richardson、Mike Amundsen 和 Sam Ruby 的 RESTful Web APIs為網絡 API 的用法提供了非常全面的理論與實踐指導。另 外,Mike Amundsen 的精彩視頻教學課程 Designing APIs for the Web,也可以教你創建自己的 API。如果你想把自己采集的數據用一種便捷的方式分享出來,他的視頻非常有用。

第 5 章 存儲數據

5.1 媒體文件

存儲媒體文件有兩種主要的方式:只獲取文件 URL 鏈接,或者直接把源文件下載下來。
你可以通過媒體文件所在的 URL 鏈接直接引用它。
這樣做的優點如下。
? 爬蟲運行得更快,耗費的流量更少,因為只要鏈接,不需要下載文件。
? 可以節省很多存儲空間,因為只需要存儲 URL 鏈接就可以。
? 存儲 URL 的代碼更容易寫,也不需要實現文件下載代碼。
? 不下載文件能夠降低目標主機服務器的負載。

不過這么做也有一些缺點。
? 這些內嵌在你的網站或應用中的外站 URL 鏈接被稱為盜鏈(hotlinking),使用盜鏈可 能會讓你麻煩不斷,每個網站都會實施防盜鏈措施。
? 因為你的鏈接文件在別人的服務器上,所以你的應用就要跟著別人的節奏運行了。
? 盜鏈是很容易改變的。如果你把盜鏈圖片放在博客上,要是被對方服務器發現,很可能被惡搞。如果你把 URL 鏈接存起來準備以后再用,可能用的時候鏈接已經失效了,或者是變成了完全無關的內容。
? 現實中的網絡瀏覽器不僅可以請求 HTML 頁面并切換頁面,它們也會下載訪問頁面上所有的資源。下載文件會讓你的爬蟲看起來更像是人在瀏覽網站,這樣做反而有好處。

在 Python 3.x 版本中,urllib.request.urlretrieve 可以根據文件的 URL 下載文件:

from urllib.request import urlretrieve 
from urllib.request import urlopen 
from bs4 import BeautifulSoup


html = urlopen("http://www.pythonscraping.com")
bsObj = BeautifulSoup(html)
imageLocation = bsObj.find("a", {"id": "logo"}).find("img")["src"]
urlretrieve (imageLocation, "logo.jpg")

這段程序從 http://pythonscraping.com 下載 logo 圖片,然后在程序運行的文件夾里保存為 logo.jpg 文件。
程序運行注意事項
你知道從網上下載未知文件的那些警告嗎?這個程序會把頁面上所有的文件下載到你的硬盤里,可能會包含一些 bash 腳本、.exe 文件,甚至可能是惡意 軟件(malware)。
如果你之前從沒有運行過任何下載到電腦里的文件,電腦就是安全的嗎?尤其是當你用管理員權限運行這個程序時,你的電腦基本已經處于危險之中。 如果你執行了網頁上的一個文件,那個文件把自己傳送到了 ../../../../usr/bin/ python 里面,會發生什么呢?等下一次你再運行 Python 程序時,你的電腦就可能會安裝惡意軟件。

5.2 把數據存儲到 CSV

CSV(Comma-Separated Values,逗號分隔值)是存儲表格數據的常用文件格式。Microsoft
Excel 和很多應用都支持 CSV 格式,因為它很簡潔。
和 Python 一樣,CSV 里留白(whitespace)也是很重要的:每一行都用一個換行符分隔, 列與列之間用逗號分隔(因此也叫“逗號分隔值”)。CSV 文件還可以用 Tab 字符或其他字符分隔行,但是不太常見,用得不多。
Python 的 csv 庫可以非常簡單地修改 CSV 文件,甚至從零開始創建一個 CSV 文件:

import csv


csvFile = open("../files/test.csv", 'w+') 
try:
    writer = csv.writer(csvFile)
    writer.writerow(('number', 'number plus 2', 'number times 2')) 
    for i in range(10):
        writer.writerow( (i, i+2, i*2)) 
finally:
     csvFile.close()

這里提個醒兒:Python 新建文件的機制考慮得非常周到(bullet-proof)。如果 ../files/test.csv 不存在,Python 會自動創建文件(不會自動創建文件夾)。如果文件已經存在,Python 會 用新的數據覆蓋 test.csv 文件。
實際工作中寫此程序之前的注意事項
如果你有很多 HTML 表格,且每個都要轉換成 CSV 文件,或者許多 HTML 表格都要匯總到一個 CSV 文件,那么把這個程序整合到爬蟲里以解決問題非常好。但是,如果你只需要做一次這種事情,那么更好的辦法就是:復制粘貼。選擇 HTML 表格內容然后粘貼到 Excel 文件里,可以另存為 CSV 格式,不需要寫代碼就能搞定!

5.3 MySQL

MySQL是目前最受歡迎的開源關系型數據庫管理系統。一個開源項目具有如此之競爭力實在是令人意外,它的流行程度正在不斷地接近另外兩個閉源的商業數據庫系統:微軟的 SQL Server 和甲骨文的 Oracle 數據庫(MySQL 在 2010 年被甲骨文收購)。
它的流程程度實在是名符其實。對大多數應用來說,MySQL 都是不二選擇。它是一種 非常靈活、穩定、功能齊全的 DBMS,許多頂級的網站都在用它:YouTube、Twitter 和 Facebook 等。
因為它受眾廣泛,免費,開箱即用,所以它也是網絡數據采集項目中常用的數據庫,

5.3.1 安裝 MySQL

用 Mac OS X 的包管理器 Homebrew (http://brew.sh/)安裝。當 Homebrew 安裝好以后,用下面的命令安裝 MySQL:

      $brew install mysql

Mac OS X 的 MySQL 安裝好之后,你可以用下面的命令啟動 MySQL 服務器:

    $cd /usr/local/mysql
    $sudo ./bin/mysqld_safe

5.3.2 基本命令

MySQL 服務器啟動之后,有很多種方法可以與數據庫交互。因為有很多工具是圖形界面,所以你不用 MySQL 的命令行(或者很少用命令行)也能管理數據庫。像 phpMyAdmin 和 MySQL Workbench 這類工具都可以很容易地實現數據的查看、排序和新建等工作。但是,掌握命令行操作數據庫依然是很重要的。
除了用戶自定義變量名(MySQL 5.x 版本是不區分大小寫的,MySQL 5.0 之前的版本是不區分大小寫的),MySQL 語句是不區分大小寫的。
如果你對這個強大數據庫的命令和技術感興趣,推薦你去看 Paul DuBois 的 MySQL Cookbook。

5.3.3 與 Python 整合

Python 沒有內置的 MySQL 支持工具。不過,有很多開源的庫可以用來與 MySQL 做交互, Python 2.x 和 Python 3.x 版本都支持。最有名的一個庫就是 PyMySQL
寫到這里的時候,PyMySQL 的版本是 0.6.2,你可以用下面的命令下載并安裝它:

     $ curl -L https://github.com/PyMySQL/PyMySQL/tarball/pymysql-0.6.2 | tar xz
     $ cd PyMySQL-PyMySQL-f953785/
     $ python setup.py install

如果需要更新,請檢查最新版的 PyMySQL,并修改第一行下載鏈接中的版本號進行更新。 安裝完成之后,你就可以使用 PyMySQL 包了。如果你的 MySQL 服務器處于運行狀態,
應該就可以成功地執行下面的命令(記得把 root 賬戶密碼加進去):

import pymysql


conn = pymysql.connect(host='127.0.0.1', unix_socket='/tmp/mysql.sock',
                       user='root', passwd=None, db='mysql')
cur = conn.cursor()
cur.execute("USE scraping")
cur.execute("SELECT * FROM pages WHERE id=1") print(cur.fetchone())
cur.close()
conn.close()

這段程序有兩個對象:連接對象(conn)和光標對象(cur)。
連接 / 光標模式是數據庫編程中常用的模式,不過剛剛接觸數據庫的時候,有些用戶很難區分兩種模式的不同。連接模式除了要連接數據庫之外,還要發送數據庫信息,處理回滾操作(當一個查詢或一組查詢被中斷時,數據庫需要回到初始狀態,一般用事務控制手段實現狀態回滾),創建新的光標對象,等等。
而一個連接可以有很多個光標。一個光標跟蹤一種狀態(state)信息,比如跟蹤數據庫的使用狀態。如果你有多個數據庫,且需要向所有數據庫寫內容,就需要多個光標來處理。 光標還會包含最后一次查詢執行的結果。通過調用光標函數,比如 cur.fetchone(),可以獲取查詢結果。
用完光標和連接之后,千萬記得把它們關閉。如果不關閉就會導致連接泄漏(connection leak),造成一種未關閉連接現象,即連接已經不再使用,但是數據庫卻不能關閉,因為數據庫不能確定你還要不要繼續使用它。這種現象會一直耗費數據庫的資源,所以用完數據 庫之后記得關閉連接!
在進行網絡數據采集時,處理 Unicode 字符串是很痛苦的事情。默認情況下,MySQL 也 不支持 Unicode 字符處理。不過你可以設置這個功能(這么做會增加數據庫的占用空間)。 因為在維基百科上我們難免會遇到各種各樣的字符,所以最好一開始就讓你的數據庫支持 Unicode:

ALTER DATABASE scraping CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; ALTER TABLE pages CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; 
ALTER TABLE pages CHANGE title title VARCHAR(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE pages CHANGE content content VARCHAR(10000) CHARACTER SET utf8mb4 CO LLATE utf8mb4_unicode_ci;

這四行語句改變的內容有:數據庫、數據表,以及兩個字段的默認編碼都從 utf8mb4 (嚴格說來也屬于 Unicode,但是對大多數 Unicode 字符的支持都非常不好)轉變成了 utf8mb4_unicode_ci。
你可以在 title 或 content 字段中插入一些德語變音符(umlauts)或漢語字符,如果沒有
錯誤就表示轉換成功了。
雖然 PyMySQL 規模并不大,但是里面有一些非常實用的函數本書并沒有介紹。具體請參考 Python 的 DBAPI 標準文檔。

5.3.4 數據庫技術與最佳實踐

和計算機科學的很多主題一樣,有一些技巧你其實可以很快地學會,它們可以讓你的數據庫變得更高效,讓應用的運行速度更快。
首先,給每個數據表都增加一個 id 字段,不會出什么問題。MySQL 里所有的表都至少有一個主鍵(就是 MySQL 用來排序的字段),因此 MySQL 知道怎么組織主鍵,通常數據庫很難智能地選擇主鍵。究竟是用人造的 id 字段作為主鍵,還是用那些具有唯一性屬性的字段作為主鍵,比如 username 字段,數據科學家和軟件工程師已經爭論了很多年,但我更傾向于主動創建一個 id 字段。這樣做的原因一兩句話難以說清,不過對于一些非企業級系統的數據庫,你還是應該用自增的 id 字段作為主鍵。
其次,用智能索引。字典(指的是常用的工具書,不是指 Python 的字典對象)是按照字母順序排列的單詞表。這樣做讓你在任何時候都能快速地找到一個單詞,只要你知道這個單詞是如何拼寫的就行。
最后一點是關于數據查詢時間和數據庫空間的問題。一個常見的誤區就是在數據庫中存儲大量重復數據,尤其是在做大量自然語言數據的網絡數據采集任務時。
除非你安裝了第三方包或保存詳細的數據庫日志,否則你無法掌握數據庫里數據增加、更新或刪除的具體時間。因此,如果需要對數據可用的空間、變更的頻率和變更的重要性進行分析,你應該考慮在數據新增、更新或刪除時加一個時間戳。

5.3.5 MySQL 里的“六度空間游戲”

5.4 Email

與網頁通過 HTTP 協議傳輸一樣,郵件是通過 SMTP(Simple Mail Transfer Protocol,簡單郵件傳輸協議)傳輸的。而且,和你用網絡服務器的客戶端(瀏覽器)處理那些通過 HTTP 協議傳輸的網頁一樣,Email 服務器也有客戶端,像 Sendmail、Postfix 和 Mailman 等,都可以收發郵件。
雖然用 Python 發郵件很容易,但是需要你連接那些正在運行 SMTP 協議的服務器。
下面的代碼運行的前提是你的電腦已經可以正常地運行一個 SMTP 客戶端。(如果要調整代碼用于遠程 SMTP 客戶端,請把 localhost 改成遠程服務器地址。)
用 Python 發一封郵件只要 9 行代碼:

import smtplib
from email.mime.text import MIMEText

msg = MIMEText("The body of the email is here")
msg['Subject'] = "An Email Alert"
msg['From'] = "ryan@pythonscraping.com"
msg['To'] = "webmaster@pythonscraping.com"
s = smtplib.SMTP('localhost')
s.send_message(msg)
s.quit()

Python 有兩個包可以發送郵件:smtplib 和 email。
Python 的 email 模塊里包含了許多實用的郵件格式設置函數,可以用來創建郵件“包 裹”。下面的示例中使用的 MIMEText 對象,為底層的 MIME(Multipurpose Internet Mail Extensions,多用途互聯網郵件擴展類型)協議傳輸創建了一封空郵件,最后通過高層的 SMTP 協議發送出去。MIMEText 對象 msg 包括收發郵箱地址、郵件正文和主題,Python 通 過它就可以創建一封格式正確的郵件。
smtplib 模塊用來設置服務器連接的相關信息。就像 MySQL 服務器的連接一樣,這個連接必須在用完之后及時關閉,以避免同時創建太多連接而浪費資源。

第 6 章 讀取文檔

互聯網最基本的特征:作為不同類型文件的傳輸媒介。
互聯網并不是一個 HTML 頁面的集合。它是一個信息集合,而 HTML 文件只是展示信息的一個框架而已。如果我們的爬蟲不能讀取其他類型的文件,包括純文本、 PDF、圖像、視頻、郵件等,我們將會失去很大一部分數據。
本章重點介紹文檔處理的相關內容,包括把文件下載到文件夾里,以及讀取文檔并提取數據。我們還會介紹文檔的不同編碼類型,讓程序可以讀取非英文的 HTML 頁面。

6.1 文檔編碼

文檔編碼是一種告訴程序——無論是計算機的操作系統還是 Python 代碼——讀取文檔的規則。文檔編碼的方式通??梢愿鶕募臄U展名進行判斷,雖然文件擴展名并不是由編碼確定的,而是由開發者確定的。
從最底層的角度看,所有文檔都是由 0 和 1 編碼而成的。而在高層(貼近用戶的層級), 編碼算法會定義“每個字符多少位”或“每個像素的顏色值用多少位”(圖像文件里)之類的事情,在那里你會遇到一些數據壓縮算法或體積縮減算法,比如 PNG 圖像編碼格式 (一種無損壓縮的位圖圖形式)。
純文本文件、視頻文件和圖像文件的唯一區別, 就是它們的 0 和 1 面向用戶的轉換方式不同。### 6.2 純文本
對大多數簡單的純文本文件,像 http://www.pythonscraping.com/pages/warandpeace/chapter1. txt 這個練習文件,你可以用下面的方法讀取:

from urllib.request import urlopen 


textPage = urlopen("http://www.pythonscraping.com/pages/warandpeace/chapter1.txt") 
print(textPage.read())

通常,當用 urlopen 獲取了網頁之后,我們會把它轉變成 BeautifulSoup 對象,方便后面對 HTML 進行分析。在這段代碼中,我們直接讀取頁面內容。你可能覺得,如果把它轉變成 BeautifulSoup 對象應該也不錯,但那樣做其實適得其反——這個頁面不是 HTML,所以 BeautifulSoup 庫就沒用了。一旦純文本文件被讀成字符串,你就只能用普通 Python 字符串的方法分析它了。當然,這么做有個缺點,就是你不能對字符串使用 HTML 標簽,去定位那些你真正需要的文字,避開那些你不需要的文字。如果現在你想從純文本文件中抽取某些信息,還是有些難度的。
文本編碼和全球互聯網
大多數時候用前面的方法讀取純文本文件都沒問題。但是,互聯網上的文本文件會比較復雜。下面介紹一些英文和非英文編碼的基礎知識,包括 ASCII、Unicode 和 ISO 編碼,以及對應的處理方法。

  1. 編碼類型簡介
    UTF-8,全稱是“Universal Character Set - Transformation Format 8 bit”,即“統一字符集 - 轉換格式 8 位”。一個常見的誤解是 UTF-8 把所有字符都存儲成 8 位。其實“8 位”只是顯示一個字符需要的最小位數,而不是最大位數。
    真實情況是,UTF-8 的每個字符開頭有一個標記表示“這個字符只用一個字節”或“那個字符需要用兩個字節”,一個字符最多可以是四個字節。由于這四個字節里還包含一部分設置信息,用來決定多少字節用做字符編碼,所以全部的 32 位(32 位 =4 字節 ×8 位 / 字 節)并不會都用,其實最多使用 21 位,也就是總共 2 097 152 種可能里面可以有 1 114 112 個字符。
    雖然對很多程序來說,Unicode 都是上帝的禮物(godsend),但是有些習慣很難改變, ASCII 依然是許多英文用戶的不二選擇。
    ASCII 是 20 世紀 60 年代開始使用的文字編碼標準,每個字符 7 位,一共 27,即 128 個字 符。這對于拉丁字母(包括大小寫)、標點符號和英文鍵盤上的所有符號,都是夠用的。
    除了 UTF-8,還有其他 UTF 標準,像 UTF-16、UTF-24、UTF-32,不過很少用這些編碼標 準對文件進行編碼。
    Unicode 標準也有問題,就是任何一種非英文語言文檔的體積都比 ASCII 編碼的體積大。雖然你的語言可能只需要用大約 100 個字符,像英文的 ASCII 編碼,8 位就夠了, 但是因為是用 UTF-8 編碼,所以你還是得用至少 16 位表示每個字符。這會讓非英文的純文本文檔體積差不多達到英文文檔的兩倍,對那些不用拉丁字符集的語言來說都是如此。
    ISO 標準解決這個問題的辦法是為每種語言創建一種編碼。和 Unicode 不同,它使用了與 ASCII 相同的編碼,但是在每個字符的開頭用 0 作“填充位”,這樣就可以讓語言在需要的時候創建特殊字符。
    雖然這些年 ISO 編碼標準的使用率一直在下降,但是目前仍有約 9% 的網站使用 ISO 編碼,所以有必要做基本的了解,并在采集網站之前需要檢查是否使用了這種編碼方法。
  2. 編碼進行時
    Python 默認把文本讀成 ASCII 編碼格式
    處理 HTML 頁面的時候,網站其實會在 <head> 部分顯示頁面使用的編碼格式。大多數網站,尤其是英文網站,都會帶這樣的標簽:
    <meta charset="utf-8" />
    如果你要做很多網絡數據采集工作,尤其是面對國際網站時,建議你先看看 meta 標簽的內容,用網站推薦的編碼方式讀取頁面內容。

6.3 CSV

Python 有一個超贊的標準庫可以讀寫 CSV 文件。
讀取CSV文件
Python 的 csv 庫主要是面向本地文件,就是說你的 CSV 文件得存儲在你的電腦上。而進
行網絡數據采集的時候,很多文件都是在線的。不過有一些方法可以解決這個問題:
? 手動把 CSV 文件下載到本機,然后用 Python 定位文件位置;
? 寫 Python 程序下載文件,讀取之后再把源文件刪除;
? 從網上直接把文件讀成一個字符串,然后轉換成一個 StringIO 對象,使它具有文件的屬性。
下面的程序就是從網上獲取一個 CSV 文件(這里用的是 http://pythonscraping.com/files/MontyPythonAlbums.csv 里的 Monty Python 樂團的專輯列表),然后把每一行都打印到命令行里:

from urllib.request import urlopen 
from io import StringIO
import csv

   
data = urlopen("http://pythonscraping.com/files/MontyPythonAlbums.csv")
                   .read().decode('ascii', 'ignore')
dataFile = StringIO(data)
csvReader = csv.reader(dataFile)
for row in csvReader: 
    print(row)

利用 DictReader 處理第一行(標題行)
csv.DictReader 會返回把 CSV 文件每一行轉換成 Python 的字典對象返回,而不是列表對
象,并把字段列表保存在變量 dictReader.fieldnames 里,字段列表同時作為字典對象的鍵

6.4 PDF

PDF 格式(Portable Document Format,便攜式文檔格式)是一種技術革命。 PDF 可以讓用戶在不同的系統上用同樣的方式查看圖片和文本文檔,無論這些文件是在哪種系統上制作的。
雖然把 PDF 顯示在網頁上已經有點兒過時了(你已經可以把內容顯示成 HTML 了,為什么還要用這種靜態、加載速度超慢的格式呢?),但是 PDF 仍然無處不在,尤其是在處理 商務報表和表單的時候。
PDFMiner3K 就是一個非常好用的庫(是 PDFMiner 的 Python 3.x 移植版)。它非常靈活, 可以通過命令行使用,也可以整合到代碼中。它還可以處理不同的語言編碼,而且對網絡 文件的處理也非常方便。
你可以下載這個模塊的源文件,解壓并用下面命 令安裝:

     $python setup.py install

文檔位于源文件解壓文件夾的 /pdfminer3k-1.3.0/docs/index.html 里,這個文檔更多是在介讀取
紹命令行接口,而不是 Python 代碼整合。

6.5 微軟 Word 和 .docx

Python 對 這 種 Google Docs、Open Office 和 Microsoft Office 都在使用的 .docx 格式的支持 還不夠好。 雖然有一個 python-docx 庫(http://python-docx.readthedocs.org/en/ latest/),但是只支持創建新文檔和讀取一些基本的文件數據,如文件大小和文件標題,不支持正文讀取。如果想讀取 Microsoft Office 文件的正文內容,我們需要自己動手找方法。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容