《Python 網(wǎng)絡(luò)數(shù)據(jù)采集》第一部分筆記

第一部分 創(chuàng)建爬蟲(chóng)

重點(diǎn)介紹網(wǎng)絡(luò)數(shù)據(jù)采集的基本原理 :

如何用 Python 從網(wǎng)絡(luò)服務(wù)器 請(qǐng)求信息,如何對(duì)服務(wù)器的響應(yīng)進(jìn)行基本處理,以及如何以自動(dòng)化手段與網(wǎng)站進(jìn)行交互幔托。

思考“網(wǎng)絡(luò)爬蟲(chóng)”時(shí)通常的想法:

? 通過(guò)網(wǎng)站域名獲取 HTML 數(shù)據(jù)
? 根據(jù)目標(biāo)信息解析數(shù)據(jù)
? 存儲(chǔ)目標(biāo)信息
? 如果有必要迄埃,移動(dòng)到另一個(gè)網(wǎng)頁(yè)重復(fù)這個(gè)過(guò)程

第 1 章 初見(jiàn)網(wǎng)絡(luò)爬蟲(chóng)

1.1 網(wǎng)絡(luò)連接

網(wǎng)絡(luò)數(shù)據(jù)采集需要拋開(kāi)一些接口的遮擋蟀架,不僅是在瀏覽器層(它如何解釋所有的 HTML、CSS 和 JavaScript)慧域,有時(shí)也包括網(wǎng)絡(luò)連接層鲤竹。

1.2 BeautifulSoup 簡(jiǎn)介

1.2.1 安裝 BeautifulSoup

1.2.2 運(yùn)行 BeautifulSoup

1.2.3 可靠的網(wǎng)絡(luò)連接

可能會(huì)發(fā)生兩種異常:
? 網(wǎng)頁(yè)在服務(wù)器上不存在(或者獲取頁(yè)面的時(shí)候出現(xiàn)錯(cuò)誤)
? 服務(wù)器不存在

第 2 章 復(fù)雜 HTML 解析

2.1 不是一直都要用錘子

在開(kāi)始解析網(wǎng)頁(yè)之前,看一些在解析復(fù)雜的 HTML 頁(yè)面時(shí)需要避免的問(wèn)題昔榴。
? 尋找“打印此頁(yè)”的鏈接辛藻,或者看看網(wǎng)站有沒(méi)有 HTML 樣式更友好的移動(dòng)版(把自己 的請(qǐng)求頭設(shè)置成處于移動(dòng)設(shè)備的狀態(tài),然后接收網(wǎng)站移動(dòng)版)互订。
? 尋找隱藏在 JavaScript 文件里的信息吱肌。可能需要查看網(wǎng)頁(yè)加載的 JavaScript 文件仰禽。
? 雖然網(wǎng)頁(yè)標(biāo)題經(jīng)常會(huì)用到氮墨,但是這個(gè)信息也許可以從網(wǎng)頁(yè)的 URL 鏈接里獲取。
? 如果你要找的信息只存在于一個(gè)網(wǎng)站上吐葵,別處沒(méi)有规揪,那你確實(shí)是運(yùn)氣不佳。如果不只限 于這個(gè)網(wǎng)站温峭,那么你可以找找其他數(shù)據(jù)源猛铅。有沒(méi)有其他網(wǎng)站也顯示了同樣的數(shù)據(jù)?網(wǎng)站
上顯示的數(shù)據(jù)是不是從其他網(wǎng)站上抓取后攢出來(lái)的?

2.2 再端一BeautifulSoup

什么時(shí)候使用 get_text() 與什么時(shí)候應(yīng)該保留標(biāo)簽?
.get_text() 會(huì)把你正在處理的 HTML 文檔中所有的標(biāo)簽都清除,然后返回一個(gè)只包含文字的字符串诚镰。假如你正在處理一個(gè)包含許多超鏈接奕坟、段落和標(biāo) 簽的大段源代碼祥款,那么 .get_text() 會(huì)把這些超鏈接、段落和標(biāo)簽都清除掉月杉, 只剩下一串不帶標(biāo)簽的文字刃跛。
用 BeautifulSoup 對(duì)象查找你想要的信息,比直接在 HTML 文本里查找信息要簡(jiǎn)單得多苛萎。通常在你準(zhǔn)備打印桨昙、存儲(chǔ)和操作數(shù)據(jù)時(shí),應(yīng)該最后才使 用 .get_text()腌歉。一般情況下蛙酪,你應(yīng)該盡可能地保留 HTML 文檔的標(biāo)簽結(jié)構(gòu)。

2.2.1 BeautifulSoup 的 find() 和 findAll()

BeautifulSoup 文檔里兩者的定義就是這樣:
findAll(tag, attributes, recursive, text, limit, keywords)
find(tag, attributes, recursive, text, keywords)
標(biāo)簽參數(shù) tag ——可以傳一個(gè)標(biāo)簽的名稱或多個(gè)標(biāo)簽名稱組成的 Python列表做標(biāo)簽參數(shù)
屬性參數(shù) attributes 是用一個(gè) Python 字典封裝一個(gè)標(biāo)簽的若干屬性和對(duì)應(yīng)的屬性值翘盖。
遞歸參數(shù) recursive 是一個(gè)布爾變量桂塞。
文本參數(shù) text 有點(diǎn)不同,它是用標(biāo)簽的文本內(nèi)容去匹配馍驯,而不是用標(biāo)簽的屬性阁危。
范圍限制參數(shù) limit,顯然只用于 findAll 方法汰瘫。find 其實(shí)等價(jià)于 findAll 的 limit 等于 1 時(shí)的情形狂打。
關(guān)鍵詞參數(shù) keyword,可以讓你選擇那些具有指定屬性的標(biāo)簽混弥。

2.2.2 其他 BeautifulSoup 對(duì)象

? BeautifulSoup 對(duì)象
? 標(biāo)簽 Tag 對(duì)象
? NavigableString 對(duì)象
用來(lái)表示標(biāo)簽里的文字趴乡,不是標(biāo)簽(有些函數(shù)可以操作和生成 NavigableString 對(duì)象捺氢, 而不是標(biāo)簽對(duì)象)威创。
? Comment 對(duì)象
用來(lái)查找 HTML 文檔的注釋標(biāo)簽,

2.2.3 導(dǎo)航樹(shù)

  1. 處理子標(biāo)簽和其他后代標(biāo)簽
    所有的子標(biāo)簽都是后代標(biāo) 簽穿撮,但不是所有的后代標(biāo)簽都是子標(biāo)簽蛹磺。
    一般情況下粟瞬,BeautifulSoup 函數(shù)總是處理當(dāng)前標(biāo)簽的后代標(biāo)簽。
    如果你只想找出子標(biāo)簽萤捆,可以用 .children 標(biāo)簽裙品。

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

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)

這段代碼會(huì)打印產(chǎn)品列表里的所有行的產(chǎn)品俗或,第一行表格標(biāo)題除外市怎。為什么標(biāo)題行被跳過(guò) 了呢?有兩個(gè)理由。
首先辛慰,對(duì)象不能把自己作為兄弟標(biāo)簽区匠。任何時(shí)候你獲取一個(gè)標(biāo)簽的兄弟標(biāo)簽,都不會(huì)包含這個(gè)標(biāo)簽本身。其次驰弄,這個(gè)函數(shù)只調(diào)用后面的兄弟標(biāo)簽麻汰。例如,如果我們選擇一組標(biāo)簽中位于中間位置的一個(gè)標(biāo)簽戚篙,然后用 next_siblings() 函數(shù)五鲫,那么它就只會(huì)返回在它后面的兄弟標(biāo)簽。
和 next_siblings 一樣岔擂,如果你很容易找到一組兄弟標(biāo)簽中的最后一個(gè)標(biāo)簽位喂,那么 previous_siblings 函數(shù)也會(huì)很有用。
當(dāng)然乱灵,還有 next_sibling 和 previous_sibling 函數(shù)塑崖,與 next_siblings 和 previous_siblings 的作用類似,只是它們返回的是單個(gè)標(biāo)簽痛倚,而不是一組標(biāo)簽规婆。

  1. 父標(biāo)簽處理
    在抓取網(wǎng)頁(yè)的時(shí)候,查找父標(biāo)簽的需求比查找子標(biāo)簽和兄弟標(biāo)簽要少很多蝉稳。通常情況 下聋呢,如果以抓取網(wǎng)頁(yè)內(nèi)容為目的來(lái)觀察 HTML 頁(yè)面,我們都是從最上層標(biāo)簽開(kāi)始的颠区,然 后思考如何定位我們想要的數(shù)據(jù)塊所在的位置。但是通铲,偶爾在特殊情況下你也會(huì)用到 BeautifulSoup 的父標(biāo)簽查找函數(shù)毕莱,parent 和 parents。

2.3 正則表達(dá)式

