3.12.3 图像的基本预处理


目录:

说明:本文是《Python数据分析与数据化运营》中的“2.2 使用Python获取运营数据”中的第二部分,由于本节内容较多,这里分几个文章。

2.2.2 从Excel获取运营数据

现有的Excel分为两种格式:xls(Excel 97-2003)和xlsx(Excel 2007及以上)。

Python处理Excel文件主要是第三方模块库xlrd、xlwt、pyexcel-xls、xluntils和pyExcelerator,以及win32com和openpyxl模块,此外Pandas中也带有可以读取Excel文件的模块(read_excel)。

基于扩展知识的目的,我们使用xlrd模块读取Excel数据。 首先安装该库,在系统终端命令行输入命令pip install xlrd。 然后我们以“附件-chapter2”文件夹demo.xlsx数据文件为例,介绍该库的具体应用。数据概览如图2-7所示:

2-7

图2-7 数据文件内容

import xlrd # 导入库

# 打开文件
xlsx = xlrd.open_workbook('demo.xlsx')
# 查看所有sheet列表
print ('All sheets: %s' %
xlsx.sheet_names())
print
('===================================') # 内容分割线
# 查看sheet1的数据概况
sheet1 = xlsx.sheets()[0] # 获得第一张sheet,索引从0开始
sheet1_name = sheet1.name # 获得名称
sheet1_cols = sheet1.ncols # 获得列数
sheet1_nrows = sheet1.nrows # 获得行数
print ('Sheet1 Name: %s\nSheet1 cols:
%s\nSheet1 rows: %s') % (sheet1_name, sheet1_cols, sheet1_nrows)
print ('===================================') # 内容分割线
# 查看sheet1的特定切片数据
sheet1_nrows4 = sheet1.row_values(4) # 获得第4行数据
sheet1_cols2 = sheet1.col_values(2) # 获得第2列数据
cell23 = sheet1.row(2)[3].value # 查看第3行第4列数据
print ('Row 4: %s\nCol 2: %s\nCell 1: %s\n'
% (sheet1_nrows4, sheet1_cols2, cell23))
print
('===================================') # 内容分割线
# 查看sheet1的数据明细
for i in range(sheet1_nrows): # 逐行打印sheet1数据
print (sheet1.row_values(i))

上述代码中,我们先读取一个Excel文件,再查看所有sheet(工作簿)并输出sheet1相关属性信息;然后查看sheet1中特定数据行、列和元素的信息;最后我们用循环的方式,依次读取每个数据行并打印输出。

以下是代码执行后打印输出的结果:

All sheets: [u'Sheet1']
===================================
Sheet1 Name: Sheet1
Sheet1 cols: 4
Sheet1 rows: 10
===================================
Row 4: [u'431381197408191515',
u'\u6709\u6548', 42725.0, u'\u6df1\u5733\u5e02']
Col 2: [u'Create_Time', 42725.0, 42725.0,
42725.0, 42725.0, 42725.0, 42725.0, 42725.0, 42725.0, 42725.0]
Cell 1: 深圳市
===================================
[u'ID_number', u'Status', u'Create_Time',
u'Business_City']
[u'431381198109106573', u'\u6709\u6548',
42725.0, u'\u6df1\u5733\u5e02']
[u'431381198809122734', u'\u6709\u6548',
42725.0, u'\u6df1\u5733\u5e02']
[u'431381197903117478', u'\u6709\u6548',
42725.0, u'\u6df1\u5733\u5e02']
[u'431381197408191515', u'\u6709\u6548',
42725.0, u'\u6df1\u5733\u5e02']
[u'431381197606166011', u'\u6709\u6548',
42725.0, u'\u6df1\u5733\u5e02']
[u'43138119850623339X', u'\u6709\u6548',
42725.0, u'\u6df1\u5733\u5e02']
[u'431381198908223477', u'\u6709\u6548',
42725.0, u'\u6df1\u5733\u5e02']
[u'431381198901176911', u'\u6709\u6548',
42725.0, u'\u6df1\u5733\u5e02']
[u'43138119870827275X', u'\u6709\u6548',
42725.0, u'\u6df1\u5733\u5e02']

提示 在上述打印输出的内容中,我们发现第二列、第三列、第四列与原始数据似乎不同。第二列和第四列“异常”的原因是将中文编码统一转换为Unicode编码,便于在不同程序间调用;第三列“异常”是由于将日期格式转换为数值格式而已。

上述操作只是将数据从Excel中读取出来,基于读取的数据转换为数组便可以进行矩阵计算。由于矩阵计算大多是基于数值型数据实现的,因此上述数据将无法适用于大多数科学计算场景,这点需要注意。

总结:在企业实际场景中,由于Excel本身的限制和适用,其无法存储和计算过大(例如千万级的数据记录)的数据量,并且Excel本身也不是为了海量数据的应用而产生的。因此,Excel可以作为日常基本数据处理、补充数据来源或者汇总级别的数据进行读取,同时也可以作为数据结果展示的载体,这种应用下对于大量数值表格的应用效果非常好。

2.2.3 从关系型数据库MySQL读取运营数据

在第一章“1.2.4 数据库和客户端”中我们介绍了安装MySQL和Navicat的方法和关键步骤。这里我们介绍如何从利用Python从MySQL中读取数据。

在“附件-chapter2”文件夹中有一个名为order.xlsx的数据文件,我们先把文件导入MySQL数据库,然后再基于数据库进行相关操作。

第一步 ,新建一个数据库用于存放要导入的目标数据。

步骤1 打开Navicat,左侧导航栏中有我们已经建立好的数据库链接,在第一章中我们已经建立好了本地连接,名为“127.0.0.1”。

步骤2 双击该数据库连接,会展示出所有当前连接(对应的用户权限)下可用的数据库,如图2-8。

2-8 图2-8 127.0.0.1下的数据库

步骤3 为了保持不同数据的独立性,我们新建一个数据库,单独存放特定主题的数据。在“127.0.0.1”名称上右键,在弹出的菜单中选择新建数据库,然后做如下设置并点击确定。完成之后在左侧的“127.0.0.1”链接下会新增一个刚才新建的数据库“python_data”。

第二步 ,将Excel数据导入数据库。

双击刚才新建的名为“python_data”的数据库名称,激活数据库连接。此时右侧功能栏中的部分功能已经可用。点击“导入向导”,开始导出Excel数据。如图2-10。

2-10 图2-10 创建导入向导

步骤1 选择数据文件格式:Excel 2007

步骤2 选择数据源文件。

2-11 图2-11 设置导入数据源

步骤3 定义附加选项。由于数据文件中第一行是表头,因此数据从第二行开始;日期分隔符与数据文件一致,需要设置为“-”,其他都为默认值。 2-12 图2-12 设置导入附件选项 步骤4 设置目标数据表名为order。

步骤5 设置字段类型、长度、约束等信息。其中主要的设置是将order_data类型设置为date(日期型),长度为0(不做限制);order_tme类型设置为time(时间型),长度为0(不做限制);total_amount类型设置为float(浮点型),长度默认为255。 2-13 图2-13 设置字段类型等约束

提示 实际上,我们的数据很可能用不了那么多的“存储空间”,因此在设置时存在数据空间冗余,不过没关系,这里我们仅仅做入门引导之用。真正涉及到数据库表的模型设计有很多学问,这里的设置不会影响我们做数据统计分析之用。

步骤6 设置导入模式。由于我们是新创建表,因此设置为: 添加 步骤7 启动导入任务。点击 开始 即可。

等待系统导入数据,导入成功后,会在窗口中显示如下信息,最终的信息中包含“successfully”,如图2-14。点击关闭按钮即可。

2-14 图2-14 导入成功提示信息

至此我们已经成功导入数据,在数据库python_data下新增了一个名为“order”的数据表。我们要初步验证导入的数据是否与原始数据一致。双击打开该表 。

打开表之后,会弹出信息提示该表没有主键。该信息不影响我们使用该表,点击确定即可。

2-15

图2-15 提示表没有主键

提示 在创建表时,我们完全可以指定主键,例如本案例中的order_id是唯一的,那么可指定该字段作为主键,更可以创建一个自增长ID作为主键。本节的主要内容是介绍如何导入外部数据到数据库,有关数据库建表还有很多话题,在此不作更多介绍。

打开order表之后,会显示如下表预览内容:

2-16

图2-16数据表概览 在当前窗口,点击“文件-查询表”或直接使用快捷键Ctrl+Q,打开SQL窗口,然后输入SELECT * FROMorderLIMIT 5;点击“运行”或使用快捷键Ctrl+R,返回前5条结果,如下图2-17。读者可以在此输入其他SQL或直接浏览数据表来核对数据导入是否准确。

注意 由于表名order是数据库中的排序关键字,因此在查询表时需要写为order,否则会报错。

2-17 图2-17运行SQL并返回结果

导入Excel数据主要核对的信息点包括:

  • 数据记录数是否一致:使用SELECT COUNT(*) FROM order;或在导入成功提示窗口也可以看到)
  • 数据字段数量是否一致,导入表中的字段不能多也不能少
  • 查看数据内容是否一致,尤其是日期、时间等非字符串字段的数据,很多时候时间型的数据转换会出现0000-00-00,这通常是导入设置的日期分隔符问题
  • 数据精准度是否一致,尤其是原始数据是浮点型,对应到数据库字段是否还是浮点型(注意小数位数),问题通常出现在字段类型和位数设置
  • 注意字段的最大长度,不同的数值型数据长度位数是不同,例如整数型tinyint、int,浮点型的单精度和双精度浮点等,这对于汇总级别的数据会产生极大影响(因为某些字段的汇总数据可能非常大)。

