《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 的接收端。现在,服务器端也会用一些 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 代码——读取文档的规则。文档编码的方式通常可以根据文件的扩展名进行判断,虽然文件扩展名并不是由编码确定的,而是由开发者确定的。
从最底层的角度看,所有文档都是由 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 文件的正文内容,我们需要自己动手找方法。

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

推荐阅读更多精彩内容