嘗試正則表達(dá)式
在學(xué)習(xí)書寫正則表達(dá)式的時(shí)候颅夺,做一些實(shí)驗(yàn)感受一下它們?nèi)绾喂ぷ髋蠼兀@是至關(guān)重要的。
如果你不想打開(kāi)代碼編輯器吧黄,寫完再運(yùn)行程序檢查正則表達(dá)式的運(yùn)行是否符合預(yù)期部服,那么你可以去 RegexPal(http://regexpal.com/)這類網(wǎng)站上在線測(cè)試 正則表達(dá)式。
正則表達(dá)式在實(shí)際中的一個(gè)經(jīng)典應(yīng)用是識(shí)別郵箱地址拗慨。雖然不同郵箱服務(wù)器的郵箱地址的具體規(guī)則不盡相同廓八,但是我們還是可以創(chuàng)建幾條通用規(guī)則。每條規(guī)則對(duì)應(yīng)的正則表達(dá)式如下表第 2 列所示赵抢。

規(guī) 則 正則表達(dá)式
1. 郵箱地址的第一部分至少包括一種內(nèi)容:大寫字母剧蹂、小寫字母、數(shù)字 0~9烦却、點(diǎn)號(hào)(.)宠叼、加號(hào) (+)或下劃線(_) [A-Za-z0-9._+]+:這個(gè)正則表達(dá)式簡(jiǎn)寫非常智慧。例 如其爵,它用“A-Z”表示“任意 A~Z 的大寫字母”冒冬。把 所有可能的序列和符號(hào)放在中括號(hào)(不是小括號(hào))里 表示“括號(hào)中的符號(hào)里任何一個(gè)”伸蚯。要注意后面的加 號(hào),它表示“這些符號(hào)都可以出現(xiàn)多次简烤,且至少出現(xiàn) 1 次”
2. 之后剂邮,郵箱地址會(huì)包含一個(gè) @ 符號(hào) @:這個(gè)符號(hào)很直接。@ 符號(hào)必須出現(xiàn)在中間位置乐埠, 有且僅有 1 次
3. 在符合 @ 之后抗斤,郵箱地址還必須至少包含一個(gè)大寫或小寫字母 [A-Za-z]+:可能只在域名的前半部分、符號(hào) @ 后面 用字母丈咐。而且瑞眼,至少有一個(gè)字母
4. 之后跟一個(gè)點(diǎn)號(hào)(.) \.:在域名前必須有一個(gè)點(diǎn)號(hào)(.)
5. 最后郵箱地址用 com、org棵逊、edu伤疙、net 結(jié)尾 (實(shí)際上,頂級(jí)域名有很多種可能辆影,但是作為示例演示這四個(gè)后綴夠用了)徒像。 (com|org|edu|net): 這樣列出了郵箱地址中可能出現(xiàn)在點(diǎn)號(hào)之后的字母序列

把上面的規(guī)則連接起來(lái),就獲得了完整的正則表達(dá)式:

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

當(dāng)我們動(dòng)手開(kāi)始寫正則表達(dá)式的時(shí)候蛙讥,最好先寫一個(gè)步驟列表描述出你的目標(biāo)字符串結(jié)構(gòu)锯蛀。 還要注意一些細(xì)節(jié)的處理。
表 2-1 用簡(jiǎn)單的說(shuō)明和例子列舉了正則表達(dá)式的一些常用符號(hào)次慢。這個(gè)列表并不是全部符 號(hào)旁涤,另外就像之前所說(shuō)的,可能在不同編程語(yǔ)言中會(huì)遇到一些變化迫像。但是劈愚,這 12 個(gè)符號(hào) 是 Python 的正則表達(dá)式中最常用的,可以用來(lái)查找和收集絕大多數(shù)數(shù)據(jù)類型闻妓。

表2-1:正則表達(dá)式常用符號(hào)

符 號(hào) 含 義 例 子 匹配結(jié)果
* 匹配前面的字符菌羽、子表達(dá)式或括號(hào)里的字符 0 次或多次 a*b* aaaaaaaa,aaabbbbb由缆, bbbbbb
+ 匹配前面的字符注祖、子表達(dá)式或括號(hào)里的字符至少 1次 a+b+ aaaaaaab,aaabbbbb均唉, abbbbbb
[] 匹配任意一個(gè)字符(相當(dāng)于“任選一個(gè)”) [A-Z]* APPLE氓轰,CAPIT ALS, QWERTY
() 表達(dá)式編組(在正則表達(dá)式的規(guī)則里編組會(huì)優(yōu)先運(yùn)行) (a*b)* aaabaab浸卦,abaaab署鸡, ababaaaaab
{m,n} 匹配前面的字符、子表達(dá)式或括號(hào)里的字符 m 到 n 次(包含 m 或 n) a{2,3}b{2,3} aabbb,aaabbb靴庆,aabb
[^] 匹配任意一個(gè)不在中括號(hào)里的字符 [^A-Z]* apple时捌,lowercase, qwerty
| 匹配任意一個(gè)由豎線分割的字符炉抒、子表達(dá)式(注 意是豎線奢讨,不是大字字母 I) b(a|i|e)d bad,bid焰薄,bed
. 匹配任意單個(gè)字符(包括符號(hào)拿诸、數(shù)字和空格等) b.d bad,bzd塞茅,b$d亩码,b d
^ 指字符串開(kāi)始位置的字符或子表達(dá)式 ^a apple,asdf野瘦,a
\ 轉(zhuǎn)義字符(把有特殊含義的字符轉(zhuǎn)換成字面形式) \.\ | \\ .| \
$ 經(jīng)常用在正則表達(dá)式的末尾描沟,表示“從字符串的末端匹配”。如果不用它鞭光,每個(gè)正則表達(dá)式實(shí)際都 帶著“.*”模式吏廉,只會(huì)從字符串開(kāi)頭進(jìn)行匹配。這 個(gè)符號(hào)可以看成是 ^ 符號(hào)的反義詞 [A-Z]*[a-z]*$ ABCabc惰许,zzzyx席覆,Bob
?! “不包含”。這個(gè)奇怪的組合通常放在字符或正則表達(dá)式前面汹买,表示字符不能出現(xiàn)在目標(biāo)字符串里娜睛。 這個(gè)符號(hào)比較難用,字符通常會(huì)在字符串的不同部位出現(xiàn)卦睹。如果要在整個(gè)字符串中全部排除某個(gè)字符,就加上 ^ 和 $ 符號(hào) ^((?![A-Z]).)*$ no-caps-here方库,$ymb0ls a4e f!ne

2.4 正則表達(dá)式和BeautifulSoup

正則表達(dá)式可以作為 BeautifulSoup 語(yǔ)句的任意一個(gè)參數(shù)结序,讓你的目標(biāo)元素查找工作極具靈 活性。

2.5 獲取屬性

對(duì)于一個(gè)標(biāo)簽對(duì)象纵潦,可以用下面的代碼獲取它的全部屬性:

    myTag.attrs

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

    myImgTag.attrs["src"]

2.6 Lambda 表達(dá)式

BeautifulSoup 允許我們把特定函數(shù)類型當(dāng)作 findAll 函數(shù)的參數(shù)返敬。唯一的限制條件是這些 函數(shù)必須把一個(gè)標(biāo)簽作為參數(shù)且返回結(jié)果是布爾類型。BeautifulSoup 用這個(gè)函數(shù)來(lái)評(píng)估它 遇到的每個(gè)標(biāo)簽對(duì)象寥院,最后把評(píng)估結(jié)果為“真”的標(biāo)簽保留劲赠,把其他標(biāo)簽剔除。
例如,下面的代碼就是獲取有兩個(gè)屬性的標(biāo)簽:

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

這行代碼會(huì)找出下面的標(biāo)簽:

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

如果你愿意多寫一點(diǎn)兒代碼凛澎,那么在 BeautifulSoup 里用 Lambda 表達(dá)式選擇標(biāo)簽霹肝,將是正 則表達(dá)式的完美替代方案。

2.7 超越 BeautifulSoup

如果 BeautifulSoup 不能滿足你的需求塑煎,你可以看看其他的庫(kù)沫换。
? lxml
這個(gè)庫(kù)(http://lxml.de/)可以用來(lái)解析 HTML 和 XML 文檔,以非常底層的實(shí)現(xiàn)而聞名于世最铁,大部分源代碼是用 C 語(yǔ)言寫的讯赏。雖然學(xué)習(xí)它需要花一些時(shí)間(其實(shí)學(xué)習(xí)曲線越陡峭,表明你可以越快地學(xué)會(huì)它)冷尉,但它在處理絕大多數(shù) HTML 文檔時(shí)速度都非呈妫快。
? HTML parser
這是 Python 自帶的解析庫(kù)(https://docs.python.org/3/library/html.parser.html)网严。因?yàn)樗挥冒惭b(只要裝了 Python 就有)识樱,所以可以很方便地使用。

第 3 章 開(kāi)始采集

3.1 遍歷單個(gè)域名

3.2 采集整個(gè)網(wǎng)站

深網(wǎng)和暗網(wǎng)
深網(wǎng)是網(wǎng)絡(luò)的一部分震束,與淺網(wǎng)(surface Web)對(duì)立怜庸。淺網(wǎng)是互聯(lián)網(wǎng)上搜索引擎可以抓 到的那部分網(wǎng)絡(luò)。據(jù)不完全統(tǒng)計(jì)垢村,互聯(lián)網(wǎng)中其實(shí)約 90% 的網(wǎng)絡(luò)都是深網(wǎng)割疾。因?yàn)楣雀璨荒茏鱿癖韱翁峤贿@類事情,也找不到那些沒(méi)有直接鏈接到頂層域名上的網(wǎng)頁(yè)嘉栓,或者因?yàn)橛?robots.txt 禁止而不能查看網(wǎng)站宏榕,所以淺網(wǎng)的數(shù)量相對(duì)深網(wǎng)還是比較少的。
暗網(wǎng)侵佃,也被稱為 Darknet 或 dark Internet麻昼,完全是另一種“怪獸”。它們也建立在已有 的網(wǎng)絡(luò)基礎(chǔ)上馋辈,但是使用 Tor 客戶端抚芦,帶有運(yùn)行在 HTTP 之上的新協(xié)議,提供了一個(gè) 信息交換的安全隧道迈螟。
和暗網(wǎng)不同叉抡,深網(wǎng)是相對(duì)容易采集的。實(shí)際上答毫,本書的很多工具都是在教你如何采集那些 Google 爬蟲(chóng)機(jī)器人不能獲取的深網(wǎng)信息褥民。
遍歷整個(gè)網(wǎng)站的網(wǎng)絡(luò)數(shù)據(jù)采集有許多好處。
? 生成網(wǎng)站地圖
? 收集數(shù)據(jù)
關(guān)于遞歸的警告
如果遞歸運(yùn) 行的次數(shù)非常多洗搂,前面的遞歸程序就很可能崩潰消返。
Python 默認(rèn)的遞歸限制(程序遞歸地自我調(diào)用次數(shù))是 1000 次载弄。

3.3 通過(guò)互聯(lián)網(wǎng)采集

在你寫爬蟲(chóng)隨意跟隨外鏈跳轉(zhuǎn)之前,請(qǐng)問(wèn)自己幾個(gè)問(wèn)題侦副。
? 我要收集哪些數(shù)據(jù)?這些數(shù)據(jù)可以通過(guò)采集幾個(gè)已經(jīng)確定的網(wǎng)站(永遠(yuǎn)是最簡(jiǎn)單的做法) 完成嗎?或者我的爬蟲(chóng)需要發(fā)現(xiàn)那些我可能不知道的網(wǎng)站嗎?
? 當(dāng)我的爬蟲(chóng)到了某個(gè)網(wǎng)站侦锯,它是立即順著下一個(gè)出站鏈接跳到一個(gè)新網(wǎng)站,還是在網(wǎng)站上呆一會(huì)兒秦驯,深入采集網(wǎng)站的內(nèi)容?
? 有沒(méi)有我不想采集的一類網(wǎng)站?我對(duì)非英文網(wǎng)站的內(nèi)容感興趣嗎?
? 如果我的網(wǎng)絡(luò)爬蟲(chóng)引起了某個(gè)網(wǎng)站網(wǎng)管的懷疑尺碰,我如何避免法律責(zé)任?


采集外鏈的程序流程圖
收集內(nèi)鏈和外鏈的程序流程圖

寫代碼之前擬個(gè)大綱或畫個(gè)流程圖是很好的編程習(xí)慣,這么做不僅可以為你后期處理節(jié)省 很多時(shí)間译隘,更重要的是可以防止自己在爬蟲(chóng)變得越來(lái)越復(fù)雜時(shí)亂了分寸亲桥。
處理網(wǎng)頁(yè)重定向
重定向(redirect)允許一個(gè)網(wǎng)頁(yè)在不同的域名下顯示。重定向有兩種形式:
? 服務(wù)器端重定向固耘,網(wǎng)頁(yè)在加載之前先改變了 URL;
? 客戶端重定向题篷,有時(shí)你會(huì)在網(wǎng)頁(yè)上看到“10 秒鐘后頁(yè)面自動(dòng)跳轉(zhuǎn)到......”之類的消息,
表示在跳轉(zhuǎn)到新 URL 之前網(wǎng)頁(yè)需要加載內(nèi)容厅目。
本節(jié)處理的是服務(wù)器端重定向的內(nèi)容番枚。更多關(guān)于客戶端重定向的細(xì)節(jié),通常用 JavaScript 或 HTML 來(lái)實(shí)現(xiàn)损敷,請(qǐng)看第 10 章葫笼。
服務(wù)器端重定向,你通常不用擔(dān)心拗馒。如果你在用 Python 3.x 版本的 urllib 庫(kù)路星,它會(huì)自動(dòng)處理重定向。不過(guò)要注意诱桂,有時(shí)候你要采集的頁(yè)面的 URL 可能并不是你當(dāng)前所在頁(yè)面的 URL洋丐。

3.4 用 Scrapy 采集

Scrapy 就是一個(gè)幫你大幅度降低網(wǎng)頁(yè)鏈接查找和識(shí)別工作復(fù)雜度的 Python 庫(kù),它可以讓你輕松地采集一個(gè)或多個(gè)域名的信息挥等。
Scrapy 日志處理
Scrapy 生成的調(diào)試信息非常有用友绝,但是通常太羅嗦。你可以在 Scrapy 項(xiàng)目中
的 setting.py 文件中設(shè)置日志顯示層級(jí): LOG_LEVEL = 'ERROR'
Scrapy 日志有五種層級(jí)肝劲,按照范圍遞增順序排列如下:
? CRITICAL
? ERROR
? WARNING
? DEBUG
? INFO
如果日志層級(jí)設(shè)置為 ERROR迁客,那么只有 CRITICAL 和 ERROR 日志會(huì)顯示出來(lái)。
如果日志層級(jí)設(shè)置為 INFO涡相,那么所有信息都會(huì)顯示出來(lái),其他同理剩蟀。 日志不僅可以顯示在終端催蝗,也可以通過(guò)下面命令輸出到一個(gè)獨(dú)立的文件中:

$ scrapy crawl article -s LOG_FILE=wiki.log

如果目錄中沒(méi)有 wiki.log,那么運(yùn)行程序會(huì)創(chuàng)建一個(gè)新文件育特,然后把所有的日志都保存到里面丙号。如果已經(jīng)存在先朦,會(huì)在原文后面加入新的日志內(nèi)容。
Scrapy 用 Item 對(duì)象決定要從它瀏覽的頁(yè)面中提取哪些信息犬缨。Scrapy 支持用不同的輸出格式來(lái)保存這些信息喳魏,比如 CSV、JSON 或 XML 文件格式怀薛,對(duì)應(yīng)命令如下所示:

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

當(dāng)然刺彩,你也可以自定義 Item 對(duì)象,把結(jié)果寫入你需要的一個(gè)文件或數(shù)據(jù)庫(kù)中枝恋,只要在爬蟲(chóng)的 parse 部分增加相應(yīng)的代碼即可创倔。
Scrapy 是處理網(wǎng)絡(luò)數(shù)據(jù)采集相關(guān)問(wèn)題的利器。它可以自動(dòng)收集所有 URL焚碌,然后和指定的規(guī)則進(jìn)行比較;確保所有的 URL 是唯一的;根據(jù)需求對(duì)相關(guān)的 URL 進(jìn)行標(biāo)準(zhǔn)化;以及到更 深層的頁(yè)面中遞歸查找畦攘。

第 4 章 使用 API

一般情況下,程序員可以用 HTTP 協(xié)議向 API 發(fā)起請(qǐng)求以獲取某種信息十电,API 會(huì)用 XML (eXtensible Markup Language知押, 可擴(kuò)展標(biāo)記語(yǔ)言 ) 或 JSON(JavaScript Object Notation, JavaScript 對(duì)象表示)格式返回服務(wù)器響應(yīng)的信息鹃骂。盡管大多數(shù) API 仍然在用 XML台盯,但是
JSON 正在快速成為數(shù)據(jù)編碼格式的主流選擇。

4.1 API 概述

想要體育 信息? ESPN 提供的 API 包括運(yùn)動(dòng)員信息偎漫、比賽分?jǐn)?shù)等爷恳。
Google 的開(kāi)發(fā)者社區(qū)也提供了一堆 API 用于獲取語(yǔ)言翻譯、分析象踊、地理位置等 信息温亲。
究竟 API 和普通的網(wǎng)址訪問(wèn)有什么區(qū)別呢?
如果不考慮 API 高大上的名稱,其實(shí)兩者沒(méi)啥區(qū)別杯矩。API 可以通過(guò) HTTP 協(xié)議下載文件栈虚,和 URL 訪問(wèn)網(wǎng)站獲取數(shù)據(jù)的協(xié)議一 樣,它幾乎可以實(shí)現(xiàn)所有在網(wǎng)上干的事情史隆。API 之所以叫 API 而不是叫網(wǎng)站的原因魂务,其實(shí) 是首先 API 請(qǐng)求使用非常嚴(yán)謹(jǐn)?shù)恼Z(yǔ)法,其次 API 用 JSON 或 XML 格式表示數(shù)據(jù)泌射,而不是 HTML 格式粘姜。