通过Python连接MySQL需要有Python库来建立连接,本节使用MySQL官方驱动连接程序,更多信息可在“1.2.3 Python第三方库”中的“5.数据库连接库”中找到。 以下是Python读取数据库数据的基本方法:

import mysql.connector # 导入库

config = {'host': '127.0.0.1', # 默认127.0.0.1
'user': 'root', # 用户名
'password': '123456', # 密码
'port': 3306, # 端口,默认为3306
'database': 'python_data', # 数据库名称
'charset': 'gb2312' # 字符编码
}
cnn = mysql.connector.connect(**config) # 建立mysql连接
cursor = cnn.cursor() # 获得游标
sql = "SELECT * FROM `order`" # SQL语句
cursor.execute(sql) # 执行sql语句
data = cursor.fetchall() # 通过fetchall方法获得数据
for i in data[:2]: # 打印输出前2条数据
    print(i)
cursor.close() # 关闭游标
cnn.close() # 关闭连接

上述代码中,实现了通过Python连接MySQL查询所有的数据,并输出前2条数据的功能。执行结果如下:

(u'3897894579', datetime.date(2010, 3, 11),
datetime.timedelta(0), u'PENDING_ORDER_CONFIRM', u'1038166', 59.0)
(u'3897983041', datetime.date(2010, 3, 11),
datetime.timedelta(0), u'REMOVED', u'1041656', 19.9)

