17. PY4E - 面向所有人的 Python¶
切换导航
第 1 章:简介 第 2 章:变量 第 3 章:条件语句 第 4 章:函数 第 5 章:迭代 第 6 章:字符串 第 7 章:文件 第 8 章:列表 第 9 章:字典 第 10 章:元组 第 11 章:正则表达式 第 12 章:网络程序 第 13 章:Python 与 Web 服务 第 14 章:Python 对象 第 15 章:Python 与数据库 第 16 章:数据可视化
17.1. 可视化数据¶
到目前为止,我们一直在学习 Python 语言,然后学习如何使用 Python、网络和数据库来操作数据。
在本章中,我们将探讨三个完整的应用程序,它们将这些内容整合在一起,以管理和可视化数据。您可以将这些应用程序作为示例代码,帮助您开始解决现实世界中的问题。
每个应用程序都是一个 ZIP 文件,您可以下载并将其解压到您的计算机上执行。
17.1.1. 从地理编码数据构建树¶
在本项目中,我们使用 OpenStreetMap 地理编码 API 来清理一些用户输入的大学名称地理位置,然后将数据放置到实际的 OpenStreetMap 上。

树
为开始,请从以下地址下载应用程序:
www.py4e.com/code3/opengeo.zip
需要解决的首要问题是这些地理编码 API 对每日请求数量有限制。如果数据量很大,您可能需要多次停止并重新启动查找过程。因此,我们将问题分为两个阶段。
在第一阶段,我们从文件 where.data 中读取输入“调查”数据,逐行读取,并从 Google 获取地理编码信息,将其存储在数据库 geodata.sqlite 中。在使用地理编码 API 处理每个用户输入的位置之前,我们只需检查是否已经拥有该特定输入行的数据。该数据库作为我们地理编码数据的本地“缓存”,确保我们永远不会向 Google 重复请求相同的数据。
您可以在任何时间通过删除文件 geodata.sqlite 来重新启动该过程。
运行 geoload.py 程序。该程序将读取 where.data 中的输入行,并对每一行检查其是否已存在于数据库中。如果缺少该位置的数据,它将调用地理编码 API 获取数据并将其存储在数据库中。
这是数据库中已有一些数据后的示例运行:
Found in database AGH University of Science and Technology
Found in database Academy of Fine Arts Warsaw Poland
Found in database American University in Cairo
Found in database Arizona State University
Found in database Athens Information Technology
Retrieving https://py4e-data.dr-chuck.net/
opengeo?q=BITS+Pilani
Retrieved 794 characters {"type":"FeatureColl
Retrieving https://py4e-data.dr-chuck.net/
opengeo?q=Babcock+University
Retrieved 760 characters {"type":"FeatureColl
Retrieving https://py4e-data.dr-chuck.net/
opengeo?q=Banaras+Hindu+University
Retrieved 866 characters {"type":"FeatureColl
...
前五个位置已在数据库中,因此被跳过。程序扫描直到找到新位置并开始检索它们。
geoload.py 程序可以在任何时候停止,并且有一个计数器,您可以用它来限制每次运行中调用地理编码 API 的次数。鉴于 where.data 只有几百个数据项,您不应该遇到每日速率限制,但如果您有更多的数据,可能需要经过几天的多次运行才能使您的数据库包含所有输入数据的地理编码数据。
一旦将一些数据加载到 geodata.sqlite 中,你就可以使用 geodump.py 程序来可视化数据。该程序读取数据库,并以可执行的 JavaScript 代码形式写入文件 where.js ,其中包含位置、纬度和经度信息。
geodump.py 程序的运行结果如下:
AGH University of Science and Technology, Czarnowiejska,
Czarna Wieś, Krowodrza, Kraków, Lesser Poland
Voivodeship, 31-126, Poland 50.0657 19.91895
Academy of Fine Arts, Krakowskie Przedmieście,
Northern Śródmieście, Śródmieście, Warsaw, Masovian
Voivodeship, 00-046, Poland 52.239 21.0155
...
260 lines were written to where.js
Open the where.html file in a web browser to view the data.
文件 where.html 包含用于可视化 Google 地图的 HTML 和 JavaScript。它读取 where.js 中的最新数据以获取待可视化的数据。以下是 where.js 文件的格式:
myData = [
[50.0657,19.91895,
'AGH University of Science and Technology, Czarnowiejska,
Czarna Wieś, Krowodrza, Kraków, Lesser Poland
Voivodeship, 31-126, Poland '],
[52.239,21.0155,
'Academy of Fine Arts, Krakowskie Przedmieściee,
Śródmieście Północne, Śródmieście, Warsaw,
Masovian Voivodeship, 00-046, Poland'],
...
];
这是一个包含列表的列表的 JavaScript 变量。JavaScript 列表常量的语法与 Python 非常相似,因此你应该很熟悉该语法。
只需在浏览器中打开 where.html 即可查看位置。您可以将鼠标悬停在每个地图图钉上,以查找地理编码 API 为用户输入返回的位置。如果您在打开 where.html 文件时看不到任何数据,您可能需要检查浏览器的 JavaScript 或开发者控制台。
17.1.2. 可视化网络与互联¶
在本应用中,我们将执行搜索引擎的一些功能。首先,我们将爬取一小部分网页,并运行简化版的 Google 页面排名算法,以确定哪些页面连接度最高,然后可视化我们网络角落的页面排名和连接度。我们将使用 D3 JavaScript 可视化库 http://d3js.org/ 来生成可视化输出。
您可以从以下位置下载并解压此应用程序:
www.py4e.com/code3/pagerank.zip

页面排名
第一个程序( spider.py )程序抓取一个网站,并将一系列页面拉取到数据库( spider.sqlite )中,记录页面之间的链接。您可以通过删除 spider.sqlite 文件并重新运行 spider.py 来随时重新开始该过程。
Enter web url or enter: http://www.dr-chuck.com/
['http://www.dr-chuck.com']
How many pages:2
1 http://www.dr-chuck.com/ 12
2 http://www.dr-chuck.com/csev-blog/ 57
How many pages:
在本示例运行中,我们指示它爬取一个网站并检索两个页面。如果您重新启动程序并指示它爬取更多页面,它将不会重新爬取数据库中已存在的任何页面。重启后,它会跳转到一个随机的未爬取页面并从那里开始。因此,每次对 spider.py 的连续运行都是累加的。
Enter web url or enter: http://www.dr-chuck.com/
['http://www.dr-chuck.com']
How many pages:3
3 http://www.dr-chuck.com/csev-blog 57
4 http://www.dr-chuck.com/dr-chuck/resume/speaking.htm 1
5 http://www.dr-chuck.com/dr-chuck/resume/index.htm 13
How many pages:
您可以在同一数据库中有多个起点——在程序中,这些被称为“网络”。蜘蛛在所有网络中所有未访问的链接中随机选择下一个要爬取的页面。
如果你想转储 spider.sqlite 文件的内容,可以按如下方式运行 spdump.py :
(5, None, 1.0, 3, 'http://www.dr-chuck.com/csev-blog')
(3, None, 1.0, 4, 'http://www.dr-chuck.com/dr-chuck/resume/speaking.htm')
(1, None, 1.0, 2, 'http://www.dr-chuck.com/csev-blog/')
(1, None, 1.0, 5, 'http://www.dr-chuck.com/dr-chuck/resume/index.htm')
4 rows.
这显示了入站链接的数量、旧的页面排名、新的页面排名、页面的 id 以及页面的 url。 spdump.py 程序仅显示至少有一个入站链接指向它们的页面。
一旦数据库中有了一些页面,你就可以使用 sprank.py 程序对这些页面运行 Page Rank。你只需告诉它要运行多少次 Page Rank 迭代。
How many iterations:2
1 0.546848992536
2 0.226714939664
[(1, 0.559), (2, 0.659), (3, 0.985), (4, 2.135), (5, 0.659)]
您可以再次转储数据库,以查看 Page Rank 已更新:
(5, 1.0, 0.985, 3, 'http://www.dr-chuck.com/csev-blog')
(3, 1.0, 2.135, 4, 'http://www.dr-chuck.com/dr-chuck/resume/speaking.htm')
(1, 1.0, 0.659, 2, 'http://www.dr-chuck.com/csev-blog/')
(1, 1.0, 0.659, 5, 'http://www.dr-chuck.com/dr-chuck/resume/index.htm')
4 rows.
你可以多次运行 sprank.py ,它会在每次运行时简单地优化页面排名。你甚至可以先运行几次 sprank.py ,然后用 spider.py 抓取更多页面,再运行 sprank.py 使页面排名值重新收敛。搜索引擎通常始终同时运行抓取和排名程序。
如果您想在不重新抓取网页的情况下重新启动页面排名计算,您可以使用 spreset.py ,然后重新启动 sprank.py 。
How many iterations:50
1 0.546848992536
2 0.226714939664
3 0.0659516187242
4 0.0244199333
5 0.0102096489546
6 0.00610244329379
...
42 0.000109076928206
43 9.91987599002e-05
44 9.02151706798e-05
45 8.20451504471e-05
46 7.46150183837e-05
47 6.7857770908e-05
48 6.17124694224e-05
49 5.61236959327e-05
50 5.10410499467e-05
[(512, 0.0296), (1, 12.79), (2, 28.93), (3, 6.808), (4, 13.46)]
对于 Page Rank 算法的每次迭代,它都会打印每页的 Page Rank 平均变化量。该网络最初非常不平衡,因此各个页面的 Page Rank 值在迭代之间变化剧烈。但在几次短暂的迭代后,Page Rank 就会收敛。你应该运行 sprank.py 足够长的时间,直到 Page Rank 值收敛。
如果你想可视化当前按页面排名排序的前几页,运行 spjson.py 以读取数据库,并将数据以 JSON 格式写入,以便在 Web 浏览器中查看那些链接最多的页面。
Creating JSON output on spider.json...
How many nodes? 30
Open force.html in a browser to view the visualization
您可以通过在 Web 浏览器中打开文件 force.html 来查看此数据。这显示了节点和链接的自动布局。您可以点击并拖动任何节点,也可以双击节点以找到该节点所代表的 URL。
如果您重新运行其他实用程序,请重新运行 spjson.py 并在浏览器中按刷新以从 spider.json 获取新数据。
17.1.3. 可视化邮件数据¶
直到本书的这一部分,你已经非常熟悉我们的 mbox-short.txt 和 mbox.txt 数据文件。现在是时候将我们的电子邮件数据分析提升到下一个层次了。
在现实世界中,有时你必须从服务器拉取邮件数据。这可能需要相当长的时间,数据可能不一致、充满错误,并且需要大量清理或调整。在本节中,我们处理迄今为止最复杂的应用程序,拉取近 1 GB 的数据并将其可视化。

来自 Sakai 开发者列表的单词云
您可以从以下位置下载此应用程序:
https://www.py4e.com/code3/gmane.zip
我们将使用来自名为 http://www.gmane.org 的免费电子邮件列表归档服务的数据。该服务因其提供了开源项目电子邮件活动的良好可搜索归档而在开源项目中非常受欢迎。他们关于通过其 API 访问数据的规定也非常宽松。他们没有速率限制,但要求您不要过度占用其服务,并且只获取您需要的数据。您可以在以下页面阅读 gmane 的条款和条件:
http://www.gmane.org/export.php
务必负责任地使用 gmane.org 数据,通过为您的服务访问添加延迟并将长时间运行的作业分散到更长的时间段内。不要滥用这项免费服务,从而破坏我们其他人的使用体验。
当使用该软件抓取 Sakai 电子邮件数据时,它生成了近 1 GB 的数据,并在数天内运行了多次。上述 ZIP 文件中的 README.txt 文件可能包含有关如何下载 content.sqlite 文件预抓取副本的说明,该副本涵盖了大部分 Sakai 电子邮件语料库,这样您就不必花费五天时间仅为了运行程序而进行抓取。如果您下载了预抓取的內容,您仍应运行抓取过程以捕获更新的消息。
第一步是抓取 gmane 存储库。基础 URL 在 gmane.py 中硬编码,并固定为 Sakai 开发者列表。若要抓取另一个存储库,可更改该基础 URL。如果切换基础 URL,请确保删除 content.sqlite 文件。
gmane.py 文件作为一个负责任的缓存爬虫运行,因为它运行缓慢,每秒只检索一封邮件,以避免被 gmane 限流。它将所有数据存储在一个数据库中,并且可以根据需要多次中断和重新启动。下载所有数据可能需要数小时。因此,您可能需要多次重启。
下面是 gmane.py 运行获取 Sakai 开发者列表最后五条消息的示例:
How many messages:10
http://download.gmane.org/gmane.comp.cms.sakai.devel/51410/51411 9460
nealcaidin@sakaifoundation.org 2013-04-05 re: [building ...
http://download.gmane.org/gmane.comp.cms.sakai.devel/51411/51412 3379
samuelgutierrezjimenez@gmail.com 2013-04-06 re: [building ...
http://download.gmane.org/gmane.comp.cms.sakai.devel/51412/51413 9903
da1@vt.edu 2013-04-05 [building sakai] melete 2.9 oracle ...
http://download.gmane.org/gmane.comp.cms.sakai.devel/51413/51414 349265
m.shedid@elraed-it.com 2013-04-07 [building sakai] ...
http://download.gmane.org/gmane.comp.cms.sakai.devel/51414/51415 3481
samuelgutierrezjimenez@gmail.com 2013-04-07 re: ...
http://download.gmane.org/gmane.comp.cms.sakai.devel/51415/51416 0
Does not start with From
该程序从 content.sqlite 扫描,直到第一个尚未爬取的消息编号,并从该消息开始爬取。它持续爬取,直到爬取到指定数量的消息,或者到达一个看起来不是格式正确消息的页面。
有时 gmane.org 会缺少一条消息。也许是管理员删除了消息,也许是它们丢失了。如果您的爬虫停止,并且看起来它遇到了一条缺失的消息,请进入 SQLite Manager 并添加一行,输入缺失的 id,其他字段留空,然后重启 gmane.py 。这将使爬虫过程不再卡住并允许其继续。这些空消息将在过程的下一阶段被忽略。
一个好处是,一旦你爬取了所有消息并将它们保存到 content.sqlite 中,你就可以再次运行 gmane.py 来获取发送到列表的新消息。
content.sqlite 数据非常原始,数据模型效率低下,且未进行压缩。这是有意为之,因为它允许你在 SQLite Manager 中查看 content.sqlite 以调试爬虫过程中的问题。针对该数据库运行任何查询都是一个糟糕的主意,因为它们的速度会非常慢。
第二个过程是运行程序 gmodel.py 。该程序从 content.sqlite 读取原始数据,并在文件 index.sqlite 中生成经过清理和良好建模的数据版本。该文件将比 content.sqlite 小得多(通常小 10 倍),因为它还压缩了标题和正文文本。
每次运行 gmodel.py 时,它都会删除并重建 index.sqlite ,允许您调整其参数并在 content.sqlite 中编辑映射表以微调数据清理过程。这是 gmodel.py 的示例运行。它每处理 250 封邮件消息就输出一行,以便您能看到一些进度正在发生,因为该程序可能需要运行一段时间来处理近 1 GB 的邮件数据。
Loaded allsenders 1588 and mapping 28 dns mapping 1
1 2005-12-08T23:34:30-06:00 ggolden22@mac.com
251 2005-12-22T10:03:20-08:00 tpamsler@ucdavis.edu
501 2006-01-12T11:17:34-05:00 lance@indiana.edu
751 2006-01-24T11:13:28-08:00 vrajgopalan@ucmerced.edu
...
gmodel.py 程序处理多种数据清理任务。
域名被截断为两级,适用于 .com、.org、.edu 和 .net。其他域名被截断为三级。因此 si.umich.edu 变为 umich.edu,而 caret.cam.ac.uk 变为 cam.ac.uk。电子邮件地址也被强制转换为小写,并且一些 @gmane.org 地址如下所示
arwhyte-63aXycvo3TyHXe+LvDLADg@public.gmane.org
每当在消息语料库的其他位置找到匹配的实体邮箱地址时,它们就会被转换为实体地址。
在 mapping.sqlite 数据库中有两张表,允许你映射随电子邮件列表生命周期变化的域名和单个电子邮件地址。例如,Steve Githens 在 Sakai 开发者列表的生命周期中随着工作变动使用了以下电子邮件地址:
s-githens@northwestern.edu
sgithens@cam.ac.uk
swgithen@mtu.edu
我们可以在 mapping.sqlite 映射表中添加两条条目,这样 gmodel.py 就能将这三个映射到一个地址:
s-githens@northwestern.edu -> swgithen@mtu.edu
sgithens@cam.ac.uk -> swgithen@mtu.edu
如果您希望将多个 DNS 名称映射到单个 DNS,也可以在 DNSMapping 表中添加类似的条目。以下映射已添加到 Sakai 数据中:
iupui.edu -> indiana.edu
因此,来自印第安纳大学各个校区的账户都被统一追踪。
你可以在查看数据时反复运行 gmodel.py ,并添加映射以使数据更加整洁。完成后,你将拥有一个在 index.sqlite 中良好索引的电子邮件版本。这是用于进行数据分析的文件。使用该文件,数据分析将非常迅速。
第一个,最简单的数据分析是确定“谁发送的邮件最多?”和“哪个组织发送的邮件最多?”这是使用 gbasic.py 完成的:
How many to dump? 5
Loaded messages= 51330 subjects= 25033 senders= 1584
Top 5 Email list participants
steve.swinsburg@gmail.com 2657
azeckoski@unicon.net 1742
ieb@tfd.co.uk 1591
csev@umich.edu 1304
david.horwitz@uct.ac.za 1184
Top 5 Email list organizations
gmail.com 7339
umich.edu 6243
uct.ac.za 2451
indiana.edu 2258
unicon.net 2055
注意 gbasic.py 的运行速度比 gmane.py 甚至 gmodel.py 快得多。它们都在处理相同的数据,但 gbasic.py 使用的是 index.sqlite 中的压缩和规范化数据。如果您需要管理大量数据,像本应用程序中的这种多步骤过程可能需要更长的开发时间,但当您真正开始探索和分析数据时,它将为您节省大量时间。
您可以在文件 gword.py 中生成主题行中单词频率的简单可视化:
Range of counts: 33229 129
Output written to gword.js
这生成了文件 gword.js ,您可以使用 gword.htm 可视化它,以生成一个类似于本节开头所示的词云。
第二个可视化由 gline.py 生成。它计算组织随时间推移的电子邮件参与度。
Loaded messages= 51330 subjects= 25033 senders= 1584
Top 10 Oranizations
['gmail.com', 'umich.edu', 'uct.ac.za', 'indiana.edu',
'unicon.net', 'tfd.co.uk', 'berkeley.edu', 'longsight.com',
'stanford.edu', 'ox.ac.uk']
Output written to gline.js
其输出被写入 gline.js ,并使用 gline.htm 进行可视化。

按组织划分的 Sakai 邮件活动
这是一个相对复杂且功能完善的应用程序,具备进行真实数据检索、清洗和可视化的功能。
如果您在本书中发现错误,欢迎使用 Github 向我发送修正。