4.2 API 通用規(guī)則

API 用一套非常標(biāo)準(zhǔn)的規(guī)則生成數(shù)據(jù),而且生成的數(shù)據(jù)也是按照非常標(biāo)準(zhǔn)的方式組織的熔酷。
第一次使用一個(gè) API 時(shí)孤紧,建議閱讀文檔

4.2.1 方法

利用 HTTP 從網(wǎng)絡(luò)服務(wù)獲取信息有四種方式:
? GET
? POST
? PUT
? DELETE
GET 就是你在瀏覽器中輸入網(wǎng)址瀏覽網(wǎng)站所做的事情。當(dāng)你訪問(wèn) http://freegeoip.net/ json/50.78.253.58 時(shí)拒秘,就會(huì)使用 GET 方法号显〕舨拢可以想象成 GET 在說(shuō):“喂,網(wǎng)絡(luò)服務(wù)器押蚤,請(qǐng)按照這個(gè)網(wǎng)址給我信息蔑歌。”
POST 基本就是當(dāng)你填寫表單或提交信息到網(wǎng)絡(luò)服務(wù)器的后端程序時(shí)所做的事情揽碘。每次當(dāng)你登錄網(wǎng)站的時(shí)候次屠,就是通過(guò)用戶名和(有可能加密的)密碼發(fā)起一個(gè) POST 請(qǐng)求。如果你用 API 發(fā)起一個(gè) POST 請(qǐng)求钾菊,相當(dāng)于說(shuō)“請(qǐng)把信息保存到你的數(shù)據(jù)庫(kù)里”帅矗。
PUT 在網(wǎng)站交互過(guò)程中不常用,但是在 API 里面有時(shí)會(huì)用到煞烫。PUT 請(qǐng)求用來(lái)更新一個(gè)對(duì)象或信息浑此。例如,API 可能會(huì)要求用 POST 請(qǐng)求創(chuàng)建新用戶滞详,但是如果你要更新老用戶的郵箱 地址凛俱,就要用 PUT 請(qǐng)求了。
DELETE 用于刪除一個(gè)對(duì)象料饥。例如蒲犬,如果我們向 http://myapi.com/user/23 發(fā)出一個(gè) DELETE 請(qǐng) 求,就會(huì)刪除 ID 號(hào)是 23 的用戶岸啡。DELETE 方法在公共 API 里面不常用原叮,它們主要用于創(chuàng)建信息,不能隨便讓一個(gè)用戶去刪掉數(shù)據(jù)庫(kù)的信息巡蘸。但是奋隶,和 PUT 方法一樣,DELETE 方法也值得了解一下悦荒。
雖然在 HTTP 規(guī)范里還有一些信息處理方式唯欣,但是這四種基本是你使用 API 過(guò)程中可能遇到的全部。