在应用MySQL中,除了查询所有数据外,更多时候我们还会应用更多SQL技巧来帮助我们快速找到目标数据并作初步处理。以下是常用SQL语法。

只查询前N条数据而非全部数据行记录 。示例:只查询前100条数据记录。

SELECT * FROM `order` LIMIT 100;

知识点:LIMIT为限制的数据记录数方法,语法为limit m,n,意思是从第m到m+n条数据。m可省略,默认值从0开始,如上述示例中是从0开始,取100条数据。例如,要从第11条开始取,取10条可以写为SELECT * FROMorderLIMIT 11, 10;

只查询特定列(而非全部列)数据。示例:只查询total_amount和order_id两列数据。

SELECT total_amount, order_id from `order`;

知识点:选择特定列只需将列名写到表达式中即可,多个列名用英文逗号分隔。另外,还可以使用表名.字段名的写法,例如上述表达式可以写成SELECT order.total_amount, order.order_id fromorder``;这种写法会出现在表达式中出现多个表的情况下,例如多表关联查询。

查询特定列去重后的数据 。示例:查询针对user_id去重后的数据。

SELECT DISTINCT user_id FROM `order`;

知识点:关键词 DISTINCT 用于返回唯一不同的值,后面接字段名即要去重的字段。如果后面接多个字段会默认对多个字段同时进行比较,只有多个字段完全相同时才会去重。DISTINCT常用来返回去重后的特定ID,或者与COUNT配合使用,查询特定数据记录的唯一数量。

