在本章中,我们将介绍以下食谱:
- 创建一个 Twitter 应用
- 了解 Twitter API v1.1
- 确定您的 Twitter 关注者和朋友
- 拉 Twitter 的用户资料
- 在不违反 Twitter 速率限制的情况下发出请求
- 将 JSON 数据存储到磁盘
- 设置 MongoDB 以存储 Twitter 数据
- 使用 PyMongo 在 MongoDB 中存储用户配置文件
- 探索配置文件中可用的地理信息
- 在 Python 中绘制地理空间数据
在本章中,我们将通过使用 RESTful Web 服务 API 进入社交媒体分析领域。 Twitter 是一个微博客社交网络,其流对于数据挖掘(尤其是文本挖掘)而言非常宝贵,并且它们具有出色的 API,我们将学习如何通过 Python 与之交互。 我们将使用该 API 来获取 Twitter 的社交联系,并使用传统的文件存储和流行的 NoSQL 数据库 MongoDB 来收集和存储 JSON 数据。 我们的分析将尝试确定连接的地理位置,并根据数据进行可视化处理。
在本章中,您应该开始注意到有关 API 的设计方式及其预期用途的模式。 与 API 的交互是一个非常重要的数据科学主题,对 API 的深入了解将为您打开一个全新的数据世界,您可以在该世界上进行大量的分析。
API 代表应用编程接口,在传统的计算机科学中,它是指允许软件应用彼此交互的方法。 如今,大多数对 API 的引用都指 Web API,它是互联网在您的软件应用和 Web 应用(例如 Twitter)之间共享数据的使用。 数据采集和管理是数据科学流程的重要组成部分,并且知道如何使用 API对于从互联网上获取可操作的数据集至关重要。
API 的一个特殊子集称为 RESTful API,实际上是大多数 Web 应用的基础,并且无处不在。 尽管我们可以避免大多数技术术语,但我们应该指出 REST 代表表示状态转移,这是一种以文档或对象作为表示形式存在的奇特方式,并且 对状态的修改应通过 API 进行。 RESTful API 是建立在万维网上的超文本传输协议(HTTP)的直接扩展,这就是为什么它们作为 Web API 如此流行的原因。 HTTP 允许客户端通过发出动词形式的请求来连接服务器:GET
,POST
,DELETE
和PUT
。 传统上,响应是一个 HTML 文档。 同样,RESTful API 使用这些动词来发出响应为 JSON 文档的请求。 前者用于人类消费(例如,当您访问这个页面之类的网站时),而后者则用于应用消费。
在本章中,我们将仅使用 HTTP GET
请求和偶发的POST
请求。 GET
请求就像听起来一样。 它要求服务器为您提供特定资源。 另一方面,POST
请求意味着客户端正在尝试向服务器提供数据(例如,提交表单或上载文件)。 API 提供程序(例如 Twitter)允许我们向特定资源 URL 发出 HTTP GET
请求,该资源 URL 通常称为端点。 例如,特定用户的所有最新推文的GET
端点是https://api.twitter.com/1.1/statuses/user_timeline.json
。 如果我们对此端点发出经过正确身份验证的 HTTP GET
请求,则 Twitter 将以 JSON 格式提供构成当前用户时间轴的数据。
Twitter 是无处不在的微博社交媒体平台,截至 2014 年拥有 2.53 亿活跃会员。对于我们来说,Twitter 使该服务的数据比其他任何具有类似规模和地位的社交媒体网站更具开放性,并且可供第三方使用 。 此外,Twitter 提供了丰富且用户友好的 RESTful API,我们将广泛使用它。 此食谱将向您展示如何创建新的 Twitter 应用,这是以编程方式访问 Twitter 数据的必需步骤。
确保已安装 Web 浏览器,然后打开一个新的浏览器选项卡或窗口。
以下步骤将引导您完成新的 Twitter 应用的创建:
请注意,Twitter 确实希望经常更新其用户界面(UI),并且这些步骤或基于 Web 的形式可能会相应地发生变化。
-
首先,请确保您已经创建了一个 Twitter 帐户。 如果尚未创建一个,请访问这个页面并注册。 如果您有一个帐户,只需使用 Web 浏览器登录到您的 Twitter 帐户即可。
-
接下来,转到这个页面,然后在屏幕右侧选择标记为
Create New App
的浅蓝色按钮:
-
在这里,它将提示您在三个必填字段和一个可选字段中输入您的应用详细信息。 为您的应用选择一个不超过 32 个字符的名称。
-
接下来,提供您的应用的简短描述,长度在 10 到 200 个字符之间。
-
您必须为您的应用提供一个网站,尽管该网站不适用于我们的用例。 此外,要成功提交表单,还需要一种特定的格式。 输入
http://127.0.0.1
。 -
最后,您可以忽略
Callback URL
字段,这是表单上的最后一个字段。 -
继续并花一些时间阅读
Developer Rules of the Road
部分,因为本文档以纯文本和简单文本详细介绍了您应该使用和不应该使用应用进行的操作。 -
点击
Create your Twitter Application
。 片刻之后,您应该在新应用的主设置页面上,并且在屏幕顶部有一个选项卡式菜单。 当前选项卡应标记为Details
。 -
单击
Keys and Access Tokens
选项卡,您应该看到以下屏幕截图:
- 现在,在底部的
Token
操作灰色框中单击Create my access token
,以授权您自己的帐户的应用(您可能需要多次单击按钮) 。 结果应类似于以下屏幕截图:
- 在文本文件中记录
API key
,API secret
,Access token
和Access token secret
。 这些很重要,但是必须像保护电子邮件密码或 ATM PIN 一样对它们进行保护。 您可以截图以保存信息,但是现在将这些值复制并粘贴到文本文件中会更加容易。
现在您已经拥有 MinGW 和 MSYS,现在不再需要嫉妒那些安装 Linux 的用户,因为它们在您的系统中实现了 Linux 开发环境中最重要的部分。
您可能会问自己,如果我们要做的就是从 Twitter 提取一些简单数据,为什么我们需要创建一个应用。 Twitter API 的早期版本(1.0 及更低版本)允许应用发出匿名 API 请求,从而从 Twitter 检索数据,而 Twitter 却不知道谁在实际发出请求。 由于不推荐使用的公司 API 版本 1.0 已于 2013 年 6 月 11 日停用,因此所有对 Twitter 的 API 请求都需要进行身份验证。 这使 Twitter 可以跟踪谁要求什么信息以及要求多少信息。
通常,行业中有几种迹象表明,社交媒体数据收集的平静日子可能正在减弱。 Facebook 最新的 API 集,特别是 Facebook Login 2.0 版,已经强烈锁定了可以从社交图中获取哪些数据。 此外,Twitter 在 2014 年 4 月收购了 Gnip; Gnip 是 Twitter 数据的转售商,它允许其客户购买大部分 Twitter 数据。 此举表明,Twitter API 的 2.0 版可能会限制对 Twitter 诗歌的进一步访问。
可以参考以下文章:
API 既是福也是祸。 应用编程接口使从 Twitter,Facebook 或 LinkedIn 等服务收集数据变得更加容易,并且可以准确地定义公司想要和不想要的数据。 不幸的是,公司在访问其 API 时设置了速率限制,以控制可以收集的数据的频率(因此,数量)。 还众所周知,它们会从一个版本到另一个版本从根本上改变他们的 API,从而导致对依赖原始 API 的所有工作进行了大量的代码重写。 Twitter 的大型 API 从 1.0 版更改为 1.1 版,提供了一个警告。
Twitter 提供了三个主要 API:搜索 API,REST API 和 Streaming API。 搜索 API 为我们提供了一种编程方法,该方法可对 Twitter 进行查询,以检索历史内容,即推文。 REST API 提供对 Twitter 核心功能的访问,包括时间表,状态更新和用户信息。 最后,流 API 是实时 API,旨在低延迟访问 Twitter 的全球 Tweet 数据流。
使用流式 API,必须保持对 Twitter 开放的持久 HTTP 连接。 为了我们的数据挖掘和分析目的,这是过大的,因为我们只会定期从 Twitter 请求数据。 因此,我们将专注于前两个 API,而不必担心流 API。
在上述配方中创建了应用并复制了密钥之后,就可以继续进行了。
执行以下步骤,以便使用 Python 以编程方式访问 Twitter API:
- 首先,安装
twython
库。 打开一个新的命令提示符,然后键入以下内容:
(sudo) pip install twython
如果您当前的用户帐户没有足够的特权,则需要sudo
命令。
- 接下来,打开一个新的终端并启动默认的 Python REPL 或 IPython。 如果您想加倍努力,也可以使用 IPython Notebook。
- 输入并执行以下 Python 代码,并填写所需的应用密钥:
In [1]: '''
...: Understanding the Twitter API v1.1
...: '''
...: from twython import Twython
In [2]: API_KEY = 'INSERT HERE'
...: API_SECRET = 'INSERT HERE'
...:
...: ACCESS_TOKEN = 'INSERT HERE'
...: ACCESS_TOKEN_SECRET = 'INSERT HERE'
In [3]: twitter = Twython(API_KEY, API_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
请注意,Twitter 经常更新其开发人员界面,并且API_KEY
以前称为CONSUMER_KEY
。 另外,请注意,代码片段中给定的键需要替换为先前配方中收集的值。
- 如果使用的是 IPython,请在 REPL 上键入以下内容,然后单击选项卡键:
In [4]:twitter
这将带来令人印象深刻的 API 调用列表,这些列表现在可供您使用。
- 作为测试,我们可以在 Python 提示符下输入以下内容:
In [4]: temp = twitter.get_user_timeline()
此命令将从您的时间轴中获取最近的 20 个状态更新,作为包含 20 个元素的 Python 字典列表。
- 此外,Twython 使我们可以访问从 Twitter 收到的响应标头:
In [5]: twitter.get_lastfunction_header('x-rate-limit-remaining')
...:
Out[5]: '899'
使用前面的代码,我们将设置传递的 API 密钥和访问令牌,以实例化Twython
类的实例。 这个新对象用作我们 Twitter API 的主要接口,默认情况下使用OAuth v1
进行身份验证。 由于这是一个非常普遍的要求,因此我们可以将此功能包装在其自己的功能中,如以下代码片段所示。 在使用以下功能代码之前,请确保输入所需的应用密钥:
In [6]: def twitter_oauth_login():
...: API_KEY = 'INSERT HERE'
...: API_SECRET = 'INSERT HERE'
...: ACCESS_TOKEN = 'INSERT HERE'
...: ACCESS_TOKEN_SECRET = 'INSERT HERE'
...:
...: twitter = Twython(API_KEY, API_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
...: return(twitter)
return(twitter)
如果您要将代码签入 GitHub 或其他基于云的版本控制解决方案(例如 Bitbucket),请检查该存储库是公共的还是私有的。 GitHub 上的所有免费存储库都是公开的。 如果您的存储库是公共的,那么全世界将可以访问您的秘密 Twitter API 密钥。 我们强烈建议您仅将私有存储库用于此类事项,并请注意 bitbucket.org 免费提供私有存储库。
OAuth 代表开放式身份验证协议,它允许用户向第三方应用授予访问该用户帐户的某些方面的权限(在这种情况下,其 Twitter 帐户 ),而不会放弃用户的登录名和密码。 深入介绍 OAuth 的工作原理不在本食谱的讨论范围之内。 但是,讨论 Twitter 应用的两种不同类型的资源身份验证很重要。 最常见的身份验证是应用用户身份验证,我们将不使用它。 在这种模式下,您的应用代表已向您的应用授予所需权限的用户发出请求。 对于我们的项目,我们关心仅应用身份验证,其中我们的应用不是针对用户而是针对自身发出 API 请求。 请注意,某些 API 调用不支持仅应用的请求,并且此类请求的速率限制通常不同。
Twython
库不是唯一可用来简化对 Twitter API 的访问的 Python 库。 以下是三个受欢迎的选择,包括受欢迎的 Python Twitter Tools,您可以自由探索或选择自己认为合适的一个:
- Python Twitter 工具:这是 Twitter 的极简 Python API,其中包括一个命令行工具,以便结识朋友。 鸣叫并发送您自己的公告。
- Twython 3.1.2:这是一个纯 Python 包装器,它同时支持搜索和流式 API,这些 API 都得到了积极维护。 这是我们将要使用的库。
- python-twitter:这是当前 v1.1 Twitter API 的纯 Python 接口。
Twitter 在这个页面上维护了跨编程语言的替代列表。 请注意,本章中的代码仅使用 Twython,但是对于读者来说,使用其他 Python 库重写示例可能是一个有用的练习。
您还可以参考:
在 Twitter 社交网络中,相对于特定用户,用户被标记为关注者或朋友。 您的朋友是您追随的人,而您的追随者是跟随您的人。 在此食谱中,我们确定谁是您的朋友,谁是追随者,以及每个组中有多少重叠。
对于此配方,我们将使用前两个配方的结果以及twitter_oauth_login()
函数。 另外,如果您愿意的话,我们将使用 IPython 或默认的 Python REPL。 随便使用编辑器即可开始捕获和修改代码,因为代码变得越来越复杂。
以下步骤将使您能够确定所有 Twitter 朋友和关注者:
- 在 IPython 或您喜欢的 REPL 中,输入以下内容:
In [8]: twitter = twitter_oauth_login()
...: friends_ids = twitter.get_friends_ids(count=5000)
...: friends_ids = friends_ids['ids']
In [9]: followers_ids = twitter.get_followers_ids(count=5000)
...: followers_ids = followers_ids['ids']
- 收集了您所有的关注者和朋友的 Twitter ID 后,让我们看看您有多少:
In [10]: len(friends_ids), len(followers_ids)
...:
Out[10]: (22, 40)
- 我们将使用基于您可能在数学课程中遇到过的 Python 集的 Python 集,来检查我们的朋友和关注者的某些属性:
In [11]: friends_set = set(friends_ids)
...: followers_set = set(followers_ids)
...:
In [12]: print('Number of Twitter users who either are our friend or follow you (union):')
...: print(len(friends_set.union(followers_set)))
...:
Number of Twitter users who either are our friend or follow you (union):
56
In [13]: len(friends_set | followers_set)
...: len(set(friends_ids+followers_ids))
...:
Out[13]: 56
In [14]: print('Number of Twitter users who follow you and are your friend (intersection):')
...: print(len(friends_set & followers_set))
...:
Number of Twitter users who follow you and are your friend (intersection):
6
In [15]: print("Number of Twitter users you follow that don't follow you (set difference):")
...: print(len(friends_set - followers_set))
...:
In [16]: Number of Twitter users you follow that don't follow you (set difference):
16
print("Number of Twitter users who follow you that you don't follow (set difference):")
...: print(len(followers_set - friends_set))
...:
Number of Twitter users who follow you that you don't follow (set difference):
34
上面的代码片段应产生以下输出:
上图中截屏中显示的数字很可能会根据您拥有的朋友和关注者的数量而有所不同。
此食谱展示了twython
软件包的实用性以及完成某些任务的容易程度。 使用twitter_oauth_login
功能登录后,我们使用twitter
对象进行了两个基本调用,一个用于获取朋友的 ID,一个用于获取关注者的 ID。 请注意,我们将 count 参数设置为 5000,这是 Twitter API 允许的最大值。 twitter
对象返回了一个字典,我们从中提取了实际的 ID。
Twython 界面的优点之一是它与 Twitter API 的镜像程度。 如果您对特定功能有疑问,只需查看 Twitter 文档。
收集好朋友和关注者 ID 的列表后,我们将使用 Python 集合类型进行快速的肚脐注视。 Python set
类型是从版本 2.4 开始内置到 Python 中的,它是唯一无序对象的集合。 对我们而言,关键词是唯一。 如果我们从具有重复项的列表中创建集合,则将获得仅包含唯一元素的集合; set([1, 2, 2, 3, 3, 3])
将返回{1, 2, 3}
。
我们将朋友的 ID 集与关注者的 ID 结合起来,以确定跟随或跟随我们的 Twitter 用户的唯一 ID 的总集合。 在前面的代码中,我们使用set
类型的 union 方法,但是还有几种其他方法可以实现此目的:
(friends_set | followers_set)
(set(friends_ids + followers_ids))
尽管 Twython 的精美抽象掩盖了使用 API的某些复杂性,但是如果我们不了解幕后实际发生的事情,那么这种简单性或魔术性可能会成问题。 当我们调用twitter.get_friends_ids(count=5000)
方法时,我们正在将 HTTP GET 请求发送到特定的 URL。 在twitter.get_friends_ids()
的情况下,URL 为https://api.twitter.com/1.1/friends/ids.json
。
函数调用的count=5000
输入参数在 URL 中显示为字段值对,因此 URL 变为https://api.twitter.com/1.1/friends/ids.json?count=5000
。
现在,实际的 API 端点需要 Twython 为我们填写的一些默认参数值,为清楚起见,如以下 URL 所示:
https://api.twitter.com/1.1/friends/ids.json?cursor=-
1&screen_name=sayhitosean&count=5000
Twitter v1.1 API 要求使用 OAuth 对所有请求进行身份验证。 所需的信息实际上嵌入在 GET 请求的标头中,并且构造适当的标头的过程非常广泛(有关更多信息,请转到这个页面)。 因此,Twython 不仅形成了发出请求的正确 URL,而且还处理了相对痛苦的“开放授权”,因此我们不必这样做。 如果您有兴趣,可以使用更高级的请求库或您选择的替代方法,下一层并构建自己的 GET 请求。 我们将其留给读者探索。
请注意,不同的 Twitter API 端点具有不同的速率限制。 对于 GET 朋友/ ID,截至 2014 年 5 月,我们仅允许在 15 分钟内对 API 1.1 版进行 15 次调用。其他端点对其数据的态度不太严格。
您还可以参考以下内容:
对于本食谱,我们将使用 Twitter API 提取有关 Twitter 用户的 JSON 数据。 由屏幕名称(例如SayHiToSean
)或唯一整数标识的每个 Twitter 用户,其配置文件均包含有关某人的丰富信息集。
您将需要上一个配方确定您的 Twitter 关注者和朋友的食谱中的关注者和朋友的 ID 的列表。
以下步骤将指导您检索一组 Twitter 用户的个人资料:
- 首先,我们创建一个函数来管理拉动 Twitter 资料:
In [18]: def pull_users_profiles(ids):
...: users = []
...: for i in range(0, len(ids), 100):
...: batch = ids[i:i + 100]
...: users += twitter.lookup_user(user_id=batch)
...: print(twitter.get_lastfunction_header('x-rate-limit-remaining'))
...: return (users)
- 我们使用此功能,提取朋友和关注者的个人资料:
In [19]: friends_profiles = pull_users_profiles(friends_ids)
...: followers_profiles = pull_users_profiles(followers_ids)
899
898
- 为了检查一切是否正常,我们使用列表推导从个人资料中提取所有朋友的屏幕名称:
In [20]: friends_screen_names = [p['screen_name'] for p in friends_profiles]
- 使用以下命令,您应该会看到朋友的屏幕名称列表:
In [21]: friends_screen_names
...:
Out[21]:
['nammamechanik',
'Ruchir78',
'nspothnis',
'jdelaney666',
'zakaas4u',
'arunkumar_n_t']
本食谱的第一步是创建一个管理twitter.lookup_user
方法调用的函数。 Twitter 用户/查找端点一次接受 100 个用户 ID。 因此,我们需要遍历朋友或关注者 ID 的列表,并将其分为 100 个组以用于请求。 Twitter 返回一个 JSON 对象,Twython 将该对象转换为可以使用的 Python 字典列表。
有另一种检索配置文件的方法。 我们可以直接使用当前用户的屏幕名称(在本例中为@SayHiToSean
)查询用户的朋友/列表端点,而不是拉出朋友和关注者的 ID,然后使用这些 ID 来请求用户个人资料。 。 然后,Twitter 将为每个请求返回多达 200 个用户个人资料。 如果您计算出 API 限制,则任一路径都将计算出与 Twitter 用于限速目的的 15 分钟默认时间窗口中提取的用户配置文件数目相同的数目。
我们创建的pull_users_profiles
函数在循环的最后一行有一个附加功能:
print(twitter.get_lastfunction_header('x-rate-limit-remaining'))
我们从上一次 API 调用中检索响应的标头,然后检查x-rate-limit-remaining
值。 该值准确地告诉我们在给定的 15 分钟窗口内,我们还剩下多少个 API 调用。 尽管我们在每个循环中都打印出了该值,但我们绝对不会采取任何措施阻止我们猛烈抨击 Twitter 的速率限制,该限制因端点而异。
此外,如果由于某种原因收到的一个 Twitter 用户配置文件没有screen_name
密钥,我们在步骤 3 中使用的列表理解可能会失败。 因此,最好在理解中增加一个条件:
In [22]: friends_screen_names = [p['screen_name'] for p in friends_profiles if 'screen_name' in p]
或者,作为一种替代方法,也可能是一种更 Python 化的方法,我们可以使用字典的 GET 方法:
In [23]: friends_screen_names = [p.get('screen_name',{}) for p in friends_profiles]
在这种情况下,不跳过没有screen_name
键的配置文件,而是将其替换为None
。
您还可以参考以下内容:
对于此配方,我们将修改在先前配方中创建的功能拉动 Twitter 用户配置文件,以避免达到可怕的 Twitter API 速率限制。
您将再次需要来自上一个配方的拉 Twitter 用户配置文件以及经过身份验证的 Twython 对象的关注者和朋友的 ID 列表。
以下功能演示了如何以感知速率限制的方式检索一组 Twitter 用户的配置文件:
In [25]: import time
...: import math
In [26]: rate_limit_window = 15 * 60 #900 seconds
In [27]: def pull_users_profiles_limit_aware(ids):
...: users = []
...: start_time = time.time()
...: # Must look up users in
...: for i in range(0, len(ids), 10):
...: batch = ids[i:i + 10]
...: users += twitter.lookup_user(user_id=batch)
...: calls_left = float(twitter.get_lastfunction_header('x-rate-limit-remaining'))
...: time_remaining_in_window = rate_limit_window - (time.time()-start_time)
...: sleep_duration = math.ceil(time_remaining_in_window/calls_left)
...: print('Sleeping for: ' + str(sleep_duration) + ' seconds; ' + str(calls_left) + ' API calls remaining')
...: time.sleep(sleep_duration)
...: return (users)
此功能是先前配方功能的修改版本,该功能提取用户的个人资料,因此我们不会违反 Twitter 无处不在的速率限制。 我们在循环的每个迭代中插入一个动态暂停,其长度由时间窗口中剩余的 API 调用次数决定。 在循环开始之前,我们在start_time
变量中捕获当前系统时间。 在twitter
对象进行每个 API 调用之后,我们获取响应的标头并检查 15 分钟时间窗口中剩余的 API 调用数。 我们计算从start_time
开始经过的时间,然后将其从 900 秒中减去,得出 15 分钟窗口中剩余的时间。 最后,我们计算剩余的每个 API 调用所需的秒数,并在所需的时间段内进行睡眠。 我们使用math.ceil
函数进行四舍五入,并确保始终提供一点额外时间,以免达到速率限制。
您可能会问,为什么有人会关心达到 Twitter API 速率限制。 为什么即使达到限制后也不要继续点击 API? 简单的答案是,Twitter 可以并且将阻止过于频繁使用规定速率限制的应用。 因此,遵守规则符合您的最大利益。 此外,一旦超过速率限制,您将无法获取任何其他信息,那么为什么要打扰呢?
就带宽和服务提供商对其 API 设置的速率限制而言,对 API 的调用可能会非常昂贵。 尽管 Twitter 对这些限制非常宽容,但其他服务却并非如此。 无论如何,优良作法是将检索到的 JSON 结构保存到磁盘以供以后使用。
对于此配方,您将需要以前检索的数据,最好是从以前的配方中检索的数据。
以下步骤指导我们将 JSON 数据保存到磁盘,然后将其重新加载到 Python 解释器的内存中:
- 首先,我们必须导入
json
包并创建两个帮助器函数:
In [31]: import json
...: def save_json(filename, data):
...: with open(filename, 'wb') as outfile:
...: json.dump(data, outfile)
In [32]: def load_json(filename):
...: with open(filename) as infile:
...: data = json.load(infile)
...: return data
- 在 Python 提示符下,让我们通过将朋友的基于 JSON 的 Twitter 配置文件保存到磁盘来测试我们的功能:
In [33]: fname = 'test_friends_profiles.json'
...: save_json(fname, friends_profiles)
- 检查以确保该文件已创建。 如果使用的是 IPython,只需键入
ls
或打开 Terminal shell,转到当前目录,然后键入ls
。 您应该在当前目录中看到test_friends_profiles.json
。 - 现在,让我们将文件加载回我们的 Python 工作区:
In [34]: test_reload = load_json(fname)
...: print(test_reload[0])
json
库是 Python 标准库的一部分,提供了一个简单但有效的 JSON 编码器和解码器。 通过save_json
函数写入文件时,我们使用json.dump
方法以默认的 UTF-8 编码将数据对象(在本例中为 Python 字典)序列化为 JSON 格式的流,并将其发送到 超出档案。 相反,load_json
函数使用json.load
,该函数将 infile 反序列化为 Python 对象。
REST API 的默认响应格式是 JSON,因此,最容易将此数据存储为 JSON,以避免不必要的数据争用。 尽管有许多不同的数据库和数据存储区可以处理 JSON 数据,但我们希望选择一种相对易于设置,本地处理 JSON 数据,免费使用且相对流行的数据库。 因此,我们将使用 MongoDB。
对于此食谱,您将需要在本地计算机上下载 MongoDB,因此请确保您具有与 Internet 的宽带连接。
以下步骤将引导您设置 MongoDB 并通过命令外壳程序使用它:
- 此阶段的第一步是安装 MongoDB。 最简单的方法是从这个页面下载最新的二进制发行版(当前为 3.4)。 适用于 Windows,Linux,macOS X 和 Solaris 的 64 位二进制发行版。
- 下载后,请按照这个页面上的相关安装指南进行操作。
- 接下来,我们需要通过在命令提示符处键入
mongod
来启动 MongoDB。 - 随着数据库的运行,我们需要通过随附的 mongo shell 连接到它。 为此,请打开另一个终端窗口或命令行,然后键入以下内容:
mongo
- 此命令假定 MongoDB 在端口
27017
和本地主机上运行。 如果不是这种情况,请如图所示启动 mongo shell,并指定正确的主机地址和端口号:
mongo address_of_host:port_number
- 现在我们正在运行 mongo shell,我们可以开始工作了。 让我们创建一个名为
test
的数据库,因此输入以下内容:
use test
- 创建
test
数据库后,我们需要创建 tweets 集合,该集合实际上将存储我们将要收获的所有 tweet。 为此,请使用以下命令:
db.createCollection('user_profiles')
- 我们要检查是否创建了集合,因此我们必须首先使用以下命令切换到当前数据库:
use test
- 要求
mongo
shell 显示此数据库中的所有集合,然后您将在本地数据库中收到一个简单的集合列表:
show collections
在这个简单明了的配方中,我们为如何使用流行的 MongoDB 奠定了基础。 我们已经安装并运行 MongoDB,并已使用 mongo shell 对其进行了连接。 此外,我们已经命名了一个新数据库test
,并创建了一个名为user_profiles
的文档集合。 MongoDB 中的集合是 MongoDB 文档的分组,这些文档在某种程度上类似于关系数据库中的表,例如Postgres
。 这些文档通常在结构和目的上相似,但是与关系数据库不同,它们不必完全统一,并且可以随时间轻松发展。 就我们的目的而言,一组 Twitter 用户的个人资料构成了一个很好的集合。
就个人而言,我们不希望在后台或登录时运行mongod
进程,因此我们从命令行启动 MongoDB。 这样,当我们不使用 MongoDB 时,它就不会在后台运行,从而消耗 CPU 周期或消耗电池。
MongoDB 并不是唯一一个非常适合 JSON 数据的 NOSQL 文档存储,并且存在许多替代方法,包括 CouchDB。 Basho 的大多数键/值存储(例如 Amazon 的 Dynamo 或 Riak)都非常适合以相对最小的设置和配置存储 JSON 数据。 借助 Amazon 的 Dynamo,您还将获得额外的好处,因为它是完全基于云的按需付费解决方案,几乎可以无限扩展。 最后,包括 Postgres 在内的一些关系数据库本机支持 JSON 数据类型,并对数据执行错误检查,以确保所存储的数据为有效 JSON。 但是,Postgres 的设置和配置往往比 MongoDB 更具挑战性。
MongoDB 的另一个优点(尽管不是排它的优点)是存在免费的托管平台即服务选项。 换句话说,您可以配置一个完整配置的在云中运行的 MongoDB 数据库,而无需做其他任何事情,而只需浏览一个非常简单的基于 Web 的界面即可登录并创建数据库。 最好的部分是 MongoLab 和 MongoHQ 都提供了免费的服务层,这意味着您无需支付任何费用即可在云中设置和使用自己的 MongoDB 数据库!
自编写本节以来,作者有机会测试和使用 RethinkDB(http://rethinkdb.com/),这是一个相对较新的开源分布式数据库,可以轻松处理 JSON 数据类型。 提供跨文档表的连接,就像关系数据库一样。 如果我们要重写本章,我们将使用 RethinkDB。
您还可以参考以下内容:
检索到用户配置文件数据并安装 MongoDB 并准备好采取行动之后,我们需要将用户配置文件 JSON 存储到适当的集合中,并且我们希望从 Python 脚本中而不是使用 mongo shell 进行操作。 为此,我们将使用 PyMongo,这是 MongoDB 员工本身推荐的从 Python 使用 MongoDB 的方法。 截至 2014 年 1 月,PyMongo 的版本为 2.6.3。
您必须已经安装了 MongoDB,并且已经准备好一些示例用户配置文件数据以准备使用此配方。
以下步骤将指导您将 Python 词典另存为 MongoDB 中的 JSON 文档:
- 首先,我们必须在系统上安装 PyMongo。 在命令行提示符下,键入以下内容:
pip install pymongo
- 根据您当前的用户权限,您可能必须在以下命令中使用
sudo
:
sudo pip install pymongo
- 如果上述安装无法正常工作并报告错误,请在线访问这个页面,查看更详细的说明,因为其中可能存在 C 依赖项 可能必须单独编译,具体取决于您的系统。
- 安装 PyMongo 后,进入 Python,IPython 或 IPython Notebook 会话并输入以下内容:
In [36]: import pymongo
In [37]: host_string = "mongodb://localhost"
...: port = 27017
...: mongo_client = pymongo.MongoClient(host_string, port)
In [38]: mongo_db = mongo_client['test']
In [39]: user_profiles_collection = mongo_db['user_profiles']
In [40]: user_profiles_collection.insert(friends_profiles)
...: user_profiles_collection.insert(followers_profiles)
安装pymongo
之后,不需要很多步骤就可以将我们连接到本地 MongoDB 数据库中,然后将其存储在 JSON 中。
我们首先创建了一个 MongoClient,它连接到主机字符串和端口中指定的mongod
。 然后,我们使用字典式访问来访问所需的mongo_db
数据库(在本例中为test
)和特定集合(在本例中为user_profiles
)。 我们调用集合的insert
方法,并将其传递给 JSON 数据进行保存。 为此,我们将为新存储的对象接收一个ObjectID
或一个ObjectIDs
列表。
这里有很多事情要注意。 我们选择使用字典样式的访问(mongo_client['test']
),以防数据库名称包含诸如-
之类的字符,以防止属性样式访问数据库(client.test
)。 另外,请注意,在将文档实际存储在集合中之前,MongoDB 不会执行任何操作。
或者,我们可以将前面的命令包装在一个函数中,以方便以后重用。 在以下命令中,save_json_data_to_mongo
可以获取单个 JSON 文档或 JSON 文档的可迭代列表以及访问 MongoDB 中特定数据库和集合所需的规范。 host_string
参数默认为localhost
,端口默认为 27017:
In [41]: def save_json_data_to_mongo(data, mongo_db,
...: mongo_db_collection,
...: host_string = "localhost",
...: port = 27017):
...: mongo_client = pymongo.MongoClient(host_string, port)
...: mongo_db = mongo_client[mongo_db]
...: collection = mongo_db[mongo_db_collection]
...: inserted_object_ids = collection.insert(data)
...: return(inserted_object_ids)
我们可以通过检查 JSON 文档的数量是否与返回的 ObjectID 的数量相匹配来改进此功能,但是我们将把练习留给读者。
Twitter 用户的个人资料包含两个不同的潜在地理信息来源:个人资料本身和最近发布的状态更新。 我们将在本食谱中利用这两个选项,着眼于在构建我们的朋友的地理可视化中的可用性。
您将需要按照以前的食谱中的指示,从 Twitter 获取收获的朋友和/或关注者的个人资料。
执行以下步骤来提取我们需要的地理数据,以可视化我们的连接的大致位置:
- 我们从 IPython 或您最喜欢的 Python REPL 开始此练习。 从文件中加载朋友的个人资料:
In[1]: fname = 'test_friends_profiles.json'
In[2]: load_json(fname)
- 接下来,我们从用户个人资料数据结构中
geo_enabled
字段的所有值中为我们的朋友构建列表。 然后,我们使用count
方法查找将geo_enabled
标志设置为true
的用户个人资料的数量:
In[3]: geo_enabled = [p['geo_enabled'] for p in friends_profiles]
In[4]: geo_enabled.count(1)
Out[4]: 127
- 我们重复与第二步中使用的过程非常相似的过程,以计算有多少朋友的用户个人资料的
location
字段为空白:
In[5]: location = [p['location'] for p in friends_profiles]
In [6]: location.count('')
Out[6]: 79
- 为了获得用户配置文件的
location
字段中包含的数据的快速 ID,我们打印出一个唯一列表并注意数据的混乱情况。 似乎location
是一个自由文本字段,这非常令人失望:
In[7]: print(set(location))
Out[7]:
...
u'Washington D.C.',
u'Washington DC',
u'iPhone: 50.122643,8.670158',
u'london',
u'new world of work',
u'san francisco',
u'seattle, wa',
u'usually in Connecticut',
u'washington, dc',
...
- 现在,我们将注意力转向
time_zone
字段:
In[8]: time_zone = [p['time_zone'] for p in friends_profiles]
In[9]: time_zone.count(None)
Out[9]: 62
In[10]: print(set(time_zone))
Out[10]: {None, u'Alaska', u'Amsterdam', u'Arizona', u'Atlantic Time (Canada)', u'Berlin',
...
- 最后,由于每个用户个人资料都包含该用户的最新状态更新(或推文),因此我们要检查用户对这些推文中的多少进行了地理标记。 请注意列表理解的逻辑和条件部分。 如果这些键存在于数据结构中,我们只需要
p['status']['geo']
:
In[11]: status_geo = [p['status']['geo'] for p in friends_profiles if ('status' in p and p['status']['geo'] is not None)]
In [12]: if status_geo: print status_geo[0]
Out[12]: {u'coordinates': [38.91431189, -77.0211878], u'type': u'Point'}
In[13]: len(status_geo)
Out[13]: 13
在此食谱中,我们使用列表推导来提取包含在朋友的用户个人资料中的潜在有用的地理信息。 对于每个数据元素,我们要问两个问题:
- 由于覆盖范围很重要,因此多少百分比的配置文件包含此信息?
- 概要文件中可用的信息的格式是什么,因此有多有用?
我们发现,大约 80%的配置文件在其配置文件中设置了位置(非常有希望),而更高比例的配置了时区(甚至是更粗糙的粒状地理指标)。 在用户个人资料数据中捕获的最新状态更新中,即使三分之一的用户个人资料(127/352)的geo_enabled
设置为True
,也有 90%以上未进行地理编码,这意味着用户有时可能会选择 到地理编码一条推文。 因此,如果我们为朋友收集了历史推文,那么我们最多应该能够找到其中约三分之一的位置。
建立了覆盖范围后,我们回顾了可用的实际数据,该数据描绘了该问题的更复杂图景。 用户配置文件的位置特征在大多数配置文件中都可用,但这只是内容的混乱。 有些位置是经度和纬度值(非常有用),有些是可识别的文本字符串地址,有些是城市和州,有些不是传统的地址格式,甚至不是位置。 时区数据似乎没有那么明显的问题,但是我们仍然必须确保 Twitter 捕获的时区名称清晰,明确地映射到实际时区。
如果我们想在 Twitter 上绘制我们的朋友,可以使用以下几种方法进行:
- 如果我们使用带有地理标记的推文的坐标,则可以立即跳至绘图。 这里唯一需要关注的是数据的稀疏性。 为了改善这种情况,我们应该为每个朋友拉动推文,以发现更多带有地理标签的推文。 这样做,我们希望能发现多达三分之一的朋友的地理数据。
- 尽管位置特征非常混乱,但是我们可以通过地理编码服务(例如 Google Maps 或 Bing)运行结果,然后看看会发生什么。 考虑到用户个人资料中某些位置的创造力,这可能不是最有效的途径。 或者,我们可以尝试使用正则表达式提取状态缩写或邮政编码,但这也将有些麻烦且耗时。
- 绘制不同时区的朋友数非常有趣,而且看起来这些数据可能很容易提取。 一个相关的问题是绘制时区有多困难?
您也可以在这个页面上参考用户 Twitter 文档。
Python 的最大优势之一就是可用软件包的数量和多样性,这些软件包使许多复杂的任务变得简单,因为其他人已经编写了大多数代码。 结果,我们有时会遇到选择的悖论,太多的选择使问题变得困惑,我们只想选择一个好的选择。 在本食谱中,我们将使用一个出色的 Python 包folium
绘制一组纬度和经度坐标:folium
-包装一个 JavaScript 库,即leaflet.js
。 您将在食谱中进一步了解有关叶的信息。
您将需要以前食谱中提取的地理数据(一组经度和纬度坐标)。 另外,我们需要安装folium
软件包,如以下部分所示,因此您需要 Internet 连接。
以下步骤将帮助您转换必须在地图上绘制的纬度和经度数据:
- 打开您的终端。 我们需要安装 Python 软件包 folium:
(sudo) pip install folium
- 转到您的源目录,然后启动 IPython 或您喜欢的 Python REPL。 我们首先需要创建两个列表,其中将包含地理数据和将用于标签的关联屏幕名称。 以下代码与上一食谱探索配置文件中可用的地理信息非常相似,但表示为循环:
status_geo = []
status_geo_screen_names = []
for fp in friends_profiles:
if ('status' in fp and fp['status']['geo'] is not None and
'screen_name' in fp):
status_geo.append(fp['status']['geo'])
status_geo_screen_names.append(fp['screen_name'])
- 现在,我们将导入我们需要的两个库:
In [44]: import folium
...: from itertools import izip
- 我们实例化
Map
对象,设置所需的初始视图位置和缩放级别,在循环中向地图添加标记和标签,最后将地图呈现为 HTML:
In [44]: map = folium.Map(location=[48, -102], zoom_start=3)
...: for sg, sn in izip(status_geo, status_geo_screen_names):
...: map.simple_marker(sg['coordinates'], popup=str(sn))
...: map.create_map(path='us_states.html')
- 现在,您的工作目录中应该有一个 HTML 文件。 双击它,您应该看到与以下屏幕截图类似的内容:
令人印象深刻的是,我们从不到十二行代码转变为完全交互式的地图,并配有地理标记,这些地理标记表示在 Twitter 上关注我们的某些用户的位置。 为此,我们利用了 folium Python 程序包的功能,该程序包又是leaflet.js
JavaScript 库的 Pythonic 包装器。 Folium 允许我们将数据从 Python 绑定到地图上,以实现整体可视化或在特定地图位置渲染标记,就像在第四步中所做的那样。
Folium 使用 Jinja2 模板包创建一个 HTML 文件,该文件包含一个非常简单的 HTML 页面,该页面带有一个div
容器,其中包含地图和自定义 JavaScript 代码,以便使用leaflet.js
库。 leaflet.js
库处理实际的地图生成:
<!DOCTYPE html>
<head>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-
0.5/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-
0.5/leaflet.js"></script>
<style>
#map {
position:absolute;
top:0;
bottom:0;
right:0;
left:0;
}
</style>
</head>
<body>
<div id="map" style="width: 960px; height: 500px"></div>
<script>
var map = L.map('map').setView([48, -102], 4);
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: 'Map data (c) <a
href="http://openstreetmap.org">OpenStreetMap</a>
contributors'
}).addTo(map);
var marker_1 = L.marker([38.56127809, -76.04610616]);
marker_1.bindPopup("Pop Text");
map.addLayer(marker_1)
</script>
</body>
我们强烈建议您仔细研究 folium(和leaflet.js
)的其他功能以及该库的基础源代码。
可视化地理数据很复杂。 您不仅需要渲染一个或多个复杂坐标系的能力,还需要原始的地图数据,这些数据定义了国家,道路,河流以及除了您自己的数据外可能还要显示的任何其他内容。 幸运的是,Python 在解决此问题时提供了很多选择。
这些软件包通常分为两大类。 一流的地理空间可视化软件包来自数十年来一直存在的科学研究需求。 对于此软件,渲染器和所需的地理数据位于用户的本地计算机上。 为了使 Python 程序包正常工作,通常需要编译和安装该软件的其他几个层。
非常强大的 Matplotlib 底图工具包就是一个很好的例子。 浏览说明,我们看到许多依赖项。 尽管我们已经处理过 matplotlib 和 NumPy,但另外两个要求-GEOS
和Proj4 map
库可能会引起问题。 几何引擎-开源(GEOS)的 C ++ 源代码实际上包含在底图中,但是必须单独编译和构建。 这需要用另一种语言编译软件是一个巨大的危险信号。 对于 Python 软件包,这通常会导致一个麻烦,一个可能需要花费数小时或数天才能解决的编译错误和技术问题(缺少头文件,稍微不标准的目录结构,错误的编译器等)。 如果您在 Mac OS X 上,尤其是在 Mavericks 上,那么您会获得更多乐趣,因为 Apple 倾向于在每个发行版中进行一些细微的改动,这会破坏以前可以正常使用的 make 文件。 结果,我们从类别 2 中选择了一个映射库,其描述如下。
程序包的第二个类提供了与 JavaScript 库进行交互的 Python 方式,该 JavaScript 库实际上处理地图和数据的呈现。 这通常需要我们将结果输出为 HTML,这常常是不正确的。 这些 JavaScript 库本身分为两类。 首先,还有更多的纯制图库,例如 Google Maps 和Leaflet.js
,它们是为在线呈现地图而构建的,但可以将其重新用于更特殊的地理空间数据可视化需求。 其次,有D3.js
,这是一个通用库,用于基于能够进行精美地理可视化的数据来处理文档。 我们选择大叶植物是因为它简单而优雅。 当软件运行时,它使我们的生活和分析变得更加轻松。
您还可以参考以下内容: