- 本书赞誉
- 前言
- 目标读者
- 不适合阅读本书的读者
- 本书结构
- 什么是数据处理
- 遇到困难怎么办
- 排版约定
- 使用代码示例
- 致谢
- 第 1 章 Python 简介
- 第 2 章 Python 基础
- 第 3 章 供机器读取的数据
- 第 4 章 处理 Excel 文件
- 第 5 章 处理 PDF 文件 以及用 Python 解决问题
- 第 6 章 数据获取与存储
- 第 7 章 数据清洗:研究、匹配与格式化
- 第 8 章 数据清洗:标准化和脚本化
- 第 9 章 数据探索和分析
- 第 10 章 展示数据
- 第 11 章 网页抓取:获取并存储网络数据
- 第 12 章 高级网页抓取:屏幕抓取器与爬虫
- 第 13 章 应用编程接口
- 第 14 章 自动化和规模化
- 第 15 章 结论
- 附录 A 编程语言对比
- 附录 B 初学者的 Python 学习资源
- 附录 C 学习命令行
- 附录 D 高级 Python 设置
- 附录 E Python 陷阱
- 附录 F IPython 指南
- 附录 G 使用亚马逊网络服务
- 关于作者
- 关于封面
14.8 监控自动化程序
花费时间监控自动化程序是必需的。如果你不知道任务是否完成了,或者不知道任务成功还是失败了,你最好还是不要运行它们了。因此,监控你的脚本和运行它们的机器是过程中非常重要的一部分。
举个例子,如果有一个隐藏的 bug,数据并没有被加载,而且每天或每周,你都在老的数据上运行报告,这是个非常可怕的消息。在自动化程序中,失败并不总是很明显,因为脚本可能使用旧数据或者在存在错误和不一致的情况下继续运行。监控是观察脚本成功或者失败,即使所有的迹象都表明脚本仍然在正常地执行。
监控可以有小的或者大的足迹,这取决于任务的规模和需求。如果你会有一个大规模的自动化程序,跨越多个服务器,你可能需要使用一个大型的分布式监控系统,或者具有监控功能的服务。然而,如果你正在一个家庭服务器上运行任务,可能只需要使用内置的 Python 日志工具。
你可能还想在脚本中使用一些警报和通知工具。在 Python 中,上传、下载、用邮件发送,甚至用 SMS 发送结果都非常简单。在这一节,我们会介绍几种不同的日志选择,并查看发送通知的方法。在全面测试并深入理解日常监控所有潜在的错误之后,你可以充分地自动化任务,并通过警报管理错误。
14.8.1 Python日志
最基本的监控脚本的方式是日志。你很幸运,Python 有一个非常健壮并且有丰富特性的日志环境,它是标准库的一部分。与你交互的客户端或库通常都拥有集成了 Python 日志系统的日志工具。
使用 Python 内置的日志模块中提供的简单基本的配置,可以实例化日志器,并且开始记录。之后你可以使用很多不同的配置选项,来迎合脚本特殊的日志需求。Python 的日志模块允许你设置特定的日志等级(https://docs.python.org/2/library/logging.html#logging-levels)和日志的记录属性(https://docs.python.org/2/library/logging.html#logrecord-attributes),调整日志的格式。日志器对象同样拥有方法和属性(https://docs.python.org/2/library/logging.html#logger-objects),根据你的需要,这也会很有用处。
下面是在代码中如何创建并使用日志的实例。
import logging from datetime import datetime def start_logger(): logging.basicConfig(filename='/var/log/my_script/daily_report_%s.log' % datetime.strftime(datetime.now(), '%m%d%Y_%H%M%S'), ➊ level=logging.DEBUG, ➋ format='%(asctime)s %(message)s', ➌ datefmt='%m-%d %H:%M:%S') ➍ def main(): start_logger() logging.debug("SCRIPT: I'm starting to do things!") ➎ try: 20 / 0 except Exception: logging.exception('SCRIPT: We had a problem!') ➏ logging.error('SCRIPT: Issue with division in the main() function') ➐ logging.debug('SCRIPT: About to wrap things up!') if __name__ == '__main__': main()
❶ 使用 logging 模块的 basicConfig 方法,初始化日志系统,这需要一个日志文件名称。这段代码输出日志到 /var/log 文件夹中的文件夹 my_script。文件名是 daily_report_<DATEINFO>.log,其中 <DATEINFO> 是脚本开始执行的时间,包括月、日、年、小时、分钟和秒。这告诉我们何时以及为什么脚本开始运行,同时这是一个好的日志实践。
❷ 设置日志级别。通常,你会想要把级别设置为 DEBUG,这样可以在代码中留下调试信息,在日志中跟踪它们。如果想要更多的信息,你可以使用 INFO 级别,这会展示更多的来自辅助库的日志信息。一些人更喜欢简略的日志,设置日志级别为 WARNING 或 ERROR。
❸ 使用日志的记录属性设置 Python 日志的格式。这里我们记录发送给日志的信息和打印日志的时间。
❹ 设置一个易于阅读的日期格式,这样很容易使用我们喜欢的日期格式解析或搜索日志。这里我们记录了月、日、小时、分钟和秒。
❺ 调用模块的 debug 方法开始记录日志。这个方法需要一个字符串。我们用单词 SCRIPT: 作为脚本日志实体的前缀。在日志中添加类似这样可搜索的标签,会在之后帮助你确定哪个进程或库写了这条日志。
❻ 使用 logging 模块的 exception 方法,输出你发送的字符串和 Python 异常的回溯信息,因此只能够在一个异常代码块中使用。这在调试错误和查看脚本中有多少异常时非常有用。
❼ 使用级别 error 打印一个长一点的错误信息。logging 模块能够打印不同级别的错误信息,包括 debug、error、info 和 warning。日志级别应该与打印的内容一致,为正常信息使用 info 或 debug 级别,用 error 打印脚本中的错误和异常信息。通过这种方式,你永远知道去哪里找问题,以及如何在检查中正确地解析日志。
正像在示例中做的那样,我们发现开始日志信息时,用一个关于正在输出信息的代码模块或区域的标签是很有用处的。这会帮助确定错误发生在哪里。这也使得日志便于搜索和解析,因为你可以清晰地看到脚本碰到了什么错误或问题。学习日志最好的方式是在第一次编写脚本的时候确定在哪里放置信息,并在脚本中保留重要的信息,以确定是否发生了问题以及在哪些节点发生了问题。
即使已经预料到了异常,每一个异常也应该通过日志记录下来。这会帮助你跟踪异常发生的频率,以及你的代码是否应按照正常情况处理它们。logging 模块提供了 exception 和 error 方法,你可以打印异常和 Python 的回溯,并且可以使用 error 添加一些额外的信息,详细描述可能发生了什么,以及哪部分代码触发了这个错误。
你还应该将同数据库、API 和外部系统的交互通过日志记录下来。这会帮助你确定脚本与它们的交互何时出现了问题,确保它们是稳定的、可信赖的或者可以使用的。你与之交互的许多库同样能够根据配置打印日志。例如,requests 模块会将连接问题和请求直接记录到你的脚本日志中。
即使不为脚本创建任何其他的监控或警报,你也应该使用日志。它很简单,并且为未来的你和其他人提供了优秀的文档。日志并不是唯一的解决方案,但是它们是一个好的标准,并且是自动化程序监控的基础。
除了日志之外,你可以为脚本设置易于分析的警报。在下一节中,我们会介绍几种使脚本能够发送有关成功和失败信息的方式。
14.8.2 添加自动化信息
发送报告、跟踪脚本、通知自己错误的一个简单方式是使用邮件或者其他直接从脚本发送的信息。有很多 Python 库能帮助完成这个任务。精确地确定脚本和项目中需要什么类型的信息是一个好的开始。
问问自己下列选项是否适用于你的脚本。
· 它会产生一个报告,并将报告发送到特定的收件人列表。
· 它有一个清晰的成功 / 失败信息。
· 它与其他的合作者或同事相关。
· 它提供了不易在网站或快速指示板上查看的结果。
如果这其中的任何一个听起来像你的项目,你的项目很可能适合某种方式的信息自动化。
01. 邮件
使用 Python 处理邮件非常直观。建议你通过你最喜欢的邮件提供商(我们使用 Gmail),构建一个独立的脚本的 email 地址。如果它没有立即自动同 Python 集成,很可能能够通过在网上搜索发现合适的配置列表和有用的示例配置。
让我们看一下曾用来发送带有附件的邮件到收件人列表的脚本。我们修改了 @dbieber 编写的代码片段(https://gist.github.com/dbieber/5146518),这段代码修改自 Rodrigo Coutinho 的博文“使用 Python 发送 Gmail 邮件”(http://kutuma.blogspot.jp/2007/08/sending-emails-via-gmail-with-python.html):
#!/usr/bin/python # Adapted from # http://kutuma.blogspot.com/2007/08/sending-emails-via-gmail-with-python.html # Modified again from: https://gist.github.com/dbieber/5146518 # config file(s) should contain section 'email' and parameters # 'user' and 'password' import smtplib ➊ from email.MIMEMultipart import MIMEMultipart ➋ from email.MIMEBase import MIMEBase from email.MIMEText import MIMEText from email import Encoders import os import ConfigParser def get_config(env): ➌ config = ConfigParser.ConfigParser() if env == "DEV": config.read(['config/development.cfg']) ➍ elif env == "PROD": config.read(['config/production.cfg']) return config def mail(to, subject, text, attach=None, config=None): ➎ if not config: config = get_config("DEV") ➏ msg = MIMEMultipart() msg['From'] = config.get('email', 'user') ➐ msg['To'] = ", ".join(to) ➑ msg['Subject'] = subject msg.attach(MIMEText(text)) if attach: ➒ part = MIMEBase('application', 'octet-stream') part.set_payload(open(attach, 'rb').read()) ➓ Encoders.encode_base64(part) part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(attach)) msg.attach(part) mailServer = smtplib.SMTP("smtp.gmail.com", 587) mailServer.ehlo() mailServer.starttls() mailServer.ehlo() mailServer.login(config.get('email', 'user'), config.get('email', 'password')) mailServer.sendmail(config.get('email', 'user'), to, msg.as_string()) mailServer.close() def example(): mail(['listof@mydomain.com', 'emails@mydomain.com'], "Automate your life: sending emails", "Why'd the elephant sit on the marshmallow?", attach="my_file.txt")
❶ Python 内置的 smtplib 库(https://docs.python.org/2/library/smtplib.html)提供了一个 SMTP 的封装,SMTP 是发送和接收邮件的标准协议。
❷ Python 的 email 库(https://docs.python.org/2/library/email.html)帮助创建邮件信息和附件,并用合适的形式保存它们。
❸ 函数 get_config 从一系列的本地配置文件中加载配置。我们传递一个环境变量,环境变量可以为字符串 "PROD" 或 "DEV",来表明这是在本地运行("DEV")还是在远程生产环境运行("PROD")的程序。如果只使用一个环境,你可以简单地返回项目中唯一的配置文件。
❹ 这行代码使用 Python 的 ConfigParser 读取 .cfg 文件,返回 config 对象。
❺ 我们的 mail 函数接受一个邮件地址列表,作为变量 to、邮件的主题和文本、一个可选的附件和一个可选的 config 变量。附件需要是本地文件的名称。config 对象需要是 ConfigParser 的实例。
❻ 这行代码设置了默认配置。安全起见,我们使用 "DEV" 配置。
❼ 这行代码使用了 ConfigParser 对象来从配置文件中拉取邮件地址。这个变量安全地保存地址,并与仓库代码分离。
❽ 这段代码拆分邮件列表,并且用逗号和一个空格分离它们。这会展开邮件地址的列表为一个字符串,因为这是我们 MIME 需要的类型。
❾ 如果有附件的话,这行代码按照 MIME 分组标准开始对附件做特殊处理,以发送附件。
❿ 这段代码使用传入的文件名字符串打开并读取完整的文件。
⓫ 如果你没有使用 Gmail,设置这些参数为邮件服务提供商 SMTP 的主机和端口。如果有好的文档的话,很容易找到这些参数。如果没有的话,直接搜索“SMTP 设置 < 你的邮件服务提供商名称 >”应该会得到答案。
⓬ 这是一些示例代码,提示你 mail 函数所需的参数。你可以看到需要的数据类型(字符串、列表、文件名)和顺序。
简单的 Python 内置库 smtplib 和 email 使用它们的类和方法帮助我们快速地创建和发送邮件信息。将脚本中的一些其他部分(例如在配置文件中保存邮件地址和密码)抽象出来是保证脚本和仓库安全可复用所必需的。一些默认的设置确保脚本永远可以发送邮件。
02. 短消息服务(SMS)和语音
如果想要集成电话信息到警报程序中,你可以使用 Python 来发送文本信息,或者打电话。Twilio(https://www.twilio.com)是一个经济有效的选择,支持多媒体信息和自动拨打电话。
在开始使用 Twilio API 之前,你需要注册得到认证代码和 key,并且安装 Twilio 的 Python 客户端(https://github.com/twilio/twilio-python)。在 Python 客户端的文档(https://twilio-python.readthedocs.org/en/latest/)中有一个很长的代码示例列表,所以如果你需要做一些有关语音或者文本的事情,很可能这里有一个好的可用的特性。
看一下发送一个快速的文本信息是多么简单。
from twilio.rest import TwilioRestClient ➊ import ConfigParser def send_text(sender, recipient, text_message, config=None): ➋ if not config: config = ConfigParser('config/development.cfg') client = TwilioRestClient(config.get('twilio', 'account_sid'), config.get('twilio', 'auth_token')) ➌ sms = client.sms.messages.create(body=text_message, to=recipient, from_=sender) ➍ def example(): send_text("+11008675309", "+11088675309", "JENNY!!!!") ➎
❶ 我们会使用 Twilio Python 客户端通过 Python 直接同 Twilio API 交互。
❷ 这行代码定义了一个可以用来发送文本的函数。我们需要发送者和接收者的电话号码(以国家代码开头),以及想要发送的简单文本信息,我们还能传递一个配置对象。我们会使用配置来同 Twilio API 进行认证。
❸ 这段代码创建了一个客户端对象,它会使用我们的 Twilio 账户认证。当你注册 Twilio 账户时,会收到一个 account_sid 和一个 auth_token。将它们放在脚本使用的配置文件中名为 twilio 的小节中。
❹ 为了发送一条信息,这段代码使用了客户端的 SMS 模块,并且调用了 message 资源的 create 方法。正像 Twilio 文档中写的(https://twilio-python.readthedocs.io/en/latest/usage/messages.html#sending-a-text-message),之后可以通过几个参数发送一条文本信息。
❺ Twilio 在全世界范围工作,并且需要基于国际的电话号码。如果你不确定要使用什么国际电话代码,维基有一个很棒的列表(https://en.wikipedia.org/wiki/List_of_country_calling_codes)。
如果你对让脚本通过 Python“交谈”感兴趣,Python 的 text-to-speech 模块(https://pyttsx.readthedocs.org/en/latest/)可以在电话上“朗读”你的文字。
03. 集成聊天
如果你想要集成聊天室到警报系统中,或者你的团队或合作者普遍使用聊天室,有很多 Python 聊天室工具可用。根据聊天室客户端和需要,可以选择 Python 或者基于 API 的解决方案,你可以使用关于 REST 客户端的知识,连接并向正确的人发送消息。
如果你使用 HipChat,他们的 API(https://www.hipchat.com/docs/apiv2)很容易同 Python 应用程序或脚本集成。这里有各种各样的 Python 库(https://www.hipchat.com/docs/apiv2/libraries),可以简单地发送信息到聊天室或直接发送给个人。
为了开始使用 HipChat API,你需要首先登录(https://hipchat.com/account/api)并获取一个 API token。你可以使用 Python 库 HypChat(https://github.com/RidersDiscountCom/HypChat),发送一个快速信息到聊天室。
首先,使用 pip 安装 HypChat:
pip install hypchat
现在,使用 Python 发送一条信息!
from hypchat import HypChat from utils import get_config def get_client(config): client = HypChat(config.get('hipchat', 'token')) ➊ return client def message_room(client, room_name, message): try: room = client.get_room(room_name) ➋ room.message(message) ➌ except Exception as e: print e ➍ def main(): config = get_config('DEV') ➎ client = get_client(config) message_room(client, 'My Favorite Room', "I'M A ROBOT!")
❶ 使用 HypChat 库同聊天室客户端交谈。这个库使用 HipChat token 初始化一个新的客户端,我们将 token 保存在配置文件中。
❷ 这行代码使用 get_room 方法,选择与字符串名称匹配的房间。
❸ 这行代码使用 message 方法发送一条信息到一个房间或者一个用户,传递给函数一个简单的字符串,里面包含想说的内容。
❹ 总是在基于 API 库的周围使用 try...except 代码块,以应对连接错误或者 API 改变带来的异常。这段代码打印了错误,但是你可能更想打印日志,以充分地自动化脚本。
❺ 这里使用的函数 get_config 是从不同的脚本中导入的。通过引入这些辅助函数,并且将它们放置到独立的模块中复用,我们遵循了模块化的代码设计。
如果想要登录到聊天室,你可以使用 HipLogging(https://github.com/invernizzi/hiplogging)探索这些选项。取决于需求和团队的工作方式,你可以根据自己的喜好设置聊天室的登录功能;但是好消息是,你总是可以在他们可能看见的地方留下笔记!
如果你更喜欢 Google Chat,有很多非常棒的示例展示了如何使用 SleekXMPP(http://sleekxmpp.com/getting_started/sendlogout.html)做这件事。你还可以使用 SleekXMPP 发送 Facebook 聊天信息(http://stackoverflow.com/questions/18906462/send-facebook-messages-via-sleekxmpp/21452035#21452035)。
关于 Slack 信息,查看 Slack 团队的 Python 客户端(https://github.com/slackhq/python-slackclient)。
对于其他的聊天客户端,建议你使用 Google 搜索“Python <你的客户端名称 >”。很有可能有人已经尝试使用他们的 Python 代码同这个客户端连接,又或者有 API 可供你使用。在第 13 章中,你知道了如何在工作中使用一个 API。
关于脚本(和自动化)成功或失败的警报的选择有这么多,很难知道应使用哪一个。重要的是选择一个你或你的团队经常使用的方式。优先考虑易用性和与日常生活的整合是必需的,自动化会帮助你节省时间,不让你花费更多的时间来检查服务。
14.8.3 上传和其他报告
如果你需要上传报告或图片到独立的服务或文件分享作为自动化的一部分,有很多很棒的工具。如果这是一种在线的形式,或者一个需要与之交互的网站,建议使用第 12 章中的 Selenium 爬取技巧。如果这是一个 FTP 服务器,Python 有一个标准 FTP 库(https://docs.python.org/2/library/ftplib.html)。如果需要发送报告给一个 API,或者通过 Web 协议发送,你可以使用 requests 库,或者在第 13 章中学到的 API 技巧。如果需要发送 XML,你可以使用 LXML(查看第 11 章)。
无论准备同哪些服务交互,你很有可能与服务有过接触和交流。我们希望你自信地练习这些技能,并且独立思考。
14.8.4 日志和监控服务
如果一个脚本满足不了需求,或者你想将自动化程序合并到一个大型的组织框架中,你可能需要研究将日志和监控作为一个服务的技术。有很多公司通过创建工具和系统来跟踪日志,致力于让数据分析师和开发者的生活更简单。这些工具通常有非常简单的 Python 库,将日志和监控信息发送到它们的平台。
通过日志服务,你可以将更多的时间投入到研究和脚本中,将更少的时间用于管理监控和日志。这可以将“我们的脚本在不在工作,工作得怎么样?”这样的问题留给团队中的非开发者,因为其中的很多服务都有很棒的报表和内置的警报。
根据自动化程序的大小和布局,你可能同时需要系统监控以及脚本与错误监控。在这一节中,我们会查看几种能够同时完成这两件事的服务,同时也会查看一些更加专业的服务。即使你的自动化程序的规模没有大到需要它们,但是了解什么是可用的总是好的。
01. 日志和异常
基于 Python 的日志服务提供了打印日志到一个中心服务的能力,同时让你的脚本在一系列不同的机器上运行,无论是本地机器还是远程机器。
一个有着很棒的 Python 支持的类似服务是 Sentry(https://getsentry.com/welcome/)。只 需要每月支付很少的费用,你就可以访问错误的报告板,收到基于异常临界值发送的警报,监控基于日、周、月的错误和异常类型。Sentry 的 Python 客户端(https://github.com/getsentry/raven-python)非常容易安装、配置和使用。如果你正在使用类似 Django、Celery 的工具,甚至是简单的 Python 日志工具(https://docs.sentry.io/clients/python/),Sentry 都有相应的集成接口,所以你不需要大量地修改代码来开始工作。最重要的是,基础代码持续地更新,工作人员非常友善,在你有问题的时候乐于帮助你。
其他选择包括 Airbrake(https://airbrake.io/languages/python_bug_tracker)和 Rollbar(https://rollbar.com)。Airbrake 最开始是一个基于 Ruby 的异常跟踪器,现在开始支持 Python。这是一个很受欢迎的市场,所以在本书开始印刷时很可能会有新的库发布。
还有拉取和解析日志的服务,例如 Loggly(https://www.loggly.com/)和 Logstash(https://www.elastic.co/products/logstash)。这些服务允许你在聚合的日志级别上监控它们,同样也可以解析、搜索以及在日志中寻找问题。这些工具只在你有足够的日志和时间检查它们时才有用,但是对于有着大量日志的分布式系统非常有用处。
02. 日志和监控
如果你有分布式机器或者正在集成脚本到公司或大学的基于 Python 的服务器环境,你可能想要更健壮的监控,不仅仅是针对 Python,而是整个系统。有很多服务提供了系统数据库负载监控、Web 应用程序的监控,同样也有自动化任务的监控。
New Relic(http://newrelic.com/)是这方面最流行的服务之一,它可以监控服务器和系统进程,也可以监控 Web 应用程序。使用 MongoDB 和 AWS ?或者 MySQL 和 Apache ? New Relic 插件(http://newrelic.com/plugins)让你可以轻松地将服务的日志集成到用于监控服务器和应用健康的报告板中。除此之外,它们提供了一个 Python 代理(https://docs.newrelic.com/docs/agents/python-agent/getting-started/introduction-new-relic-python),通过它你可以很容易地为 Python 应用(或脚本)在相同的生态系统中打印日志。在整合所有的监控工具之后,发现问题和创建合适的警报系统会变得更简单,这样团队中的相关人员会及时了解任何发生的问题。
另外一个系统和应用监控服务是 Datadog(https://www.datadoghq.com/)。Datadog 允许你整合许多服务(https://www.datadoghq.com/product/integrations/)到一个报告板中。这会节省时间和精力,让你轻松地发现项目、应用和脚本中的错误。Datadog 的 Python 客户端(https://github.com/DataDog/datadogpy)能够打印你想监控的不同事件的日志,但是需要一些个性化设置。
无论你使用什么监控器,也无论你是决定构建自己的监控器还是使用一个服务,拥有规律的警报、深入了解你使用的服务、理解代码和自动化系统的完整性是非常重要的。
当依赖自动化程序完成工作和项目的其他部分时,你应该确保监控系统容易使用并且直观,这样你可以专注于项目中更重要的部分,不用承担疏忽错误或其他问题的风险。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论