4.2.2 驗(yàn)證

有些 API 要求客戶驗(yàn)證是為了計(jì)算 API 調(diào)用的費(fèi)用搬味,或者是提供了包月的服務(wù)境氢。
有些驗(yàn)證是為了“限制”用戶使用 API(限制每秒鐘、每小時(shí)或每天 API 調(diào)用的次數(shù))碰纬,或者是限 制一部分用戶對(duì)某種信息或某類 API 的訪問(wèn)萍聊。
還有一些 API 可能不要求驗(yàn)證,但是可能會(huì)為了市場(chǎng)營(yíng)銷而跟蹤用戶的使用行為悦析。
通常 API 驗(yàn)證的方法都是用類似令牌(token)的方式調(diào)用曼库,每次 API 調(diào)用都會(huì)把令牌傳遞到服務(wù)器上阔逼。這種令牌要么是用戶注冊(cè)的時(shí)候分配給用戶淹辞,要么就是在用戶調(diào)用的時(shí)候才提供飘庄,可能是長(zhǎng)期固定的值,也可能是頻繁變化的酌泰,通過(guò)服務(wù)器對(duì)用戶名和密碼的組合處理后生成媒佣。
令牌除了在 URL 鏈接中傳遞,還會(huì)通過(guò)請(qǐng)求頭里的 cookie 把用戶信息傳遞給服務(wù)器陵刹。

4.3 服務(wù)器響應(yīng)

API 有一個(gè)重要的特征是它們會(huì)反饋格式友好的數(shù)據(jù)默伍。大多數(shù)反饋的數(shù)據(jù)格式都是 XML 和 JSON。
這幾年衰琐,JSON 比 XML 更受歡迎也糊,主要有兩個(gè)原因。
首先羡宙,JSON 文件比完整的 XML 格式小狸剃。
比如下面的 XML 數(shù)據(jù)用了 98 個(gè)字符:

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

同樣的 JSON 格式數(shù)據(jù):

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

只要用 73 個(gè)字符,比表述同樣內(nèi)容的 XML 文件要小 36%狗热。 當(dāng)然有人可能會(huì)說(shuō)钞馁,XML 也可以表示成這種形式:

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

不過(guò)這么做并不好,因?yàn)樗恢С稚顚忧度霐?shù)據(jù)匿刮。而且它也用了 71 個(gè)字符僧凰,和 JSON 差 不多。
JSON 格式比 XML 更受歡迎的另一個(gè)原因是網(wǎng)絡(luò)技術(shù)的改變熟丸。過(guò)去训措,服務(wù)器端用 PHP 和 .NET 這些程序作為 API 的接收端。現(xiàn)在光羞,服務(wù)器端也會(huì)用一些 JavaScript 框架作為 API 的發(fā)送和接收端绩鸣,像 Angular 或 Backbone 等。雖然服務(wù)器端的技術(shù)無(wú)法預(yù)測(cè)它們即將收到的數(shù)據(jù)格式狞山,但是像 Backbone 之類的 JavaScript 庫(kù)處理 JSON 比處理 XML 要更簡(jiǎn)單全闷。

API調(diào)用

不同 API 的調(diào)用語(yǔ)法大不相同,但是有幾條共同準(zhǔn)則萍启。
當(dāng)使用 GET 請(qǐng)求獲取數(shù)據(jù)時(shí)总珠,用 URL 路徑描述你要獲取的數(shù)據(jù)范圍,查詢參數(shù)可以作為過(guò)濾器或附加請(qǐng)求使用勘纯。
例如局服,下面這個(gè)虛擬的 API,可以獲取 ID 是 1234 的用戶在 2014 年 8 月份發(fā)表的所有博文:

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

有許多 API 會(huì)通過(guò)文件路徑(path)的形式指定 API 版本驳遵、數(shù)據(jù)格式和其他屬性淫奔。例如,下面的鏈接會(huì)返回同樣的結(jié)果堤结,但是使用虛擬 API 的第四版唆迁,反饋數(shù)據(jù)為 JSON 格式:

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

還有一些 API 會(huì)通過(guò)請(qǐng)求參數(shù)(request parameter)的形式指定數(shù)據(jù)格式和 API 版本:

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

4.4 Echo Nest

The Echo Nest 音樂(lè)數(shù)據(jù)網(wǎng)站是一個(gè)用網(wǎng)絡(luò)爬蟲(chóng)建立的超級(jí)給力的企業(yè)級(jí)案例鸭丛。雖然像 Pandora 之類的音樂(lè)公司都是通過(guò)人工干預(yù)完成音樂(lè)的分類與說(shuō)明,但是 The Echo Nest 是通過(guò)自動(dòng)智能技術(shù)唐责,以及博客與新聞信息的采集鳞溉,來(lái)完成藝術(shù)家、歌曲和專輯的分類工作的鼠哥。
更給力的是熟菲,它的 API 可以經(jīng)非商業(yè)用途免費(fèi)使用。使用 API 得有一個(gè) key朴恳,你可以在 The Echo Nest 的注冊(cè)頁(yè)面填入名稱抄罕、郵箱和用戶名來(lái)注冊(cè)賬號(hào)。
The Echo Nest 的 API 的響應(yīng)結(jié)果由四個(gè)部分組成:藝術(shù)家(artist)于颖、歌曲(song)呆贿、專輯 (track)和風(fēng)格(genre)。除了風(fēng)格之外森渐,所有信息都帶有唯一的 ID 號(hào)榨崩,可以通過(guò) API 調(diào) 用把信息展示成不同的形式。假如我想獲取 Monty Python 喜劇樂(lè)團(tuán)的歌曲章母,可以用下面的
鏈接獲取歌曲的 ID(記得把 < 你的 api_key> 替換成你自己的 API key):

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

響應(yīng)的結(jié)果是:

    {"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" }]}}

具體文檔請(qǐng)參考 The Echo Nest API 概述母蛛。
The Echo Nest 資助了很多技術(shù)與音樂(lè)交叉領(lǐng)域的黑客松項(xiàng)目(hackathon,也叫黑客馬拉 松乳怎、編程馬拉松)和編程項(xiàng)目彩郊。如果你想從中獲取靈感,The Echo Nest 示例頁(yè)面是一個(gè)好的起點(diǎn)蚪缀。

4.5 Twitter API

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

4.5.1 開(kāi)始

除了流量限制,Twitter 的 API 驗(yàn)證方式也比 The Echo Nest 要復(fù)雜金蜀,既要有 API 的 key刷后,
也要用其他 key。要獲取 API 的 key渊抄,你需要注冊(cè)一個(gè) Twitter 賬號(hào);可以在注冊(cè)頁(yè)面直接注冊(cè)尝胆。另外,還需要在 Twitter 的開(kāi)發(fā)者網(wǎng)站注冊(cè)一個(gè)新應(yīng)用护桦。 完成注冊(cè)之后含衔,你會(huì)在一個(gè)新頁(yè)面看到你應(yīng)用的基本信息,包括自定義的 key。
如果你單擊“manage keys and access tokens”頁(yè)面贪染,就會(huì)跳轉(zhuǎn)到一個(gè)包含更多信息的頁(yè)面上缓呛。
這個(gè)頁(yè)面還包括一個(gè)自動(dòng)生成加密 key 的按鈕,可以使得應(yīng)用被公開(kāi)訪問(wèn)杭隙。

4.5.2 幾個(gè)示例

Twitter 的驗(yàn)證系統(tǒng)用 OAuth 驗(yàn)證强经,非常復(fù)雜;最好找一個(gè)成熟穩(wěn)定的 Python 庫(kù)來(lái)處理它,不要自己從頭寫代碼來(lái)實(shí)現(xiàn)寺渗。因?yàn)槭止ぬ幚?Twitter 的 API 是非常復(fù)雜的工作,所以本節(jié)內(nèi)容的重點(diǎn)是用 Python 代碼來(lái)實(shí)現(xiàn) API 的交互兰迫,不是親手實(shí)現(xiàn)這個(gè) API信殊。
最好的一個(gè) Python Twitter 庫(kù)(名字也叫 Twitter)。你可以從 Python Twitter Tools(PTT) 網(wǎng)站下載并安裝這個(gè)庫(kù)( pip 安裝也可以汁果,pip install twitter):

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

Twitter 訪問(wèn)權(quán)限
應(yīng)用的默認(rèn)訪問(wèn)權(quán)限(credential permissions)是只讀(read-only)模式涡拘,除了讓你的應(yīng)用發(fā)推文之外,這樣的權(quán)限可以滿足大部分需求据德。
如果想把令牌的權(quán)限改成讀 / 寫(read/write)模式鳄乏,你可以在 Twitter 的應(yīng)用控制面板的權(quán)限欄進(jìn)行修改。改變權(quán)限后令牌會(huì)重新生成棘利。
如果有需要你也可以更新應(yīng)用的令牌權(quán)限橱野,用它登錄你的 Twitter 賬號(hào)直接收發(fā)推文。不過(guò)要注意信息安全善玫。通常水援,應(yīng)該對(duì)不同的應(yīng)用授予不同的權(quán) 限,而不是給那些不需要太多權(quán)限的應(yīng)用過(guò)多的訪問(wèn)權(quán)限茅郎。
完整的文檔請(qǐng)?jiān)?GitHub 上查看蜗元。

4.6 Google API