查询带有1个条件的数据 。示例:查询total_amount<100的所有数据。

SELECT * FROM `order` WHERE total_amount < 100;

知识点:WHERE是条件关键字,后面接具体条件。本示例中,由于total_amount为浮点型,因此直接可与数值型比较;如果是字符串型,则比较值需要加引号用来表示一致的字段类型。

查询带有多个条件(同时满足)的数据 。示例:查询total_amount<100且status为REMOVED的所有数据。

SELECT * FROM `order` WHERE total_amount < 100 and `status` = 'REMOVED';

知识点:多个条件使用and表示且的关系,多个条件必须同时满足。MySQL中字段不区分大小写。由于status也是关键字,因此也需要使用status

查询带有多个条件(满足任意一个)的数据 。示例:查询total_amount<100或status为REMOVED的所有数据。

SELECT * FROM `order` WHERE total_amount < 100 or `status` = 'REMOVED';

知识点:多个条件使用or表示或的关系,多个条件满足任意一个条件即可。

查询特定条件值在某个值域范围内的数据。示例:查询status的值为REMOVED或NO_PENDING_ACTION或PENDING_ORDER_CONFIRM。

SELECT * FROM `order` WHERE `status` in ('REMOVED','NO_PENDING_ACTION','PENDING_ORDER_CONFIRM');

知识点:对于特定字段采用“穷举法”列出值域的方法,也可以使用or方法连接多个条件,但使用列表的穷举法会使得表达式更简单。

使用正则表达式查询具有复杂条件的数据 。示例:查询user_id以106开头且order_id包含04的所有订单数据。

SELECT * FROM `order` WHERE user_id LIKE '103%' and order_id LIKE '%04%';

知识点:正则表达式通常用在复杂条件中,通过使用关键字LIKE来连接不同的正则语法,LIKE后面接具体的匹配模式,常用的匹配模式包括:

  • 以*开头:'字符串%',例如'ABD%'表示以ABD为开头的所有匹配;
  • 以*结尾:'%字符串',例如'% ABD'表示以ABD为结尾的所有匹配;
  • 包含*:'%字符串%',例如'%ABD%'表示包含ABD的所有匹配。

关于正则表达式还有更多强大的语法规则,限于篇幅不作展开介绍。

将查询到的数据倒叙排列。示例:将total_amount金额在10~100之间的所有数据,按照订单ID倒叙排序。

SELECT * FROM `order` WHERE 10 <total_amount < 100 ORDER BY order_id DESC;

知识点:在查询表达式ORDER BY的结尾,通过使用DESC来表示倒叙(逆序)排序;省略是默认使用ASC正序(顺序)排序。

查询指定列中不包含空值的所有数据 。示例:查询order_id不为空的所有数据。

SELECT * FROM `order` WHERE order_id IS NOT NULL;

知识点:在MySQL中,IS NULL表示为空, IS NOT NULL 表示非空。需要注意的是,NULL不代表空字符串或者空格,而是真正意义上的没有任何数据,类似于Python中的None。

上述案例只是展示了如何取数,更多情况下,我们会配合特定函数和方法做数据计算、整合和探索性分析,例如:

  • 使用MySQL聚合函数求算术平均值、计数、求最大最小值、做分类汇总等;
  • 使用MySQL数学函数,用来求绝对值、对数计算、平方根等;
  • 使用MySQL字符串函数进行字符串分割、组合、截取、匹配、处理等;
  • 使用MySQL的日期函数进行日期获取、转换、处理等;
  • 使用MySQL将多个表(2个或2个以上)数据进行关联、匹配和整合。

