爬虫系列:读取 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。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,640评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,254评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,011评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,755评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,774评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,610评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,352评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,257评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,717评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,894评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,021评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,735评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,354评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,936评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,054评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,224评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,974评论 2 355

推荐阅读更多精彩内容