Google 是目前為網(wǎng)民提供最全面、最好用的網(wǎng)絡(luò) API 套件(collection)的公司之一系冗。無(wú)論你想處理哪種信息奕扣,包括語(yǔ)言翻譯、地理位置掌敬、日歷惯豆,甚至基因數(shù)據(jù),Google 都提供了 API奔害。Google 還為它的一些知名應(yīng)用提供 API循帐,比如 Gmail、YouTube 和 Blogger 等舀武。
查看 Google API 有兩種方式拄养。一種方式是通過(guò)產(chǎn)品頁(yè)面查看,里面有許多 API、軟件開(kāi)發(fā)工具包瘪匿,以及其他軟件開(kāi)發(fā)者感興趣的項(xiàng)目跛梗。 另一種方式是 API 控制臺(tái),里面提供了方便的接口來(lái)開(kāi)啟和關(guān)閉 API 服務(wù)棋弥,查看流量限制和使用情況核偿,還可以和 Google 強(qiáng)大的云計(jì)算平臺(tái)的開(kāi)發(fā)實(shí)例結(jié)合使用。
Google 的大多數(shù) API 都是免費(fèi)的顽染,不過(guò)有些需要付費(fèi)漾岳,比如搜索 API 需要一個(gè)付費(fèi)的授權(quán)。Google 的免費(fèi) API 套件對(duì)普通版的賬號(hào)也是非撤勰慷慨的尼荆,允許每天進(jìn)行 250 次到 20 000 000 次的訪問(wèn)。還有一些 API 可以通過(guò)驗(yàn)證信用卡提高流量上限(不需要支付費(fèi)用)唧垦。比如捅儒,Google 的地點(diǎn)查詢 API 每 24 小時(shí)的流量限制是 1000 次,但是如果你通過(guò)了信用卡驗(yàn)證振亮,就可以提高到 150 000 次巧还。更多的信息請(qǐng)參考 Google 的 API 使用限額和計(jì)費(fèi)方式頁(yè)面

4.6.1 開(kāi)始

如果你有 Google 賬號(hào)坊秸,可以查看自己可用的 API 列表麸祷,并通過(guò) Google 開(kāi)發(fā)者控制臺(tái)創(chuàng)建 API 的 key。如果你沒(méi)有 Google 賬號(hào)褒搔,請(qǐng)?jiān)趧?chuàng)
Google 賬號(hào)頁(yè)面建立自己的賬號(hào)摇锋。
當(dāng)你登錄賬號(hào)或賬號(hào)創(chuàng)建完成后,就能在 API 控制臺(tái)頁(yè)面看到一些賬號(hào)信息站超,包含 API 的 key荸恕。
在憑證頁(yè)面,你可以單擊“Create new Key”按鈕創(chuàng)建新的 API key死相。為了你的賬號(hào)安全融求,建議限制 API 使用的 IP 地址或 URL 鏈接。你也可以創(chuàng)建一個(gè)可用于任意 IP 地址或 URL 的 API key算撮,只要把“Accept Request From These Server IP Addresses”(接受這些服務(wù)器 IP 地址發(fā)出的請(qǐng)求)這一欄空著就行了生宛。但是,請(qǐng)記住保證 API key 的安全性是非常重要的事情肮柜,如果你不限制允許使用 API 的 IP 地址——任何使用你的 API key 調(diào)用你的 API 都算成是你的消費(fèi)陷舅,即使你并不知情。
你也可以建立多個(gè) API key审洞。比如莱睁,你可以為每個(gè)項(xiàng)目都分配一個(gè)單獨(dú)的 API key,也可以為每個(gè)網(wǎng)站域名都分配一個(gè) API key。但是仰剿,Google 的 API 流量是按照每個(gè)賬號(hào)分配的创淡,不是按照每個(gè) key 分配的,所以這樣做雖然可以方便地管理 API 權(quán)限南吮,但是并不會(huì)提高你的可用流量!

4.6.2 幾個(gè)示例

Google 最受歡迎的(個(gè)人認(rèn)為也是最有趣的)API 都在 Google 地圖 API 套件中琳彩。你可能見(jiàn)過(guò)很多網(wǎng)站都在用嵌入式 Google 地圖,覺(jué)得自己對(duì)這類功能很熟悉部凑。但是露乏,地圖 API 遠(yuǎn)比嵌入式地圖的功能豐富得多——你可以把街道地址解析成經(jīng) / 緯度(longitude/latitude) 坐標(biāo)值,地球上任意點(diǎn)的海拔高度涂邀,做出基于位置的可視化圖形瘟仿,獲取任意位置的時(shí)區(qū)信息,以及其他一些地圖相關(guān)的事情必孤。

4.7 解析 JSON 數(shù)據(jù)

用過(guò) freegeoip.net 網(wǎng)站 IP 查詢的例子,可以把 IP 地址解析轉(zhuǎn)換成地
http://freegeoip.net/json/50.78.253.58
我可以獲取這個(gè)請(qǐng)求的反饋數(shù)據(jù)瑞躺,然后用 Python 的 JSON 解析函數(shù)來(lái)解碼:

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 的國(guó)家代碼敷搪。
這里用的 JSON 解析庫(kù)是 Python 標(biāo)準(zhǔn)庫(kù)的一部分。只需要在代碼開(kāi)頭寫上 import json, 不同于那些需要先把 JSON 解析成一種 JSON 對(duì)象或 JSON 節(jié)點(diǎn)的語(yǔ)言幢哨,Python 使用了一種更加靈活的方式赡勘,把 JSON 轉(zhuǎn)換成字典,JSON 數(shù)組轉(zhuǎn)換成列表捞镰, JSON 字符串轉(zhuǎn)換成 Python 字符串闸与。通過(guò)這種方式,就可以讓 JSON 的獲取和操作變得非常簡(jiǎn)單岸售。

4.8 回到主題

Python 的集合類型簡(jiǎn)介
到現(xiàn)在為止践樱,我用已經(jīng)用過(guò)兩個(gè) Python 的數(shù)據(jù)結(jié)構(gòu)來(lái)儲(chǔ)存不同類型的數(shù)據(jù):列表和詞典。已經(jīng)有了兩種數(shù)據(jù)類型凸丸,為什么還要用集合(set)?
Python 的集合是無(wú)序的拷邢,就是說(shuō)你不能用位置來(lái)獲得集合元素對(duì)應(yīng)的值。數(shù)據(jù)加入集合的順序屎慢,和你重新獲取它們的順序瞭稼,很可能是不一樣的。如果你要存儲(chǔ)一個(gè)已有的值到集合中腻惠,集合會(huì)自動(dòng)忽略它环肘。
對(duì)于未來(lái)可能需要擴(kuò)展的代碼,在決定使用集合還是列表時(shí)集灌,有兩件事情需要考慮: 雖然列表迭代速度比集合稍微快一點(diǎn)兒悔雹,但集合查找速度更快(確定一個(gè)對(duì)象是否在集合中),因?yàn)?Python 集合就是值為 None 的詞典,用的是哈希表結(jié)構(gòu)荠商,查詢速度為 O(1)寂恬。

4.9 再說(shuō)一點(diǎn) API

Leonard Richardson、Mike Amundsen 和 Sam Ruby 的 RESTful Web APIs為網(wǎng)絡(luò) API 的用法提供了非常全面的理論與實(shí)踐指導(dǎo)莱没。另 外初肉,Mike Amundsen 的精彩視頻教學(xué)課程 Designing APIs for the Web,也可以教你創(chuàng)建自己的 API饰躲。如果你想把自己采集的數(shù)據(jù)用一種便捷的方式分享出來(lái)牙咏,他的視頻非常有用。

第 5 章 存儲(chǔ)數(shù)據(jù)

5.1 媒體文件

存儲(chǔ)媒體文件有兩種主要的方式:只獲取文件 URL 鏈接嘹裂,或者直接把源文件下載下來(lái)妄壶。
你可以通過(guò)媒體文件所在的 URL 鏈接直接引用它。
這樣做的優(yōu)點(diǎn)如下寄狼。
? 爬蟲(chóng)運(yùn)行得更快丁寄,耗費(fèi)的流量更少,因?yàn)橹灰溄硬蠢ⅲ恍枰螺d文件伊磺。
? 可以節(jié)省很多存儲(chǔ)空間,因?yàn)橹恍枰鎯?chǔ) URL 鏈接就可以删咱。
? 存儲(chǔ) URL 的代碼更容易寫屑埋,也不需要實(shí)現(xiàn)文件下載代碼。
? 不下載文件能夠降低目標(biāo)主機(jī)服務(wù)器的負(fù)載痰滋。

不過(guò)這么做也有一些缺點(diǎn)摘能。
? 這些內(nèi)嵌在你的網(wǎng)站或應(yīng)用中的外站 URL 鏈接被稱為盜鏈(hotlinking),使用盜鏈可 能會(huì)讓你麻煩不斷敲街,每個(gè)網(wǎng)站都會(huì)實(shí)施防盜鏈措施团搞。
? 因?yàn)槟愕逆溄游募趧e人的服務(wù)器上,所以你的應(yīng)用就要跟著別人的節(jié)奏運(yùn)行了多艇。
? 盜鏈?zhǔn)呛苋菀赘淖兊妮撼蟆H绻惆驯I鏈圖片放在博客上,要是被對(duì)方服務(wù)器發(fā)現(xiàn)墩蔓,很可能被惡搞梢莽。如果你把 URL 鏈接存起來(lái)準(zhǔn)備以后再用,可能用的時(shí)候鏈接已經(jīng)失效了奸披,或者是變成了完全無(wú)關(guān)的內(nèi)容昏名。
? 現(xiàn)實(shí)中的網(wǎng)絡(luò)瀏覽器不僅可以請(qǐng)求 HTML 頁(yè)面并切換頁(yè)面,它們也會(huì)下載訪問(wèn)頁(yè)面上所有的資源阵面。下載文件會(huì)讓你的爬蟲(chóng)看起來(lái)更像是人在瀏覽網(wǎng)站轻局,這樣做反而有好處洪鸭。