关于在Python还是MySQL中进行数据处理计算的争议

在Pythoner(Python工作者)看来,MySQL的很多工作Python本身也能胜任,为什么还要耗费时间和精力学习如何在MySQL中完成这些数据工作,直接用Python读取数据然后基于Python的相关库进行数据运算岂不更好?

  • Python的工作强项并不是数据计算,而是其灵活、高效、简易和集成多方的工作方式、效率和效果。MySQL(以及其他关系型数据库)作为成熟的数据存储和集成解决方案,在(关系型)数据本身方面更具优势,尤其是对于数据结构定义(实体、属性、关系)、关系操作(选择、连接、聚合等)、关系完整性约束(主外键、唯一性等)等方面具有成熟且稳定的应用价值,这对于数据处理至关重要,因此可以说MySQL在结构化数据存储和初步处理工作上比Python更专业。
  • 还有一个更加关键的原因是如果所有数据工作都由Python完成,那么Python的事务处理在某些情况下一定会面临资源瓶颈、工作效率等问题,届时可能会导致程序崩溃和报错,这将大大降低程序的可靠性以及结果输出的效率,对于海量数据工作尤为如此。

因此,很多时候不是Python不能做,而是在合适的时机选择最合适的工具来完成才是最好的选择。

2.2.4 从非关系型数据库MongoDB读取运营数据

由于MongoDB一般都是企业级数据存储,因此这里我们使用在第一章已经安装过的SSH远程客户端SecureCRT。如果读者有自己在虚拟机或本地安装MongoDB,也可以使用,操作方法类似。

双击SecureCRT打开程序,点击顶部的“快速连接”按钮,新建连接。

2-18 图2-18新建快速连接

然后根据服务器具体配置情况,配置如下信息,其中主机名和用户名需要单独配置,其他的根据实际情况询问IT或运维管理人员(本案例中都是默认值)。可勾选用于以后启动该程序时直接显示快捷入口。

2-19 图2-19 填写连接配置信息

点击连接之后,会弹出密码输入窗口,输入密码,可勾选 用于以后无需重复输入密码,具体视公司安全规定执行。

2-20 图2-20 填写密码

如果服务器配置信息填写正确,连接服务器成功会显示如下信息:

2-21 图2-21 成功连接服务器

要想通过Python调用MongoDB,需要安装PyMongoDB,直接使用pip install pymongo即可实现。

注意 不同的服务器环境对于可访问外网的限制有差异,有的服务器可以访问外网,而有的服务器却不能。我们在第一章中的大多数安装方法,默认都是通过pip请求外部网络资源实现的,对于无法直接从服务器连接外网的,请参照“1.2.3 Python第三方库”中的方法,先将安装包和依赖包下载到本机,然后再拷贝到服务器上,再通过setup和pip命令安装。

使用Python连接MongoDB之前,需要服务器上的MongoDB处于启动状态。查看是否成功启动的方法是找一个可以直接连接或访问服务器的浏览器,直接输入IP:端口,如果出现“It looks like you are trying to access MongoDB over HTTP on the native driver port.”字样的提示则说明已经正常启动和运行。

例如:笔者的服务器上已经启动了该服务,在浏览器中输入http://10.66.202.134:27017/后提示如下信息:

2-22 图2-22 MongoDB服务启动后的浏览器返回信息

提示 MongoDB也提供了Windows版本的程序,有兴趣的读者,可登陆https://www.mongodb.com/download-center#community下载程序并进行本地部署安装。

要在服务器上进入Python环境,直接在终端窗口输入命令python。在Python命令行窗口中,通过如下方法调用MongoDB数据。

from pymongo import MongoClient # 导入库

client = MongoClient() # 建立连接
client = MongoClient('10.66.202.134',27017) # 环境变量初始化
db = client.test_py # 选择test_py库
orders = db.ordersets # 选择orders集合
terms = [{"user":"tony", "id": "31020", "age":"30", "products": ["215120", "245101","128410"], "date": "2017-04-06"}, {"user": "lucy", "id": "32210", "age": "29", "products": ["541001", "340740", "450111"],"date": "2017-04-06"}] # 定义一条数据集合用于插入
orders.insert_many(terms) # 插入数据
print (orders.find_one()) # 获取一文档数据
print('========================================')
for i in orders.find(): # 获取所有文档数据并展示
    print (i)

