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

爬蟲系列:讀取 CSV、PDF、Word 文件

上一期我們講解了使用 Python 讀取文件編碼的相關問題,本期我們講解使用 Python 處理 CSV、PDF、Word 文件相關內容。

CSV

我們進行網頁採集的時候,你可能會遇到 CSV 檔案,也可能專案需要將資料儲存到 CSV 檔案。Python 有一個超讚的標準庫可以讀寫 CSV 檔案。雖然這個庫可以處理各種 CSV 檔案,但是我們這裡重點介紹標準 CSV 格式。

讀取 CSV 檔案

Python 的 CSV 主要是面向本地使用者,也就是說你的 CSV 檔案得儲存到你的電腦上。而經行網路資料採集的時候,很多檔案都是線上的。不過有一些方法可以解決這個問題:

  • 手動把 CSV 檔案下載到本機,然後用 Python 定位檔案位置;

  • 寫 Python 程式下載檔案,讀取之後把原始檔刪除;

  • 從網上直接把檔案讀取成一個字串,然後轉換成一個 StringIO 物件,使它具有檔案的屬性。

雖然前兩個方法也可以用,但是既然你可以輕易的把 CSV 檔案儲存到記憶體裡,就不要下載到本地佔用硬碟空間了。直接把檔案讀取成字串,然後封裝成 StringIO 物件,讓 Python 把他當作檔案來處理,就不需要儲存成檔案了。下面的程式就是從網上獲取一個 CSV 檔案,然後把每一行都列印到命令列裡:

import requests
from io import StringIO
import csv


class ProcessCSVPDFDOCX(object):
    def __init__(self):
        self._csv_path = 'https://image.pdflibr.com/crawler/blog/country.CSV'
        self._session = requests.Session()

    def read_csv(self):
        response = self._session.get(self._csv_path)
        # 將文字設定成 utf-8 的編碼方式
        response.encoding = 'utf-8'
        response_text = response.text
        data_file = StringIO(response_text)
        dict_reader = csv.DictReader(data_file)

        print(dict_reader.fieldnames)

        for row in dict_reader:
            print(row)


if __name__ == '__main__':
    ProcessCSVPDFDOCX().read_csv()

csv.DictReader會返回把 CSV 檔案每一行轉化成 Python 的字典物件返回,而不是列表物件,並把欄位列表儲存到變數dict_reader.fieldnames裡,欄位同時作為字典物件的鍵。

PDF

從某種意義上來說, Adobe 在 1993 年發明 PDF 格式(Protable Document Format,行動式文件格式)是一種技術革命。PDF 可以讓使用者在不同系統上使用同樣的方式檢視圖片和文字文件,無論這種檔案是在那種系統上製作的。

雖然把 PDF 顯示在網頁上已經過時了(你已經可以把內容顯示成 HTML 了,為什麼還要這種靜態、載入速度超慢的格式呢?),但是 PDF 仍然無處不在,尤其是在處理商務報表和表單的時候。

目前很多 PDF 解析庫都是 Python 2.x 版本建立的,還沒有遷移到 Python 3.x 版本。但是,因為 PDF 比較簡單,而且開源的文件格式,所以一些給力的 Python 可以讀取 PDF 檔案,而且支援 Python 3.x 版本。

PDFMiner3K 就是一個非常好用的庫(是 PDFMiner 的 Python 3.x 移植版)。他非常靈活,可以通過命令列使用,也可以整合到程式碼中。還可以處理不同的語言編碼,而且對網路檔案的處理也非常的方便。