在 Python 3.x 版本中,urllib.request.urlretrieve 可以根據(jù)文件的 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 圖片仑扑,然后在程序運(yùn)行的文件夾里保存為 logo.jpg 文件览爵。
程序運(yùn)行注意事項(xiàng)
你知道從網(wǎng)上下載未知文件的那些警告嗎?這個(gè)程序會(huì)把頁(yè)面上所有的文件下載到你的硬盤里,可能會(huì)包含一些 bash 腳本镇饮、.exe 文件蜓竹,甚至可能是惡意 軟件(malware)。
如果你之前從沒(méi)有運(yùn)行過(guò)任何下載到電腦里的文件储藐,電腦就是安全的嗎?尤其是當(dāng)你用管理員權(quán)限運(yùn)行這個(gè)程序時(shí)俱济,你的電腦基本已經(jīng)處于危險(xiǎn)之中。 如果你執(zhí)行了網(wǎng)頁(yè)上的一個(gè)文件钙勃,那個(gè)文件把自己傳送到了 ../../../../usr/bin/ python 里面蛛碌,會(huì)發(fā)生什么呢?等下一次你再運(yùn)行 Python 程序時(shí),你的電腦就可能會(huì)安裝惡意軟件辖源。

5.2 把數(shù)據(jù)存儲(chǔ)到 CSV

CSV(Comma-Separated Values蔚携,逗號(hào)分隔值)是存儲(chǔ)表格數(shù)據(jù)的常用文件格式。Microsoft
Excel 和很多應(yīng)用都支持 CSV 格式克饶,因?yàn)樗芎?jiǎn)潔酝蜒。
和 Python 一樣,CSV 里留白(whitespace)也是很重要的:每一行都用一個(gè)換行符分隔彤路, 列與列之間用逗號(hào)分隔(因此也叫“逗號(hào)分隔值”)秕硝。CSV 文件還可以用 Tab 字符或其他字符分隔行芥映,但是不太常見(jiàn)洲尊,用得不多。
Python 的 csv 庫(kù)可以非常簡(jiǎn)單地修改 CSV 文件奈偏,甚至從零開(kāi)始創(chuàng)建一個(gè) 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()

這里提個(gè)醒兒:Python 新建文件的機(jī)制考慮得非常周到(bullet-proof)坞嘀。如果 ../files/test.csv 不存在,Python 會(huì)自動(dòng)創(chuàng)建文件(不會(huì)自動(dòng)創(chuàng)建文件夾)惊来。如果文件已經(jīng)存在丽涩,Python 會(huì) 用新的數(shù)據(jù)覆蓋 test.csv 文件。
實(shí)際工作中寫此程序之前的注意事項(xiàng)
如果你有很多 HTML 表格裁蚁,且每個(gè)都要轉(zhuǎn)換成 CSV 文件矢渊,或者許多 HTML 表格都要匯總到一個(gè) CSV 文件,那么把這個(gè)程序整合到爬蟲(chóng)里以解決問(wèn)題非常好枉证。但是矮男,如果你只需要做一次這種事情,那么更好的辦法就是:復(fù)制粘貼室谚。選擇 HTML 表格內(nèi)容然后粘貼到 Excel 文件里毡鉴,可以另存為 CSV 格式崔泵,不需要寫代碼就能搞定!

5.3 MySQL

MySQL是目前最受歡迎的開(kāi)源關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng)。一個(gè)開(kāi)源項(xiàng)目具有如此之競(jìng)爭(zhēng)力實(shí)在是令人意外猪瞬,它的流行程度正在不斷地接近另外兩個(gè)閉源的商業(yè)數(shù)據(jù)庫(kù)系統(tǒng):微軟的 SQL Server 和甲骨文的 Oracle 數(shù)據(jù)庫(kù)(MySQL 在 2010 年被甲骨文收購(gòu))憎瘸。
它的流程程度實(shí)在是名符其實(shí)。對(duì)大多數(shù)應(yīng)用來(lái)說(shuō)陈瘦,MySQL 都是不二選擇幌甘。它是一種 非常靈活、穩(wěn)定甘晤、功能齊全的 DBMS含潘,許多頂級(jí)的網(wǎng)站都在用它:YouTube、Twitter 和 Facebook 等线婚。
因?yàn)樗鼙姀V泛遏弱,免費(fèi),開(kāi)箱即用塞弊,所以它也是網(wǎng)絡(luò)數(shù)據(jù)采集項(xiàng)目中常用的數(shù)據(jù)庫(kù)漱逸,

5.3.1 安裝 MySQL

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

      $brew install mysql

Mac OS X 的 MySQL 安裝好之后饰抒,你可以用下面的命令啟動(dòng) MySQL 服務(wù)器:

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

5.3.2 基本命令

MySQL 服務(wù)器啟動(dòng)之后,有很多種方法可以與數(shù)據(jù)庫(kù)交互诀黍。因?yàn)橛泻芏喙ぞ呤菆D形界面袋坑,所以你不用 MySQL 的命令行(或者很少用命令行)也能管理數(shù)據(jù)庫(kù)。像 phpMyAdmin 和 MySQL Workbench 這類工具都可以很容易地實(shí)現(xiàn)數(shù)據(jù)的查看眯勾、排序和新建等工作枣宫。但是,掌握命令行操作數(shù)據(jù)庫(kù)依然是很重要的吃环。
除了用戶自定義變量名(MySQL 5.x 版本是不區(qū)分大小寫的也颤,MySQL 5.0 之前的版本是不區(qū)分大小寫的),MySQL 語(yǔ)句是不區(qū)分大小寫的郁轻。
如果你對(duì)這個(gè)強(qiáng)大數(shù)據(jù)庫(kù)的命令和技術(shù)感興趣翅娶,推薦你去看 Paul DuBois 的 MySQL Cookbook

5.3.3 與 Python 整合

Python 沒(méi)有內(nèi)置的 MySQL 支持工具好唯。不過(guò)竭沫,有很多開(kāi)源的庫(kù)可以用來(lái)與 MySQL 做交互, Python 2.x 和 Python 3.x 版本都支持骑篙。最有名的一個(gè)庫(kù)就是 PyMySQL蜕提。
寫到這里的時(shí)候,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

如果需要更新贯溅,請(qǐng)檢查最新版的 PyMySQL拄氯,并修改第一行下載鏈接中的版本號(hào)進(jìn)行更新。 安裝完成之后它浅,你就可以使用 PyMySQL 包了译柏。如果你的 MySQL 服務(wù)器處于運(yùn)行狀態(tài),
應(yīng)該就可以成功地執(zhí)行下面的命令(記得把 root 賬戶密碼加進(jìn)去):

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()

這段程序有兩個(gè)對(duì)象:連接對(duì)象(conn)和光標(biāo)對(duì)象(cur)姐霍。
連接 / 光標(biāo)模式是數(shù)據(jù)庫(kù)編程中常用的模式鄙麦,不過(guò)剛剛接觸數(shù)據(jù)庫(kù)的時(shí)候渡蜻,有些用戶很難區(qū)分兩種模式的不同混蔼。連接模式除了要連接數(shù)據(jù)庫(kù)之外勇皇,還要發(fā)送數(shù)據(jù)庫(kù)信息莲绰,處理回滾操作(當(dāng)一個(gè)查詢或一組查詢被中斷時(shí),數(shù)據(jù)庫(kù)需要回到初始狀態(tài)贸诚,一般用事務(wù)控制手段實(shí)現(xiàn)狀態(tài)回滾)徘键,創(chuàng)建新的光標(biāo)對(duì)象绍哎,等等赃泡。
而一個(gè)連接可以有很多個(gè)光標(biāo)寒波。一個(gè)光標(biāo)跟蹤一種狀態(tài)(state)信息,比如跟蹤數(shù)據(jù)庫(kù)的使用狀態(tài)升熊。如果你有多個(gè)數(shù)據(jù)庫(kù)俄烁,且需要向所有數(shù)據(jù)庫(kù)寫內(nèi)容,就需要多個(gè)光標(biāo)來(lái)處理级野。 光標(biāo)還會(huì)包含最后一次查詢執(zhí)行的結(jié)果页屠。通過(guò)調(diào)用光標(biāo)函數(shù),比如 cur.fetchone()蓖柔,可以獲取查詢結(jié)果辰企。
用完光標(biāo)和連接之后,千萬(wàn)記得把它們關(guān)閉渊抽。如果不關(guān)閉就會(huì)導(dǎo)致連接泄漏(connection leak)蟆豫,造成一種未關(guān)閉連接現(xiàn)象议忽,即連接已經(jīng)不再使用懒闷,但是數(shù)據(jù)庫(kù)卻不能關(guān)閉,因?yàn)閿?shù)據(jù)庫(kù)不能確定你還要不要繼續(xù)使用它栈幸。這種現(xiàn)象會(huì)一直耗費(fèi)數(shù)據(jù)庫(kù)的資源愤估,所以用完數(shù)據(jù) 庫(kù)之后記得關(guān)閉連接!
在進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)采集時(shí),處理 Unicode 字符串是很痛苦的事情速址。默認(rèn)情況下玩焰,MySQL 也 不支持 Unicode 字符處理。不過(guò)你可以設(shè)置這個(gè)功能(這么做會(huì)增加數(shù)據(jù)庫(kù)的占用空間)芍锚。 因?yàn)樵诰S基百科上我們難免會(huì)遇到各種各樣的字符昔园,所以最好一開(kāi)始就讓你的數(shù)據(jù)庫(kù)支持 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;