上述代码中,我们连接到MongoDB后选择了test_py库下的ordersets集合,然后插入2条数据到集合中,再从集合中查看单独一条以及所有集合数据。 以下是代码执行后打印输出的结果:

{u'age': u'30', u'products': [u'215120',u'245101', u'128410'], u'user': u'tony', u'date': u'2017-04-06', u'_id': ObjectId('58eb1f0b2f76bf26108b4898'), u'id': u'31020'}
========================================
{u'age': u'30', u'products': [u'215120', u'245101', u'128410'], u'user': u'tony', u'date': u'2017-04-06', u'_id': ObjectId('58eb1f0b2f76bf26108b4898'), u'id': u'31020'}
{u'age': u'29', u'products': [u'541001', u'340740', u'450111'], u'user': u'lucy', u'date': u'2017-04-06', u'_id': ObjectId('58eb1f0b2f76bf26108b4899'), u'id': u'32210'}

除了上述基本查询语句外,PyMongo也提供了类似于MySQL一样的条件过滤。

查询特定文档数据 。示例,值查询第2条数据。

orders.find()[1] # 查询特定索引的数据记录 知识点:通过增加索引值可以指定查询特定索引的文档数据。注意索引值从0开始,0代表第一条是数据。

查询特定范围内的文档数据 。示例,值查询第1-2条数据。

orders.find()[0:2] # 查询特定范围内的数据记录

知识点:通过增加索引值可以指定查询特定索引范围的文档数据(切片)。注意索引值为空则默认从0开始,0代表第一条是数据。上述表达式可以写成orders.find()[:2]。索引结束值默认不包含,例如上述语法中的0:2的实际索引只有0和1。

增加查询条件 。示例:只查询user为lucy的文档数据。

orders.find({"user": "lucy"}) # 所有数据,注意使用迭代方法查看数据
orders.find_one({"user": "lucy"}) # 单条数据

知识点:作为Key-Value形式的代表应用之一,MongoDB几乎所有的用法都以K-V的形式存在。过滤条件也不例外,只需在find函数中,以Key-Value的形式设置过滤条件即可,其中Key为过滤维度,Value为对应值。

排序 。示例:针对user排序输出。

orders.find({"user": "lucy"}).sort("user") # 所有数据,注意使用迭代方法查看数据

知识点:通过使用Sort方法设置排序维度。默认是正序,也可以指定为倒叙排列,同时也可以指定多个字段排序规则,例如collection.find().sort([('field1', pymongo.ASCENDING), ('field2', pymongo.DESCENDING)])。从MongoDB 2.6开始,还提供了按照文本相关性的排序方法,例如collection.find({'score': {'$meta':'textScore'}}).sort([('score', {'$meta': 'textScore'})])。

除了上述基本查询和过滤条件外,PyMongo也提供了用于做数据统计、分析和探索的基本方法,更多信息,具体查阅http://api.mongodb.com/python/current/

总结:在企业实际应用中,非关系型数据库往往基于“大数据”的场景产生,伴随着海量、实时、多类型等特征而来。这些数据库通过舍弃了关系型数据库的某些特征和约束,然后在特定方面进行增强,因此才能满足特定应用需求。非关系型数据库由于约束性、规范性、一致性和数据准确性低于关系性数据库,因此常用于实时海量数据读写、非结构化和半结构化信息读写、海量集群扩展、特殊场景应用等。所以在金融、保险、财务、银行等领域内,这种应用比较少;而互联网、移动应用等新兴产业和行业领域则应用较多。

2.2.5 从API获取运营数据

为了更好的让所有读者都能了解从API获取数据的具体过程,本节使用百度免费API作为实际数据来源。百度API提供了众多地图类功能,可用于基本地图、位置搜索、周边搜索、公交驾车导航、定位服务、地理编码及逆地理编码等。本节使用的是百度Web服务API中的Geocoding API。 Geocoding