你可以下載這個模組的原始檔(https://pypi.org/project/pdfminer3k/),解壓並用下面命令安裝:

python setup.py install

我們也可以使用 pip 的方式安裝:

pip install pdfminer3k

下面的例子可以把任意 PDF 讀成字串,然後使用 StringIO 轉換成檔案物件:

import requests
from io import StringIO
import csv
from pdfminer.pdfinterp import PDFResourceManager, process_pdf
from pdfminer.layout import LAParams
from pdfminer.converter import TextConverter
from urllib.request import urlopen


class ProcessCSVPDFDOCX(object):
    def __init__(self):
        self._session = requests.Session()
        self._pdf_path = 'https://image.pdflibr.com/crawler/blog/markdown-cheatsheet-online.pdf'


    def read_pdf(self, pdf_file):
        rscmgr = PDFResourceManager()
        retstr = StringIO()
        laparames = LAParams()
        device = TextConverter(rscmgr, retstr, laparams=laparames)
        process_pdf(rscmgr, device, pdf_file)
        device.close()

        content = retstr.getvalue()
        retstr.close()
        return content

    def read_pdf_main(self):
        pdf_file = urlopen(self._pdf_path)
        output_string = self.read_pdf(pdf_file)
        print(output_string)
        pdf_file.close()


if __name__ == '__main__':
    ProcessCSVPDFDOCX().read_pdf_main()

readPDF 最大的好處是,如果你的 PDF 檔案在電腦裡,你就可以直接把 urlopen 返回的物件 pdf_file 換成普通的 open() 檔案物件。

輸入的結果可能不是很完美,尤其是當檔案中包含圖片、各種各樣的文字格式,或者帶有表格和資料圖的時候。但是,對於大多數只包含純文字內容的 PDF 而言,其輸出結果與純文字並沒有什麼區別。

微軟 Word 和 .docx

網上有很多對 Word 吐槽的網友,Word 的特意功能就是把那些因該寫成簡單 TXT 或 PDF 格式的檔案,變成了即大又慢且難以開啟的怪獸,它們經常在系統切換和版本切換中出現格式不相容,而且應為某些原因在檔案內容已經定稿後仍處於可編輯狀態。Word 文件從未打算讓人頻繁傳遞。不過他們在一些網站上很流行,包括重要的文件、資訊,甚至圖表和多媒體;總之,那些內容都應該使用 HTML 代替。

大約在 2008 年以前,微軟 Office 產品中 Word 用 .doc 檔案格式。這種二進位制格式很難讀取,而且能夠讀取 word 格式的軟體很少。為了跟上時代,讓自己的軟體符合主流軟體的標準,微軟決定使用 Open Office 的類 XML 格式標準,此後新版本 Word 才能與其他文書處理軟體相容,這個格式就是 .docx

不過,Python 對這種 Google Docs、Open Office 和 Microsoft Office 都在使用的 .docx 格式支援還不夠好。雖然有一個 python-docx 庫,但是隻支援建立和讀取一些基本的資料,入檔案大小和檔案標題,不支援正文讀取。如果想讀取 Microsoft Office 檔案的正文內容,我們需要自己動手找方法。

第一步是從檔案讀取 XML:

import requests
from io import StringIO
import csv
from pdfminer.pdfinterp import PDFResourceManager, process_pdf
from pdfminer.layout import LAParams
from pdfminer.converter import TextConverter
from urllib.request import urlopen
from io import open, BytesIO
from zipfile import ZipFile


class ProcessCSVPDFDOCX(object):
    def __init__(self):
        self._csv_path = 'https://image.pdflibr.com/crawler/blog/country.CSV'
        self._session = requests.Session()
        self._pdf_path = 'https://image.pdflibr.com/crawler/blog/markdown-cheatsheet-online.pdf'
        self._docx_path = 'https://image.pdflibr.com/crawler/blog/test_document.docx'


    def convert_docx_to_xml(self):
        word_file = urlopen(self._docx_path).read()
        word_file = BytesIO(word_file)
        document = ZipFile(word_file)
        xml_content = document.read('word/document.xml')
        print(xml_content.decode('utf-8'))


if __name__ == '__main__':
    ProcessCSVPDFDOCX().convert_docx_to_xml()

這段程式碼把遠端 Word 讀取成一個二進位制檔案物件(BytesIO 與上面使用的 StringIO 類似),再使用 Python 的標準庫 zipfile 解壓(所有的 .docx 檔案為了節省空間都進行了壓縮),然後對讀取這個解壓檔案,就變成了 XML 了。

解壓後的 XML 檔案包含了大量資訊,好在所有的內容都包含在<w:t>標籤裡面,標題內容也是如此,這樣就容易處理多了。

import requests
from io import StringIO
import csv
from pdfminer.pdfinterp import PDFResourceManager, process_pdf
from pdfminer.layout import LAParams
from pdfminer.converter import TextConverter
from urllib.request import urlopen
from io import open, BytesIO
from zipfile import ZipFile
from bs4 import BeautifulSoup


class ProcessCSVPDFDOCX(object):
    def __init__(self):
        self._csv_path = 'https://image.pdflibr.com/crawler/blog/country.CSV'
        self._session = requests.Session()
        self._pdf_path = 'https://image.pdflibr.com/crawler/blog/markdown-cheatsheet-online.pdf'
        self._docx_path = 'https://image.pdflibr.com/crawler/blog/test_document.docx'


    def convert_docx_to_xml(self):
        word_file = urlopen(self._docx_path).read()
        word_file = BytesIO(word_file)
        document = ZipFile(word_file)
        xml_content = document.read('word/document.xml')
        print(xml_content.decode('utf-8'))
        word_obj = BeautifulSoup(xml_content.decode('utf-8'), features="html.parser")
        text_string = word_obj.findAll("w:t")
        for text_ele in text_string:
            print(text_ele.text)


if __name__ == '__main__':
    ProcessCSVPDFDOCX().convert_docx_to_xml()

這段程式碼顯示的結果可能並不完美,但是已經差不多了,一行列印一個<w:t>標籤。

總結

這篇文章主要講解了使用 Python 如何處理線上 CSV、PDF、Word 文件,由於 docx 文件並沒有很好的庫,如何曲線解析 docx 檔案,通過這篇文章可以處理網際網路上大部分文件內容。

這篇文章的所有原始碼已經託管於 Github:https://github.com/sycct/Scrape_1_1.git

如果有任何問題,歡迎大家 issue。