這四行語(yǔ)句改變的內(nèi)容有:數(shù)據(jù)庫(kù)蔓榄、數(shù)據(jù)表,以及兩個(gè)字段的默認(rèn)編碼都從 utf8mb4 (嚴(yán)格說(shuō)來(lái)也屬于 Unicode默刚,但是對(duì)大多數(shù) Unicode 字符的支持都非常不好)轉(zhuǎn)變成了 utf8mb4_unicode_ci甥郑。
你可以在 title 或 content 字段中插入一些德語(yǔ)變音符(umlauts)或漢語(yǔ)字符,如果沒(méi)有
錯(cuò)誤就表示轉(zhuǎn)換成功了荤西。
雖然 PyMySQL 規(guī)模并不大澜搅,但是里面有一些非常實(shí)用的函數(shù)本書并沒(méi)有介紹。具體請(qǐng)參考 Python 的 DBAPI 標(biāo)準(zhǔn)文檔邪锌。

5.3.4 數(shù)據(jù)庫(kù)技術(shù)與最佳實(shí)踐

和計(jì)算機(jī)科學(xué)的很多主題一樣勉躺,有一些技巧你其實(shí)可以很快地學(xué)會(huì),它們可以讓你的數(shù)據(jù)庫(kù)變得更高效觅丰,讓應(yīng)用的運(yùn)行速度更快饵溅。
首先,給每個(gè)數(shù)據(jù)表都增加一個(gè) id 字段妇萄,不會(huì)出什么問(wèn)題概说。MySQL 里所有的表都至少有一個(gè)主鍵(就是 MySQL 用來(lái)排序的字段),因此 MySQL 知道怎么組織主鍵嚣伐,通常數(shù)據(jù)庫(kù)很難智能地選擇主鍵糖赔。究竟是用人造的 id 字段作為主鍵,還是用那些具有唯一性屬性的字段作為主鍵轩端,比如 username 字段放典,數(shù)據(jù)科學(xué)家和軟件工程師已經(jīng)爭(zhēng)論了很多年,但我更傾向于主動(dòng)創(chuàng)建一個(gè) id 字段基茵。這樣做的原因一兩句話難以說(shuō)清奋构,不過(guò)對(duì)于一些非企業(yè)級(jí)系統(tǒng)的數(shù)據(jù)庫(kù),你還是應(yīng)該用自增的 id 字段作為主鍵拱层。
其次弥臼,用智能索引。字典(指的是常用的工具書根灯,不是指 Python 的字典對(duì)象)是按照字母順序排列的單詞表径缅。這樣做讓你在任何時(shí)候都能快速地找到一個(gè)單詞,只要你知道這個(gè)單詞是如何拼寫的就行烙肺。
最后一點(diǎn)是關(guān)于數(shù)據(jù)查詢時(shí)間和數(shù)據(jù)庫(kù)空間的問(wèn)題纳猪。一個(gè)常見(jiàn)的誤區(qū)就是在數(shù)據(jù)庫(kù)中存儲(chǔ)大量重復(fù)數(shù)據(jù),尤其是在做大量自然語(yǔ)言數(shù)據(jù)的網(wǎng)絡(luò)數(shù)據(jù)采集任務(wù)時(shí)桃笙。
除非你安裝了第三方包或保存詳細(xì)的數(shù)據(jù)庫(kù)日志氏堤,否則你無(wú)法掌握數(shù)據(jù)庫(kù)里數(shù)據(jù)增加、更新或刪除的具體時(shí)間搏明。因此鼠锈,如果需要對(duì)數(shù)據(jù)可用的空間闪檬、變更的頻率和變更的重要性進(jìn)行分析,你應(yīng)該考慮在數(shù)據(jù)新增购笆、更新或刪除時(shí)加一個(gè)時(shí)間戳谬以。

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

5.4 Email

與網(wǎng)頁(yè)通過(guò) HTTP 協(xié)議傳輸一樣,郵件是通過(guò) SMTP(Simple Mail Transfer Protocol由桌,簡(jiǎn)單郵件傳輸協(xié)議)傳輸?shù)奈琛6遥湍阌镁W(wǎng)絡(luò)服務(wù)器的客戶端(瀏覽器)處理那些通過(guò) HTTP 協(xié)議傳輸?shù)木W(wǎng)頁(yè)一樣行您,Email 服務(wù)器也有客戶端铭乾,像 Sendmail、Postfix 和 Mailman 等娃循,都可以收發(fā)郵件炕檩。
雖然用 Python 發(fā)郵件很容易,但是需要你連接那些正在運(yùn)行 SMTP 協(xié)議的服務(wù)器捌斧。
下面的代碼運(yùn)行的前提是你的電腦已經(jīng)可以正常地運(yùn)行一個(gè) SMTP 客戶端笛质。(如果要調(diào)整代碼用于遠(yuǎn)程 SMTP 客戶端,請(qǐng)把 localhost 改成遠(yuǎn)程服務(wù)器地址捞蚂。)
用 Python 發(fā)一封郵件只要 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 有兩個(gè)包可以發(fā)送郵件:smtplib 和 email妇押。
Python 的 email 模塊里包含了許多實(shí)用的郵件格式設(shè)置函數(shù),可以用來(lái)創(chuàng)建郵件“包 裹”姓迅。下面的示例中使用的 MIMEText 對(duì)象敲霍,為底層的 MIME(Multipurpose Internet Mail Extensions,多用途互聯(lián)網(wǎng)郵件擴(kuò)展類型)協(xié)議傳輸創(chuàng)建了一封空郵件丁存,最后通過(guò)高層的 SMTP 協(xié)議發(fā)送出去肩杈。MIMEText 對(duì)象 msg 包括收發(fā)郵箱地址、郵件正文和主題解寝,Python 通 過(guò)它就可以創(chuàng)建一封格式正確的郵件扩然。
smtplib 模塊用來(lái)設(shè)置服務(wù)器連接的相關(guān)信息。就像 MySQL 服務(wù)器的連接一樣聋伦,這個(gè)連接必須在用完之后及時(shí)關(guān)閉夫偶,以避免同時(shí)創(chuàng)建太多連接而浪費(fèi)資源。

第 6 章 讀取文檔

互聯(lián)網(wǎng)最基本的特征:作為不同類型文件的傳輸媒介嘉抓。
互聯(lián)網(wǎng)并不是一個(gè) HTML 頁(yè)面的集合索守。它是一個(gè)信息集合晕窑,而 HTML 文件只是展示信息的一個(gè)框架而已抑片。如果我們的爬蟲(chóng)不能讀取其他類型的文件,包括純文本杨赤、 PDF敞斋、圖像截汪、視頻、郵件等植捎,我們將會(huì)失去很大一部分?jǐn)?shù)據(jù)衙解。
本章重點(diǎn)介紹文檔處理的相關(guān)內(nèi)容,包括把文件下載到文件夾里焰枢,以及讀取文檔并提取數(shù)據(jù)蚓峦。我們還會(huì)介紹文檔的不同編碼類型,讓程序可以讀取非英文的 HTML 頁(yè)面济锄。

6.1 文檔編碼

文檔編碼是一種告訴程序——無(wú)論是計(jì)算機(jī)的操作系統(tǒng)還是 Python 代碼——讀取文檔的規(guī)則暑椰。文檔編碼的方式通常可以根據(jù)文件的擴(kuò)展名進(jìn)行判斷荐绝,雖然文件擴(kuò)展名并不是由編碼確定的一汽,而是由開(kāi)發(fā)者確定的。
從最底層的角度看低滩,所有文檔都是由 0 和 1 編碼而成的召夹。而在高層(貼近用戶的層級(jí)), 編碼算法會(huì)定義“每個(gè)字符多少位”或“每個(gè)像素的顏色值用多少位”(圖像文件里)之類的事情恕沫,在那里你會(huì)遇到一些數(shù)據(jù)壓縮算法或體積縮減算法监憎,比如 PNG 圖像編碼格式 (一種無(wú)損壓縮的位圖圖形式)。
純文本文件婶溯、視頻文件和圖像文件的唯一區(qū)別枫虏, 就是它們的 0 和 1 面向用戶的轉(zhuǎn)換方式不同。### 6.2 純文本
對(duì)大多數(shù)簡(jiǎn)單的純文本文件爬虱,像 http://www.pythonscraping.com/pages/warandpeace/chapter1. txt 這個(gè)練習(xí)文件隶债,你可以用下面的方法讀取:

from urllib.request import urlopen 


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

通常,當(dāng)用 urlopen 獲取了網(wǎng)頁(yè)之后跑筝,我們會(huì)把它轉(zhuǎn)變成 BeautifulSoup 對(duì)象死讹,方便后面對(duì) HTML 進(jìn)行分析。在這段代碼中曲梗,我們直接讀取頁(yè)面內(nèi)容赞警。你可能覺(jué)得,如果把它轉(zhuǎn)變成 BeautifulSoup 對(duì)象應(yīng)該也不錯(cuò)虏两,但那樣做其實(shí)適得其反——這個(gè)頁(yè)面不是 HTML愧旦,所以 BeautifulSoup 庫(kù)就沒(méi)用了。一旦純文本文件被讀成字符串定罢,你就只能用普通 Python 字符串的方法分析它了笤虫。當(dāng)然,這么做有個(gè)缺點(diǎn),就是你不能對(duì)字符串使用 HTML 標(biāo)簽琼蚯,去定位那些你真正需要的文字酬凳,避開(kāi)那些你不需要的文字。如果現(xiàn)在你想從純文本文件中抽取某些信息遭庶,還是有些難度的宁仔。
文本編碼和全球互聯(lián)網(wǎng)
大多數(shù)時(shí)候用前面的方法讀取純文本文件都沒(méi)問(wèn)題。但是峦睡,互聯(lián)網(wǎng)上的文本文件會(huì)比較復(fù)雜翎苫。下面介紹一些英文和非英文編碼的基礎(chǔ)知識(shí),包括 ASCII榨了、Unicode 和 ISO 編碼拉队,以及對(duì)應(yīng)的處理方法。

  1. 編碼類型簡(jiǎn)介
    UTF-8阻逮,全稱是“Universal Character Set - Transformation Format 8 bit”粱快,即“統(tǒng)一字符集 - 轉(zhuǎn)換格式 8 位”。一個(gè)常見(jiàn)的誤解是 UTF-8 把所有字符都存儲(chǔ)成 8 位叔扼。其實(shí)“8 位”只是顯示一個(gè)字符需要的最小位數(shù)事哭,而不是最大位數(shù)。
    真實(shí)情況是瓜富,UTF-8 的每個(gè)字符開(kāi)頭有一個(gè)標(biāo)記表示“這個(gè)字符只用一個(gè)字節(jié)”或“那個(gè)字符需要用兩個(gè)字節(jié)”鳍咱,一個(gè)字符最多可以是四個(gè)字節(jié)。由于這四個(gè)字節(jié)里還包含一部分設(shè)置信息与柑,用來(lái)決定多少字節(jié)用做字符編碼谤辜,所以全部的 32 位(32 位 =4 字節(jié) ×8 位 / 字 節(jié))并不會(huì)都用,其實(shí)最多使用 21 位价捧,也就是總共 2 097 152 種可能里面可以有 1 114 112 個(gè)字符丑念。
    雖然對(duì)很多程序來(lái)說(shuō),Unicode 都是上帝的禮物(godsend)结蟋,但是有些習(xí)慣很難改變脯倚, ASCII 依然是許多英文用戶的不二選擇。
    ASCII 是 20 世紀(jì) 60 年代開(kāi)始使用的文字編碼標(biāo)準(zhǔn)嵌屎,每個(gè)字符 7 位推正,一共 27,即 128 個(gè)字 符宝惰。這對(duì)于拉丁字母(包括大小寫)植榕、標(biāo)點(diǎn)符號(hào)和英文鍵盤上的所有符號(hào),都是夠用的尼夺。
    除了 UTF-8尊残,還有其他 UTF 標(biāo)準(zhǔn)炒瘸,像 UTF-16、UTF-24夜郁、UTF-32什燕,不過(guò)很少用這些編碼標(biāo) 準(zhǔn)對(duì)文件進(jìn)行編碼粘勒。
    Unicode 標(biāo)準(zhǔn)也有問(wèn)題竞端,就是任何一種非英文語(yǔ)言文檔的體積都比 ASCII 編碼的體積大。雖然你的語(yǔ)言可能只需要用大約 100 個(gè)字符庙睡,像英文的 ASCII 編碼事富,8 位就夠了, 但是因?yàn)槭怯?UTF-8 編碼乘陪,所以你還是得用至少 16 位表示每個(gè)字符统台。這會(huì)讓非英文的純文本文檔體積差不多達(dá)到英文文檔的兩倍,對(duì)那些不用拉丁字符集的語(yǔ)言來(lái)說(shuō)都是如此啡邑。
    ISO 標(biāo)準(zhǔn)解決這個(gè)問(wèn)題的辦法是為每種語(yǔ)言創(chuàng)建一種編碼贱勃。和 Unicode 不同,它使用了與 ASCII 相同的編碼谤逼,但是在每個(gè)字符的開(kāi)頭用 0 作“填充位”贵扰,這樣就可以讓語(yǔ)言在需要的時(shí)候創(chuàng)建特殊字符。
    雖然這些年 ISO 編碼標(biāo)準(zhǔn)的使用率一直在下降流部,但是目前仍有約 9% 的網(wǎng)站使用 ISO 編碼戚绕,所以有必要做基本的了解,并在采集網(wǎng)站之前需要檢查是否使用了這種編碼方法枝冀。
  2. 編碼進(jìn)行時(shí)
    Python 默認(rèn)把文本讀成 ASCII 編碼格式
    處理 HTML 頁(yè)面的時(shí)候舞丛,網(wǎng)站其實(shí)會(huì)在 <head> 部分顯示頁(yè)面使用的編碼格式。大多數(shù)網(wǎng)站果漾,尤其是英文網(wǎng)站球切,都會(huì)帶這樣的標(biāo)簽:
    <meta charset="utf-8" />
    如果你要做很多網(wǎng)絡(luò)數(shù)據(jù)采集工作,尤其是面對(duì)國(guó)際網(wǎng)站時(shí)绒障,建議你先看看 meta 標(biāo)簽的內(nèi)容欧聘,用網(wǎng)站推薦的編碼方式讀取頁(yè)面內(nèi)容。

6.3 CSV

Python 有一個(gè)超贊的標(biāo)準(zhǔn)庫(kù)可以讀寫 CSV 文件端盆。
讀取CSV文件
Python 的 csv 庫(kù)主要是面向本地文件怀骤,就是說(shuō)你的 CSV 文件得存儲(chǔ)在你的電腦上。而進(jìn)
行網(wǎng)絡(luò)數(shù)據(jù)采集的時(shí)候焕妙,很多文件都是在線的蒋伦。不過(guò)有一些方法可以解決這個(gè)問(wèn)題:
? 手動(dòng)把 CSV 文件下載到本機(jī),然后用 Python 定位文件位置;
? 寫 Python 程序下載文件焚鹊,讀取之后再把源文件刪除;
? 從網(wǎng)上直接把文件讀成一個(gè)字符串痕届,然后轉(zhuǎn)換成一個(gè) StringIO 對(duì)象韧献,使它具有文件的屬性。
下面的程序就是從網(wǎng)上獲取一個(gè) CSV 文件(這里用的是 http://pythonscraping.com/files/MontyPythonAlbums.csv 里的 Monty Python 樂(lè)團(tuán)的專輯列表)研叫,然后把每一行都打印到命令行里:

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 處理第一行(標(biāo)題行)
csv.DictReader 會(huì)返回把 CSV 文件每一行轉(zhuǎn)換成 Python 的字典對(duì)象返回锤窑,而不是列表對(duì)
象,并把字段列表保存在變量 dictReader.fieldnames 里嚷炉,字段列表同時(shí)作為字典對(duì)象的鍵

6.4 PDF

PDF 格式(Portable Document Format渊啰,便攜式文檔格式)是一種技術(shù)革命。 PDF 可以讓用戶在不同的系統(tǒng)上用同樣的方式查看圖片和文本文檔申屹,無(wú)論這些文件是在哪種系統(tǒng)上制作的绘证。
雖然把 PDF 顯示在網(wǎng)頁(yè)上已經(jīng)有點(diǎn)兒過(guò)時(shí)了(你已經(jīng)可以把內(nèi)容顯示成 HTML 了,為什么還要用這種靜態(tài)哗讥、加載速度超慢的格式呢?)嚷那,但是 PDF 仍然無(wú)處不在,尤其是在處理 商務(wù)報(bào)表和表單的時(shí)候杆煞。
PDFMiner3K 就是一個(gè)非常好用的庫(kù)(是 PDFMiner 的 Python 3.x 移植版)魏宽。它非常靈活, 可以通過(guò)命令行使用决乎,也可以整合到代碼中队询。它還可以處理不同的語(yǔ)言編碼,而且對(duì)網(wǎng)絡(luò) 文件的處理也非常方便瑞驱。
你可以下載這個(gè)模塊的源文件娘摔,解壓并用下面命 令安裝:

     $python setup.py install

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

6.5 微軟 Word 和 .docx

Python 對(duì) 這 種 Google Docs、Open Office 和 Microsoft Office 都在使用的 .docx 格式的支持 還不夠好彤侍。 雖然有一個(gè) python-docx 庫(kù)(http://python-docx.readthedocs.org/en/ latest/)肠缨,但是只支持創(chuàng)建新文檔和讀取一些基本的文件數(shù)據(jù),如文件大小和文件標(biāo)題盏阶,不支持正文讀取晒奕。如果想讀取 Microsoft Office 文件的正文內(nèi)容,我們需要自己動(dòng)手找方法名斟。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末脑慧,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子砰盐,更是在濱河造成了極大的恐慌闷袒,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件岩梳,死亡現(xiàn)場(chǎng)離奇詭異囊骤,居然都是意外死亡晃择,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門也物,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)宫屠,“玉大人,你說(shuō)我怎么就攤上這事滑蚯±缩澹” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵膘魄,是天一觀的道長(zhǎng)乌逐。 經(jīng)常有香客問(wèn)我竭讳,道長(zhǎng)创葡,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任绢慢,我火速辦了婚禮灿渴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘胰舆。我一直安慰自己骚露,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布缚窿。 她就那樣靜靜地躺著棘幸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪倦零。 梳的紋絲不亂的頭發(fā)上误续,一...
    開(kāi)封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音扫茅,去河邊找鬼蹋嵌。 笑死,一個(gè)胖子當(dāng)著我的面吹牛葫隙,可吹牛的內(nèi)容都是我干的栽烂。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼恋脚,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼腺办!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起糟描,我...
    開(kāi)封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤怀喉,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后蚓挤,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體磺送,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡驻子,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了估灿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片崇呵。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖馅袁,靈堂內(nèi)的尸體忽然破棺而出域慷,到底是詐尸還是另有隱情,我是刑警寧澤汗销,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布犹褒,位于F島的核電站,受9級(jí)特大地震影響弛针,放射性物質(zhì)發(fā)生泄漏叠骑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一削茁、第九天 我趴在偏房一處隱蔽的房頂上張望宙枷。 院中可真熱鬧,春花似錦茧跋、人聲如沸慰丛。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)诅病。三九已至,卻和暖如春粥烁,著一層夾襖步出監(jiān)牢的瞬間贤笆,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工页徐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留苏潜,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓变勇,卻偏偏與公主長(zhǎng)得像恤左,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子搀绣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容