API用于提供从地址到经纬度坐标或者从经纬度坐标到地址的转换服务,用户可以发送请求且接收JSON、XML的返回数据。该应用可用于对运营数据中的地址相关信息进行解析,从而获得有关经纬度信息,这些信息可用于进一步的基于地理位置的解析、展示和分析等应用。要获得该API,读者需要拥有百度相关账户和AK信息。

第一步 获得百度账户,没有账户的读者可在https://passport.baidu.com/v2/?reg 免费注册获取。

第二步 注册成为百度开放平台开发者,读者可进入http://lbsyun.baidu.com/apiconsole/key?application=key 完成相关注册。该过程非常简单,遵循引导整个过程在5分钟以内即可完成。

2-23 图2-23提交成功

第三步 注册完成之后,会有一封名为“【百度地图开放平台】开发者激活邮件”发送验证链接到指定(注册时邮箱)邮箱,点击链接进行验证。

2-24 图2-24激活验证链接

第四步点击“申请秘钥”进入创建应用界面,在该应用创建中,我们主要使用Geocoding API v2,其他应用服务根据实际需求勾选。IP白名单区域,如果不做限制,请设置为“0.0.0.0/0”。设置完成后,点击提交。

2-25 图2-25创建应用

第五步 获得AK秘钥。完成上述步骤之后,会默认跳转到应用列表界面,界面中的“访问应用(AK)”便是该应用的秘钥。

2-26 图2-26获得AK秘钥

1. 获取并解析json数据

我们先通过Python请求该API来获得json格式的数据。本示例的目标是通过给百度API发送一条地理位置数据,返回其经纬度信息。 本节会用到Python第三方库requests,读者需要先通过pip install requests进行安装。完整代码如下:

import requests # 导入库
add = '北京市中关村软件园' # 定义地址
ak =
'DdOyOKo0VZBgdDFQnyhINKYDGkzBkuQr' # 创建访问应用时获得的AK
url = 'http://api.map.baidu.com/geocoder/v2/?address=%s&output=json&ak=%s' # 请求URL
res = requests.get(url % (add, ak)) # 获得返回请求
add_info = res.text # 返回文本信息
print (add_info) # 打印输出
  • 在上述代码中,我们先导入一个requests库,该库用来发送网络请求,支持多种发送模式,可以用来做网页内容抓取、自动化网页测试、网页内容采集等。
  • 第二行我们定义了一个地址,该地址通常是运营中的地址类信息,例如:通讯地址、注册地址、收货地址、联系地址等。
  • 第三行ak是创建访问应用时获得的AK,注意百度有每天6000次的限制,读者可填写自行申请的AK。
  • 第四行定义了一个通过get方法发送的URL请求地址,地址中的address和ak的具体值使用占位符代替,在后面用到时具体赋值,这种方法经常用到地址、ak有变化的场景中。例如:我们通常会创建多个应用(基于每个账户有限制,以及分开治理应用的考虑),然后从数据库中读取一系列地址用于解析,此时的地址和AK都需要能动态赋值。

提示 在API请求方法中,最常用的是get和post方法。前者用于向服务器以“明文”(所有参数都在URL中体现)的形式请求数据,常用于普通的页面或服务请求,例如浏览网页、搜索关键字等都是GET方法;后者则以“暗语”(所有的数据信息都在HTTP消息中)以键值对的形式发送,在URL中是看不到具体信息的,常用于数据保密性高的场景,例如登录、注册、订单等表单的处理都是POST方法。

  • 第五行通过get方法发送请求到上面定义的URL中,并具体赋值。
  • 第六行将返回对象的文本信息赋值到add_info,返回对象中还包括非常多的相关属性信息,例如状态码、cookie、reason、headers、编码等,这些信息可用于判断是否正确返回、问题原因、状态等信息。
  • 最后一行打印输出返回的文本信息。 以下是代码执行后打印输出的结果:

    {"status":0,"result":{"location":{"lng":116.29933765654373,"lat":40.05399730253742},"precise":0,"confidence":40,"level":"工业园区"}}

