1. 程式人生 > 其它 >爬蟲系列:讀取文件

爬蟲系列:讀取文件

上一篇文章我們介紹瞭如何通過 MySQL 儲存 Python 爬蟲採集的內容,以及使用Python 與 MySQL 互動,這篇文章我們介紹如何通過 Python 讀取文件。

雖然網際網路在20世紀60年代末期就已經以不同的形式出現,但是 HTML 直到1992年才問世。在此之前,網際網路上基本就是收發郵件傳輸檔案;今天看到的網頁概念那時還沒有。總之,網際網路並不是一個 HTML 頁面的集合。他是一個資訊集合,而 HTML 檔案只是展示資訊的一個框架而已。如果我們的爬蟲不能讀取其他型別的檔案,包括純文字、PDF、影象、視訊、郵件等,我們將會失去很大一部分資料。

本篇文章我將詳細介紹文件處理的相關內容,包括把檔案下載到資料夾裡,以及讀取文件並提取資料。同時介紹文件不同編碼型別,讓程式可以讀取非英文 HTML 頁面。

文件編碼

文件編碼是一種告訴程式——無論是計算機的作業系統還是 Python 程式碼——讀取文件的規則。文件的編碼方式通常可以根據檔案的副檔名進行判斷,雖然副檔名並不是由編碼確定的,而是由開發者確定的。例如,如果我把 python_logo.jpg 儲存為 python_logo.txt 不會出現任何問題,但當我使用文字編輯器開啟的時候就有問題了。這種情況很少見,如果要正確的讀取一個文件,必須知道它的副檔名。

從最底層的角度看,所有文件都是由0和1編碼而成的。而在高層(貼近使用者的層級)編碼演算法會定義“每個字元多少位”或“每個畫素的顏色值用多少位”(影象檔案裡)之類的事情,在哪裡你會遇到一些資料壓縮演算法或體積縮減演算法,比如 PNG 影象編碼格式(一種無失真壓縮的點陣圖圖形格式)。

雖然我們第一次處理這些非 HTML 格式的檔案會覺得沒有任何經驗,但是隻要安裝了合適的庫,Python 就可以幫你處理任意型別的文件。純文字檔案、視訊檔案和影象檔案的唯一區別,就是他們的0和1面向使用者的轉換方式不同。

純文字

雖然把檔案儲存為線上的純文字格式並不常見,但是一些簡易的網站,或者有大量純文字檔案的“舊式學術”(old-shcool)網站經常會這麼做。例如,網際網路工程任務組(Internet Engineering Task Force,IETF)網站就儲存了 IETF 發表過的所有文件,包含 HTML、PDF 和純文字格式(例如https://datatracker.ietf.org/doc/html/rfc4437.txt

)。大多數瀏覽器都可以很好的顯示純文字檔案,採集這些純文字檔案的網站不會遇到什麼問題。

下面一個 Python 讀取純文字示例,展示瞭如何讀取https://image.pdflibr.com/crawler/blog/tencent_cloud_ip_range.txt地址的純文字檔案。

from requests import Session


class ReadDocument(object):
    def __init__(self):
        self._text_url = 'https://image.pdflibr.com/crawler/blog/tencent_cloud_ip_range.txt'

    def read_text_document(self):
        init_session = Session()
        response = init_session.get(url=self._text_url)
        response.encoding = 'utf-8'
        print(response.text)


if __name__ == '__main__':
    ReadDocument().read_text_document()

這段 Python 程式碼,我們直接讀取文字內容,並對文字從新編碼,如果使用原來的編碼方式,顯示為亂碼。一旦純文字被讀取成字串,你就只能用普通的 Python 字串方法分析他了。當然這沒做有個缺點,就是你不能對字串使用 HTML 標籤,去定位那些你真正需要的文字,避開那些你不需要的文字。如果你現在需要在純文本里面找到你需要的資訊還是有困難的。

文字編碼和全球網際網路

記得我前面說過,如果你想正確的讀取一個檔案,知道它的副檔名就可以了。不過非常奇怪的是,這條規則不能應用到最基本的文件格式:.txt 檔案。

大多數時候前面的方法讀取純文字檔案都沒有問題。但是,護糧網上的文字檔案會比較複雜。下面介紹一些英文和非英文編碼的基礎知識,包括 ASCII、Unicode 和 ISO 編碼,以及應對的處理方法。

編碼型別簡介