返回结果包括以下字段:

  • status:返回结果状态值, 成功返回0。
  • location:经纬度坐标,其中lat是纬度值,lng是经度值。
  • precise:位置的附加信息,是否精确查找。1为精确查找,即准确打点;0为不精确,即模糊打点。
  • confidence:可信度,描述打点准确度。
  • level:百度定义的地址类型。

该结果可以通过JSON进行格式化处理。

import json # 导入库
add_json = json.loads(add_info) # 加载Json字符串对象
lat_lng =
add_json['result']['location'] # 获得经纬度信息
print (lat_lng) # 打印输出

代码中,我们导入Python自带的json库。该库支持Python其他对象和Json对象之间的相互转换。

先将获得的地址信息格式化为json字符串(字典类型),然后通过读取字典的Key来获得其Value,由于返回的地址信息中包含字典嵌套,因此这里需要依次读出第一层和第二层信息。读者可通过add_json.items()来更加直观的观察2层嵌套。

上述代码执行后,返回的结果(字典类型)可进行进一步处理:

{u'lat': 40.05399730253742, u'lng':116.29933765654373}

2. 获取并解析xml数据

Geocoding API也提供XML格式的返回数据,下面以获得XML格式的数据为例介绍代码过程。

import requests # 导入库
add = '北京市中关村软件园' # 定义地址
ak ='DdOyOKo0VZBgdDFQnyhINKYDGkzBkuQr' # 创建访问应用时获得的AK
url ='http://api.map.baidu.com/geocoder/v2/?address=%s&output=xml&ak=%s' # 请求URL
res = requests.get(url % (add, ak)) # 获得返回请求
add_info = res.text # 返回文本信息
print (add_info) # 打印输出

上述所有代码与json格式的代码完全相同,只在第四行定义url时,将output的返回值类型设置为xml,执行后返回结果如下:

<?xml version="1.0"
encoding="utf-8"?>
<GeocoderSearchResponse>
<status>0</status>
<result>
<location>
<lng>116.299337657</lng>
<lat>40.0539973025</lat>
</location>
<precise>0</precise>
<confidence>40</confidence>
<level>工业园区</level>
</result>
</GeocoderSearchResponse>

接着我们通过引入一个xml格式化处理库来从中提取经纬度信息。关于xml文件的解析,Python默认和第三方的常用库包括xml、libxml2、lxml、xpath等,我们使用Python自带的xml进行处理。

设置字符编码为utf-8

import sys
reload(sys)
sys.setdefaultencoding('utf-8')
import xml.etree.ElementTree as Etree # 导入xml中的ElementTree方法
root = Etree.fromstring(add_info) # 获得xml的根节点
lng = root[1][0][0].text # 获得lng数据
lat = root[1][0][1].text # 获得lat数据
print ('lng: %s' % lng) # 格式化打印输出
print ('lat: %s' % lat) # 格式化打印输出

上述代码中,前4行实现了一个功能,将代码的字符编码设置为utf-8,否则系统会默认为ASCII,对返回的带有中文字符串无法识别而报错。

代码第五行导入XML自带的ElementTree方法,该方法可以实现对XML内容的查询、新建、修改等操作,是XML中比较简单容易上手的方法(XML除了ElementTree外,还提供了DOM和SAX方法)。

代码第六行获得从API得到的XML对象并获得根节点。 代码第七、八行分别通过嵌套读取从根节点开始向下的第3层经纬度数据。 代码最后两行格式化打印输出

上述代码执行后返回结果如下:

lng: 116.299337657
lat: 40.0539973025

有关百度API的更多信息,具体查阅http://lbsyun.baidu.com/index.php?title=webapi/guide/webservice-geocoding

总结:在API应用中,中文的编码处理常常是非常头疼的细节。因此,如果可以尽量少的直接在API的数据中出现中文字符。在实际企业应用中,会出现多种API形式,但无论哪种形式,其基本实现思路都是一致的:导入库→定义请求变量→发送请求→获得返回数据→格式化并获得目标数据,因此需要掌握Json和xml的数据与其他数据的转换方法。