20世紀90年代,一個叫 Unicode 聯盟(The Unicode Consortium)的非盈利組織嘗試將地球所有的用於書寫的符號經行統一編碼。其目標包括拉丁字母、斯拉夫字母、中國象形文字(象形)、數字和邏輯符號,甚至表情和“雜項”(misellaneous)符號,比如生化危機標記(☣)和和平符號(☮)等。

UTF-8(8-bit Unicode Transformation Format)是一種針對 Unicode 的可變長度字元編碼,也是一種字首碼。它可以用一至四個位元組對 Unicode 字符集中的所有有效編碼點進行編碼,屬於U nicode 標準的一部分,最初由肯·湯普遜和羅布·派克提出。

一個最常見的錯誤就是 UTF-8 把所有的字元都儲存成8位。其實“8位”顯示一個字元所需要的最小位數,而不是最大位數。(如果 UTF-8 的每個字元都是8位,,那一共只能儲存2^8個字元,這對於中文和其他字元顯然不夠。)

真實情況是,UTF-8每個字元開頭都有一個標記表示“這個字元只用一個位元組”或“那個字元需要兩個位元組”,一個字元最多可以是四個位元組。由於這四個位元組裡面還包含一部分設定資訊,用來決定多少位元組用來做字元編碼,所以全部的32位(32位=4位元組x8位/位元組)並不都會用,其實最多使用21位,也就是總共2097152種可能裡面可以有1114112個字元。

雖然對很多程式來說,Unicode 都是上帝的禮物(godsend),但是有很多習慣都很難改變, ASCII 依然是許多英文使用者的主要選擇。

ASCII 是20世紀60年代開始使用的文字編碼標準,每個字元7位,一共2^7,即128個字元。這個對於拉丁字母(包括大小寫)、標點符號和英文鍵盤上的所有符號,都是夠用的。

在20世紀60年代,儲存的檔案用7位編碼和用8位編碼之間的差異是巨大的,因為記憶體非常昂貴。當時,電腦科學家們為了需要增加一位獲得一個漂亮的二進位制數(用8位),還是在檔案裡用更少的位數(7位)費盡心機。最終,7位編碼勝利了。但是,在新式計算方式中,每個7位碼前面都補充(pad)了一個“0”,留給我們最壞的結果是,檔案大了14%(編碼由7為變成8位,體積增加了14%),並且由於只有128個字元,缺乏靈活性。

在 UTF-8 設計過程中,設計師決定利用 ASCII 文件裡的“填充位”,讓所有“0”開頭的位元組表示這個字元自用1個位元組,從而把 ASCII 和 UTF-8 編碼完美的結合在一起。因此,下面的字元在 ASCII 和 UTF-8 兩種編碼方式中都是有效的:

01000001 - A
01000010 - B
01000011 - C

除了 UTF-8,還有其他 UTF 標準,像 UTF-16、UTF-24、UTF-32,不過很少用這些編碼標準對檔案經行編碼,所以在此不做過多介紹。

Python 編碼示例

在上面的例項中我們通過 Python 的 requests 庫讀取了遠端的文件內容,但是顯示的是亂碼,無法閱讀,我們對文件內容重新設定編碼,使其正常顯示,示例如下:

from requests import Session


class ReadDocument(object):
    def __init__(self):
        self._text_url = 'https://image.pdflibr.com/crawler/blog/tencent_cloud_ip_range.txt'

    def read_text_document(self):
        init_session = Session()
        response = init_session.get(url=self._text_url)
        # 顯示原來文字的編碼方式
        print(response.encoding)
        # 將文字設定成 utf-8 的編碼方式
        response.encoding = 'utf-8'
        print(response.text)
        # 顯示改變編碼後的編碼方式
        print(response.encoding)


if __name__ == '__main__':
    ReadDocument().read_text_document()

上面的程式碼首先打印出了原來文件採用的編碼方式,之後將文件重設成 UTF-8 的編碼方式,之後再次列印文件內容,再次顯示被編碼後的文件編碼。

你可能打算以後使用網路爬蟲全部採用 UTF-8 編碼讀取內容,畢竟 UTF-8 也可以完美的處理 ASCII 編碼。但是,要記住還有9%的網站使用 ISO 編碼格式。所以在處理純文字文件的時候,想用一種編碼搞定所有文件是不可能的。有一些庫可以檢查文件的編碼,或是對文件編碼經行估計,不過效果並不是很好。

處理 HTML 的時候,網站其實會在<head>部分顯示頁面使用的編碼格式。大多數網站,尤其是英文網站,都會帶上這樣的標籤:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

如果你要做很多網路資料採集工作,尤其是面對國際網站時,建議先看看 meta 標籤的內容,用網站推薦的方式讀取頁面內容。