AutoIt 秘籍(二)

原文:zh.annas-archive.org/md5/8f8a70ed95a9c4e2d339b67f1fd45d15

译者:飞龙

协议:CC BY-NC-SA 4.0

第五章。电子邮件的乐趣

在过去几十年中,电子邮件已成为信息交换的主要模式。您每天都会处理电子邮件,出于多种原因。但您是否曾想过可以用 Python 操作您的收件箱?

在本章中,我们将介绍以下食谱:

  • 发送电子邮件消息

  • 电子邮件加密

  • 使用 MIME 消息美化电子邮件消息

  • 带附件的电子邮件消息

  • 连接到您的收件箱

  • 获取和阅读电子邮件消息

  • 标记电子邮件消息

  • 清除收件箱中的电子邮件消息

  • 使用电子邮件响应自动化客户支持流程

简介

嗨,朋友们!希望你们今天过得愉快。在本章中,我们将讨论电子邮件以及我们可以使用 Python 实现的众多电子邮件操作。我们还将通过实际业务用例了解如何利用电子邮件自动化业务流程。

那么,我们还在等什么呢?让我们开始,了解一下电子邮件的历史及其技术实现。

实际上,电子邮件无需介绍;当然,它们是用户之间交换数字消息的方法。电子邮件在互联网上的计算机网络上进行信息交换。您可以登录您喜欢的电子邮件客户端,开始处理存储在电子邮件服务器上的消息。最广泛使用的网络客户端是 Gmail。

电子邮件有着非常有趣的历史。在过去,电子邮件需要发送者和接收者同时在线才能成功进行通信。这难道没有意义吗?随着时间的推移,电子邮件服务器变得智能,开始采用存储和转发哲学。今天,电子邮件消息异步存储在服务器上,以便接收者可以在方便的时候查看。因此,电子邮件服务器能够提供接受、转发和标记消息等服务。

电子邮件最初只使用 ASCII 字符,后来通过多用途互联网邮件扩展MIME)扩展为支持富文本和附件。从协议的角度来看,电子邮件最初使用文件传输协议FTP)在计算机之间发送消息,但如您所知,简单邮件传输协议SMTP)是目前最广泛使用的电子邮件处理协议。

备注

请注意,本书不涵盖电子邮件服务器的设置。如果你在网上搜索,你会找到更多可以帮助你入门的资源。本章的目的是让你了解使用 Python 程序可以做什么。我们使用 Gmail 网络客户端作为例子,这样你可以快速尝试代码示例,并欣赏使用 Python 自动化电子邮件任务而不必设置自己的电子邮件服务器的强大功能。虽然我们以 Gmail 为例,但这些代码片段也可以适用于任何其他支持 SMTP 发送电子邮件和 IMAP 检索电子邮件的电子邮件服务器。

在本章中,我们将学习如何使用 Python 处理电子邮件。我们还将使用以下列出的多个 Python 模块来对电子邮件消息执行各种操作:

注意

当涉及到使用 Python 处理电子邮件时,你需要的是一个帮助你构建消息的模块,一个可以发送电子邮件的模块,以及一个帮助你检索和更新消息的模块。

发送电子邮件消息

你可能首先想要通过电子邮件客户端实现的是向你的朋友或同事的电子邮件地址发送消息。让我们继续看看在 Python 中我们如何实现这一点。

准备工作

为了发送电子邮件,我们需要拥有 Python 的smtplib模块。正如其名所示,这个库使用 SMTP 协议来发送电子邮件。我们可以使用我们喜欢的pip工具通过以下命令安装smtplib。但 Python 的默认安装应该已经包含了这个模块:

pip install smtplib

如何操作…

  1. 在你的电脑上,打开你最喜欢的编辑器并添加以下代码片段。让我们称这个为config.py。配置文件包含登录详细信息,如电子邮件地址、密码以及需要发送电子邮件的电子邮件地址:

            fromaddr = "abc@gmail.com"
            password = "xyz@123"
            toaddr = "abc@gmail.com"
    
  2. 现在,让我们编写代码来使用这个配置文件发送电子邮件:

            import smtplib
            import config server = smtplib.SMTP('smtp.gmail.com', 587)
            server.starttls()
            server.login(config.fromaddr, config.password)
            msg = "Some nice msg"
            server.sendmail(config.fromaddr, config.toaddr, msg) 
            server.quit()
    
  3. 将前面的代码保存为basic_email.py,并使用以下命令运行代码:

     python basic_email.py 
    
    
  4. 如果你运行前面的代码,你会看到带有SMTPAuthenticationError的异常,并且你的程序将因为退出代码1而失败。你的异常将如下所示:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_06_001.jpg

  5. 好吧,这很糟糕,但相反,这也很好!异常表明服务器登录是正常的,但 Gmail 阻止了您发送消息。现在,如果您登录 Gmail,您应该会看到一封电子邮件建议检测到来自不安全应用的注册。真的吗?!是的,这是因为我们尝试从我们的 Python 程序访问 Gmail 账户。这就是为什么我们收到了来自 Google 的电子邮件,建议如果我们的账户有恶意活动,可能存在安全漏洞。Google 的电子邮件消息可以在以下截图中查看:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_06_002.jpg

  6. 但显然,这是一个合法尝试使用 Gmail 账户,所以让我们向 Google 确认这一点。打开来自 Google 的电子邮件消息,点击允许访问。您将被带到不安全应用页面,在那里您可以开启此设置,如下面的截图所示:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_06_003.jpg

  7. 现在,请从 Gmail 网页客户端登出并再次登录,以便让设置对您的账户生效。如果一切顺利,您将收到来自 Google 的电子邮件,告知不安全应用的访问已开启。Google 的确认电子邮件将类似于以下截图:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_06_004.jpg

    现在,如果您再次运行 Python 程序,它应该会成功运行,并且您将在收件箱中收到一封电子邮件:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_06_005.jpg

  8. 太棒了!注意,消息内容与我们添加到代码片段中的内容相同。此外,由于发件人收件人地址相同,邮件是从您那里发出的,但它没有任何主题,这并不理想。我们将在下一个菜谱中解决这个问题。

它是如何工作的…

如前所述,SMTP 用于发送电子邮件消息。我们使用 Python 模块smtplib来完成这个目的。

如果您查看前面的代码片段,我们使用构造函数smtplib.SMTP()来配置 Gmail 的 SMTP 设置并获取对电子邮件服务器的访问权限。Gmail 的 SMTP 服务器运行在smtp.gmail.com上,端口为 587。

一旦我们有了服务器对象server,我们就使用它来用我们的用户名和密码登录 Gmail。请注意,在前面代码中我们还有另一行:server.starttls();我们将在本章后面讨论这个问题。

我们创建了一个测试消息并将其存储在变量msg中,然后使用sendmail方法('fromaddr', 'toddr', msg)发送。

最后,我们使用server.quit()关闭与电子邮件服务器的连接。

还有更多…

我们探讨了如何使用 SMTP 协议和 Python 的smptlib库登录 Gmail 并发送基本电子邮件。虽然这个菜谱让我们开始了,但还有一些细节我们将在下一个菜谱中深入探讨。让我们来看看它们。

电子邮件加密

电子邮件容易泄露信息。目前大多数电子邮件都是以明文格式传输的。电子邮件加密涉及加密或伪装电子邮件的内容,以便只有预期的收件人可以阅读。始终记住,在处理电子邮件时,安全性是最重要的。让我们看看如何使用 Python 加密电子邮件。

准备工作

在之前的菜谱中,我们看到了如何发送基本的电子邮件,但starttls()方法是什么?电子邮件加密是如何工作的?我们将在这部分得到这些问题的答案。

如何做…

  1. 让我们先打开我们最喜欢的编辑器,并输入以下代码片段:

            import smtplib
            server = smtplib.SMTP('smtp.gmail.com', 587)
            try:
                server.set_debuglevel(True)
                print "Sending ehlo"
                server.ehlo()
                if server.has_extn('STARTTLS'):
                   print "Starting TLS Session"
                   server.starttls()
                   print "Sending ehlo again"
                   server.ehlo()
            finally:
                server.quit()
    
  2. 现在,让我们运行 Python 代码并查看它打印的内容。我们有三个不同的输出段。第一个是我们向电子邮件服务器发送ehlo()消息时:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_06_006.jpg

  3. 第二种情况是我们对服务器对象调用starttls()方法。查看以下截图:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_06_007.jpg

  4. 第三个是我们再次使用ehlo()连接到电子邮件服务器时:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_06_008.jpg

它是如何工作的…

让我们从基础知识开始。电子邮件加密意味着保护电子邮件消息不被除预期当事人之外的其他人阅读。电子邮件通常以明文发送,可以被第三方嗅探。为了避免这种情况,我们在协议层加密电子邮件;这可能包括身份验证。

SMTP 服务器通常使用 SSL/TLS 协议在端口 25 上发送电子邮件。然而,随着 STARTTLS(位于 SMTP 之上的一层)的出现以及使用端口 587 进行消息提交,像 Gmail 这样的电子邮件客户端使用 STARTTLS 和端口 587 来发送电子邮件。Gmail 还实现了身份验证;记住我们使用server.login (username, password)登录到 Gmail 服务器。

为了让 STARTTLS 在服务器和客户端之间使用,客户端首先需要知道服务器是否支持此协议。当我们发出server.ehlo()时,程序会向 SMTP 服务器发送一个EHLO消息以建立通信。服务器会以消息和允许的扩展响应,如第一张截图所示。

现在,从代码中,我们使用server.has_extn('STARTTLS')检查服务器是否支持STARTTLS扩展。正如我们在第一张截图中所见,SMTP 服务器响应了STARTTLS扩展;这证实了 Gmail 支持STARTTLS协议层,这真是太棒了。

现在,我们使用server.starttls()与服务器通信。服务器通过发送消息Ready to start TLS来响应。这样,我们就加密了我们的会话。如果你现在查看第三张截图,当我们发送server.ehlo()时,我们通过 TLS 会话重新向服务器确认身份。这也表明服务器实现了身份验证扩展。

最后,我们使用 server.quit() 退出 SMTP 会话,服务器响应为 closing connection,如第三张截图所示。

还有更多…

好吧,这相当详细。花点时间理解它。实际上,了解简单电子邮件发送背后的过程很有趣。但不用担心太多;让我们开始有趣的部分,并探索更多示例。

使用 MIME 美化电子邮件消息

在前几个菜谱中,我们以简单的纯文本格式发送了电子邮件消息。MIME 互联网标准帮助我们构建包含非 ASCII 字符、multipart 消息和图像的消息。它还帮助处理附件和其他许多任务。这样,我们可以构建丰富的电子邮件消息。让我们看看 MIME 格式在这个菜谱中的应用。

准备工作

对于这个菜谱,我们将使用相同的模块 smtplib 来发送电子邮件消息。我们还将介绍另一个模块 email,它将帮助我们使用 MIME 格式构建更好的电子邮件消息。email 模块随 Python 安装而来;因此,我们不需要进行任何新的模块或安装。在本节中,我们将探讨如何使用 MIME 属性发送外观更好的电子邮件。

如何做到这一点…

  1. 让我们先导入我们需要的所有模块:

            import smtplib
            from email.mime.multipart import MIMEMultipart
            from email.mime.text import MIMEText
            import config
            import email.utils
    
  2. 现在,让我们使用 MIME 模块构建我们的电子邮件消息。以下代码创建了消息:

            fromaddr = config.fromaddr
            toaddr = config.toaddr
            msg = MIMEMultipart()
            msg['Subject'] = "Hello from the Author of Automate It!"
            msg['To'] = email.utils.formataddr(('Recipient', toaddr))
            msg['From'] = email.utils.formataddr(('Author',
            fromaddr))
            body = "What a wonderful world!"
            msgBody = MIMEText(body, 'plain')
            msg.attach(msgBody)
    
  3. 因此,现在我们有了要发送电子邮件消息的收件人详细信息。我们已使用 MIME 格式构建了电子邮件消息。我们还在等什么呢?让我们使用以下代码发送它:

            server = smtplib.SMTP('smtp.gmail.com', 587)
            server.starttls()
            server.login(fromaddr, config.password)
            text = msg.as_string()
            print "Text is:", text
            server.sendmail(fromaddr, toaddr, text)
            server.quit()
    

    收到的电子邮件如下所示:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/B05370_07_09.jpg

太棒了!太好了,但是…它是怎么工作的?

工作原理…

在前面的例子中,我们导入配置文件,从中我们获得了 fromaddresspassword 以登录 SMTP 服务器,以及 toaddress 以发送电子邮件消息。

现在,在发送消息之前,我们使用 email.mime.multipart 模块中的 MIMEMultipart() 类构建一个新的 MIME 消息对象。对于那些不知道的人来说,MIME multipart 消息意味着在单个电子邮件中既有 HTML 内容又有文本内容。因此,在这段代码中,我们创建了一个新的 multipart MIME 消息,并将其文本内容添加到其中。

文本内容,即电子邮件的正文,是通过 email.mime.text 模块中的 MIMEText() 构造函数创建的,然后使用 attach() 方法将其附加到 multipart 消息中。

构建的 MIME 消息如下截图所示,其中内容类型为 multipart,MIME 版本为 1.0,主题收件人发件人的详细信息符合预期,电子邮件正文包含预期的文本:

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/B05370_07_10.jpg

一旦我们有了消息和收件人详细信息,我们就可以像往常一样使用 SMTP.sendmail() 方法发送电子邮件。

带有附件的电子邮件消息

电子邮件中最常用且简单的用例之一是能够向你的电子邮件消息中添加附件。在本节中,我们将学习如何在 Python 中给我们的电子邮件添加附件。

准备工作

我们在这个例子中使用了相同的 smtplibemail 模块。所以,不必担心要安装的模块。让我们继续进行。

如何操作…

  1. 让我们先快速创建一个小的文本文件。我们将称之为 attach.txt,其内容如下截图所示:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_06_011.jpg

  2. 让我们看看将附件添加到我们的电子邮件中的代码:

            import smtplib
            from email.mime.multipart import MIMEMultipart
            from email.mime.text import MIMEText
            from email.mime.base import MIMEBase
            from email import encoders
            import config
    
            fromaddr = config.fromaddr
            toaddr = config.toaddr
    
            msg = MIMEMultipart()
    
            msg['From'] = fromaddr
            msg['To'] = toaddr
            msg['Subject'] = "Email with an attachment"
    
            body = "Click to open the attachment"
    
            msg.attach(MIMEText(body, 'plain'))
    
            filename = "attach.txt"
            attachment = open(filename, "rb")
    
            part = MIMEBase('application', 'octet-stream') 
            part.set_payload((attachment).read())
            encoders.encode_base64(part)
            part.add_header('Content-Disposition', "attachment;
                            filename= %s" % filename)
            msg.attach(part)
            server = smtplib.SMTP('smtp.gmail.com', 587)
            server.starttls()
            server.login(fromaddr, config.password)
            text = msg.as_string()
            server.sendmail(fromaddr, toaddr, text)
            server.quit()
    
  3. 当你运行前面的代码时,你将在你的收件箱中收到电子邮件,其外观类似于以下截图:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_06_012.jpg

工作原理…

我们已经熟悉了创建 MIME 消息对象。因此,在这段代码中,我们创建了一个多部分消息对象 msg。然后我们使用 msg.attach() 向其中添加一条文本消息。文本正文说“点击打开附件”。

在这个食谱中,我们使用了来自 email 模块的新类 MIMEBase,它将被用来附加文本文件。记住,我们已经创建了 attach.txt 文件,我们使用 Python 的 open() 方法打开文件,并获取文件句柄 attachment。然后我们创建一个 MIMEBase 对象 part,并将文件的 内容作为有效载荷分配给此对象。文件内容通过 attachment.read() 获取,有效载荷通过 set_payload() 方法设置。

为了附加文件,MIMEBase 对象必须被编码为 base64,并且需要将 Content-Disposition 标头添加到 part 对象中。现在我们有了 part 对象,它可以像我们为正文文本所做的那样,使用 attach() 方法附加到多部分对象 msg 上。

很酷,所以我们已经有了完整的 MIME 消息以及发送消息给谁的具体信息。那么,我们就继续发送带有附件的电子邮件。这正是我们想要达成的目标。

连接到你的收件箱

在本章中,我们一直在谈论使用 Python 发送电子邮件。然而,在某些时候,你也可能想要扫描你的收件箱并阅读收到的消息。那么,你该如何做呢?让我们在本食谱中理解和学习它。

准备工作

在这个食谱中,我们使用了一个新的 Python 模块,它将帮助我们从收件箱中检索消息。我们使用的是 Python 模块 imaplib,它是默认 Python 安装的一部分。酷,那么让我们开始吧。

如何操作…

  1. 我们首先使用配置文件,这是我们之前创建的用于存储我们的电子邮件和密码的文件,来登录服务器。然后我们添加我们的代码来创建一个处理或对象来处理我们的收件箱。代码如下所示:

             import config, imaplib
             M = imaplib.IMAP4_SSL("imap.gmail.com", 993)
             M.login(config.fromaddr, config.password)
             print "Inbox:", M
             M.logout()
    

    如果你运行前面的代码片段,你将得到以下输出:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_06_013.jpg

  2. 默认情况下,当我们登录到 Gmail 时,默认收件箱会被选中,但如果我们还创建了其他收件箱,我们可以通过添加一小段代码来获取列表。现在,从所有标签中,如果我们特别想选择 Inbox,这也可以实现。请看以下代码示例:

            import config, imaplib
            M = imaplib.IMAP4_SSL("imap.gmail.com", 993)
            M.login(config.fromaddr, config.password)
            print M.list()
            M.select('INBOX')
            print "Inbox:", M
            M.logout()
    

    上述代码片段的输出显示在以下屏幕截图中。尽管我已经创建了多个标签,但我使用较少标签的屏幕截图:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_06_014.jpg

它是如何工作的…

如本章开头所述,我们有三项主要协议用于处理电子邮件。我们大量使用 SMTP 发送电子邮件,但在阅读电子邮件时,我们可以使用 POP 或 IMAP 从电子邮件服务器检索消息。我们将逐步分析代码。

Python 的 imaplib 库帮助我们使用 互联网消息访问协议IMAP)连接到我们的邮箱。Gmail 服务器配置为 IMAP,服务器运行在 imap.gmail.com 上,端口为 ‘993’。

在我们的代码示例中,我们使用构造函数 IMAP4_SSL("imap.gmail.com", 993) 创建了一个类型为 imaplib 的对象;我们称这个对象为 M

关于加密,我们使用 IMAP4_SSL 连接到服务器,因为它在 SSL 套接字上使用加密通信。我们避免使用 IMAP4 类,它内部使用明文套接字。

现在,使用对象 M,我们可以使用我们的用户名和密码登录到 Gmail 并连接到我们的收件箱。

当我们在对象 M 上调用 list() 方法时,它会返回您已经创建的所有标签。现在,在我的情况下,我已经创建了 ACM 标签(用于我的与 ACM 的工作),因此它出现在我的标签列表中。

如果你看代码示例,我们可以使用 select() 方法明确连接到 INBOX。一旦连接到收件箱,我们就可以开始从收件箱获取电子邮件消息。

最后,我们使用 M.logout() 方法关闭与收件箱的连接。酷!这很简单。

还有更多…

因此,我们在这个食谱中学习了如何连接到我们的收件箱,但我们可能还想阅读消息、标记它们并在它们上执行有趣的操作。让我们看看如何在下一个食谱中执行消息操作。

获取和阅读电子邮件消息

使用 imaplib 获取电子邮件消息也很容易实现。在本食谱中,我们将学习如何使用 Python 代码来完成这项操作。在本食谱中,我们将搜索具有特定主题行的电子邮件,并从符合预定义标准的收件箱中获取最新消息。

准备工作

我们继续使用 imaplib 模块来读取电子邮件消息,因此不需要为本食谱进行新安装。

如何做…

  1. 我们利用配置文件并导入 fromaddresspasswordtoaddress 来登录到服务器。一旦登录,我们选择默认收件箱,获取电子邮件消息并阅读它们。让我们看看完整的代码:

            import config, imaplib
            M = imaplib.IMAP4_SSL("imap.gmail.com", 993) 
            M.login(config.fromaddr, config.password)
            M.select("INBOX")
            typ, data = M.search(None, 'SUBJECT', 
                                 "Email with an attachment")
            typs, msg = M.fetch(data[0].split()[-1], '(RFC822)')
            print "Message is ", msg[0][1]
            M.close()
            M.logout()
    
  2. 将前面的文件保存为 inbox_search.py 并使用以下命令运行代码:

     python inbox_search.py 
    
    
  3. 前面代码片段的输出显示在以下截图:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_06_015.jpg

如何工作…

在前面的代码片段中,我们首先使用适当的 IMAP 设置创建了一个 IMAP_SSL4() 对象。然后,我们借助 IMAP 对象,使用配置文件中的凭据登录到客户端。接着,我们选择我们的 INBOX 以便在其上执行搜索操作。

在 IMAP 对象上调用 M.search() 方法可以帮助我们搜索主题为 带附件的电子邮件 的电子邮件。search() 方法返回一个与搜索标准匹配的消息数组。

现在,如果我们需要获取特定的消息,并且由于我们必须读取符合我们标准的最新的电子邮件,我们使用 M.fetch() 方法。fetch() 方法需要一个给定的消息对象和您想要获取的消息部分。因此,在这个代码示例中,我们传递符合标准的最新电子邮件对象,并传递 RFC822,这表示我们想要以 RFC 822 格式获取电子邮件正文。

当我们打印从 fetch() 获取的消息时,我们得到与搜索匹配的最新电子邮件的电子邮件正文内容。

现在,你还记得看到电子邮件的内容吗?嗯,这正是我们在之前的菜谱中发送的同一封电子邮件,当时我们用它来演示电子邮件附件。

还有更多…

好的!所以,现在我们可以搜索一条消息并获取它。在我们的收件箱中还有许多更细粒度的操作,比如标记我们想要执行的操作。让我们在下一个菜谱中看看它们。

标记电子邮件消息

在之前的菜谱中,我们看了获取和读取消息。这不是太复杂了吗?在进行像搜索或读取这样的简单操作时,我们必须注意这么多细节吗?在本节中,我们将看看另一个可以帮助我们不仅更好地搜索或读取,还可以在我们的电子邮件上执行各种操作的库。

准备中

对于本节,我们将安装 gmail 模块。您可以通过终端使用 pip 命令安装此模块,如下所示:

pip install gmail 

让我们看看如何使用 gmail API 搜索和读取电子邮件。这将使我们开始使用 gmail 模块。以下代码片段搜索在 2016 年 7 月 22 日之后收到的电子邮件。然后我们取最新的消息并获取它。一旦我们获取了消息,我们就可以继续读取电子邮件的正文:

import gmail, config
from datetime import date
g = gmail.login(config.fromaddr, config.password)
mails = g.inbox().mail(after=date(2016, 7, 22))
mails[-1].fetch()
print "Email Body:\n", mails[-1].body
g.logout()

前面代码的输出显示在以下截图。看起来我可能收到了来自 Quora 的电子邮件摘要!

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_06_016.jpg

这里是我的收件箱的截图:

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/Chapter5-Page-21.jpg

这不是太简单了吗?顺便说一下,gmail模块是在imaplib之上编写的,但提供了更好的 API,所以让我们利用这个模块做一些惊人的操作。

如何操作…

  1. 让我们打开收件箱,寻找符合特定标准的未读邮件,并将其标记为已读。以下代码可以轻松完成此操作:

            import gmail, config
            g = gmail.login(config.fromaddr, config.password)
            mails = g.inbox().mail(unread=True,
            sender='noreply@glassdoor.com')
            mails[-1].fetch()
            mails[-1].read()
            g.logout()
    

    在运行此程序之前,我在收件箱中有一封来自glassdoor.com的未读电子邮件。在我的收件箱中它看起来是这样的:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_06_017.jpg

    运行代码片段后,它将这封电子邮件识别为符合来自noreply@glassdoor.com的未读邮件标准,并将我的邮件标记为已读。所以现在在我的收件箱中看起来是这样的。Gmail 会取消粗体显示已读邮件,这就是我在收件箱中看到的情况:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_06_018.jpg

    很好!

  2. 让我们看看另一个例子。从 2016 年 1 月开始,我收到了来自 Amazon Now 的大量促销电子邮件。我的邮箱看起来是这样的:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_06_019.jpg

  3. 现在,我想将它们全部标记为read,并将它们分配到单个标签AMAZON下。我该如何操作?以下代码执行了这个操作:

            import gmail, config
            from datetime import date
            g = gmail.login(config.fromaddr, config.password)
            mails = g.inbox().mail(unread=True, 
                                   sender='store-news@amazon.in',
                                   after=date(2016, 01, 01))
           for email in mails:
               email.read()
               email.add_label("AMAZON")
               g.logout()
    
  4. 运行此代码后,您的收件箱中会出现一个新的标签,名为AMAZON。现在,如果您在收件箱中搜索所有带有标签AMAZON的电子邮件,您会看到所有这些电子邮件都已标记为已读。请看以下截图,我正在搜索带有标签AMAZON的电子邮件:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_06_020.jpg

工作原理…

在第一步中,我们通过登录 Gmail 服务器创建了一个名为g的对象。请注意,我们没有传递任何参数(如 IMAP 设置或端口)来创建对象。gmail模块内部处理这些。

现在,使用这个对象,我们开始搜索收件箱中未读且由noreply@glassdoor.in发送的电子邮件,所有符合这个标准的邮件对象都存储在mails中。

之后,我们使用fetch()方法获取最新记录,并使用read()方法将此邮件标记为已读。

同样,在第二个菜谱中,我们遍历所有未读的电子邮件,这些电子邮件是由store-news@amazon.in发送的,并且是在今年发送给我的。

每封邮件随后都会使用read()方法标记为已读,并添加到标签AMAZON中。操作起来就像微风一样,太棒了!

还有更多…

我们查看了一些可以在我们的电子邮件上执行的操作。还有很多。使用gmail模块,您可以标记邮件为未读,甚至用星号标记它们为重要。让我们看看一个可以清理我们收件箱的例子。

清除收件箱中的电子邮件

最后但同样重要的是,这个菜谱将向您展示如何从收件箱中删除电子邮件。正如您所期望的,通过编程删除电子邮件非常直接。

注意

即使你从电子邮件客户端删除了消息,电子邮件服务器仍然可以选择存储它们。所以,当你删除你的消息时,你只是在标记它们以隐藏在你的收件箱中,而它们可以根据服务器实现继续留在你的电子邮件服务器上。

准备工作

我们继续使用 imaplib 模块来删除电子邮件消息,因此不需要为这个配方安装任何新软件。

如何操作…

  1. 让我们利用配置文件并导入 fromaddresspasswordtoaddress 来登录到服务器。

  2. 这就是完整的代码看起来像什么:

            import gmail, config
            g = gmail.login(config.fromaddr, config.password)
            emails = g.inbox().mail(sender='junk@xyz.com')
            if emails:
               for mail in emails:
                   mail.delete()
    
  3. 将前面的文件保存为 inbox_delete.py 并使用以下命令运行代码:

     python inbox_delete.py 
    
    

它是如何工作的…

与我们之前看到的例子类似,我们首先使用配置文件中的登录凭证登录到 Gmail。

我们然后连接到我们的收件箱并搜索来自 junk@xyz.com 的电子邮件。如果我们找到符合这个标准的任何电子邮件,我们希望删除它们。

因此,我们遍历邮件对象并对它们执行 delete() 操作,就是这样!我们的收件箱现在已经没有我们认为的垃圾邮件了。 😃

还有更多…

太好了!所以,现在我们知道了如何发送电子邮件消息,添加附件,以及获取和读取它们。我们还学会了如何将我们的消息标记为已读,添加适当的标签,并在需要时删除这些消息。掌握了这些知识,我们能为凯利做些什么吗?

使用电子邮件响应自动化客户支持流程

客户支持部经理凯利手头有一个问题。她的大部分支持工程师最终都在处理一级支持请求,这些请求是客户在网站上已经可以找到信息的。客户只是简单地发送电子邮件给支持部门,而没有尝试自己搜索。

这一系列事件对客户和支持工程师来说都是低效的。客户只是等待信息,而不是直接在网站上获取信息,支持工程师手动将网站上的常见问题解答FAQ)部分的指针发送给客户。凯利认为这是一个改进的机会,并希望通过自动化这个流程来减少在支持上花费的时间。我们能做些什么来帮助她?

准备工作

当然,这是一个更大的问题,但至少我们可以做一些事情来自动化流程。每当支持团队通过电子邮件收到新的工单时,我们可以自动回复工单,确认已收到工单,并从公司网站上发送 FAQ 部分的链接。这样,客户就可以浏览并从 FAQ 部分查找他们所需的信息。这也减轻了支持工程师的负担,因为自动回复的电子邮件已经迅速解决了客户的查询。

那么,我们现在到底需要什么呢?我们需要监控我们的支持收件箱,查看任何新的客户查询,然后使用我们的模板电子邮件自动回复。

如何操作…

  1. 让我们直接跳到我们的解决方案。创建一个 Python 文件,并将以下代码片段复制到其中。它正好完成了我们需要自动化支持流程的工作:

            from email.mime.multipart import MIMEMultipart
            from email.mime.text import MIMEText
            from email.mime.image import MIMEImage
            import config, time, gmail
    
            def send_email(strTo):
                strFrom = config.fromaddr
                msgRoot = MIMEMultipart('related')
                msgRoot['Subject'] = 'Thanks for your ticket'
                msgRoot['From'] = strFrom
                msgRoot['To'] = strTo
    
                msgRoot.preamble = 'This is a multi-part message 
                                    in MIME format.'
                msgAlternative = MIMEMultipart('alternative')
                msgRoot.attach(msgAlternative)
                msgText = MIMEText('This is the alternative plain
                                    text message.')
                msgAlternative.attach(msgText)
                msgText = MIMEText('Hi there, <br><br>Thanks for your 
                                    query with us today.'
                                   ' You can look at our 
                                   <a href="https://google.com">FAQs</a>'
                                   ' and we shall get back to you 
                                     soon.<br><br>'
                                   'Thanks,<br>Support Team<br><br>
                                   <img src="img/cid:image1">', 'html')
                msgAlternative.attach(msgText)
                fp = open('google.png', 'rb')
                msgImage = MIMEImage(fp.read())
                fp.close()
                msgImage.add_header('Content-ID', '<image1>')
                msgRoot.attach(msgImage)
    
                import smtplib
                server = smtplib.SMTP('smtp.gmail.com', 587)
                server.starttls()
                server.login(config.fromaddr, config.password)
                server.sendmail(config.fromaddr, config.toaddr, 
                                msgRoot.as_string())
                server.quit()
    
                while True:
                      g = gmail.login(config.fromaddr, config.password)
                      mails = g.inbox().mail(unread=True)
                      mails[-1].fetch()
                      from_ = mails[-1].fr
                      send_email(from_)
                      time.sleep(60)
    
  2. 使用 Python 运行前面的代码,你会观察到程序仍在运行。实际上,它正在等待新的电子邮件,在这种情况下,是客户对支持工程师的请求。

  3. 如果你现在给客户支持发送电子邮件,你将收到我们 Python 程序的自动回复。

    注意

    在这种情况下,支持收件箱是我的电子邮件地址,但你可以轻松地为你的公司设置一个电子邮件账户,以便客户请求被导向这个账户。

    这就是自动回复电子邮件的样子:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_06_021.jpg

它是如何工作的…

我们从一个每分钟运行一次(60 秒)的while循环开始。每次迭代都会读取支持收件箱并搜索未读电子邮件。

如果while循环发现任何未读电子邮件,它将获取该电子邮件并获取fr属性。fr属性让你获取电子邮件消息的from字段。from字段是请求支持工程师信息的客户电子邮件地址。

一旦我们有了客户的电子邮件地址,我们就会从我们的收件箱向客户发送自动回复。前面的截图显示了自动回复的确切样子。太酷了,所以现在当客户通过发送电子邮件向客户支持工程师提问时,他们将会收到一个带有常见问题解答部分链接的自动电子邮件回复。

这样,客户可以快速地从常见问题解答链接中获取所需信息。此外,由于支持工程师不需要手动回复日常支持请求,因此他们的工作负担也得到了减轻。

我相信凯莉会对这个改进感到高兴。她明白客户支持流程已经自动化到一定程度,并希望很快看到生产力的提升!

还有更多…

太棒了!你还可以用电子邮件做很多事情。尝试下载电子邮件附件了吗?想试试看?我们会让你亲自尝试。下章再见!

第六章. 演示文稿及其他

哦,今天还要给老板做一个演示文稿!我能不能简单地运行一个程序生成演示文稿,而不是手动从头再来?别再担心了;这一章可能正是解决您所有担忧的方法。我们将探讨您可以使用 Python 以自动化方式创建自己演示文稿的各种方法。

在本章中,我们将涵盖以下菜谱:

  • 读取 PowerPoint 演示文稿

  • 创建和更新演示文稿,以及添加幻灯片

  • 玩转布局、占位符和文本框

  • 使用不同形状和添加表格

  • 图片和图表的视觉盛宴

  • 自动化每周销售报告

简介

当涉及到报告数据或工作管理状态或展示一个想法时,PowerPoint 演示文稿是您最好的选择之一。PowerPoint 允许您制作交互式多媒体幻灯片来展示信息。问问您商业界的几位朋友;专业人士几乎都是在演示文稿中思考,这意味着思维过程是围绕PowerPoint演示文稿的幻灯片结构化的(简称为PPT)。

在本章中,我们讨论使用 Python 生成自定义 PowerPoint 演示文稿。您将了解如何读取、写入和操作 PPT 文件。PPT 提供了添加表格、托管图像和展示图表等功能;我们将学习如何使用 Python 来操作所有这些有趣的功能。

在本章中,我们将使用二进制文件格式:.pptx。与 PPT 版本不同,PPTX 演示文稿使用的是自 Microsoft Office 2007 引入的 Microsoft Open XML 格式。

本章中的菜谱将专注于帮助我们执行多个 PPTX 文件操作的 Python 模块;具体来说,我们将在本章中关注以下 Python 模块:

注意

注意,尽管我们试图涵盖与 PowerPoint 演示文稿相关的所有主要操作,但总是有可能做更多。因此,我强烈建议您尝试多种技巧,并用 Python 进行实践。

您也可以使用 Windows 的 Win32 COM API 在 Windows 操作系统中处理 PPT,而不是使用python-pptx模块(我们将在本章中使用)。

读取 PowerPoint 演示文稿

基于我们对 PowerPoint 演示文稿的经验,我们知道 PPT 文件包含幻灯片,每张幻灯片都包含需要向观众展示的详细信息。这个菜谱将向您展示如何使用python-pptx模块从 PPTX 文件中提取信息。

准备工作

要逐步完成这个菜谱,我们需要安装python-pptx模块。让我们使用 Python 的pip来安装这个模块:

chetans-MacBookPro:ch08 Chetan$ sudo pip install python-pptx 
Password: 
Downloading/unpacking python-pptx 
  Downloading python-pptx-0.6.0.tar.gz (6.3MB): 6.3MB downloaded 
  Running setup.py (path:/private/tmp/pip_build_root/python-pptx/setup.py) egg_info for package python-pptx 

Requirement already satisfied (use --upgrade to upgrade): lxml>=3.1.0 in /Library/Python/2.7/site-packages (from python-pptx) 
Requirement already satisfied (use --upgrade to upgrade): Pillow>=2.6.1 in /Library/Python/2.7/site-packages (from python-pptx) 
Requirement already satisfied (use --upgrade to upgrade): XlsxWriter>=0.5.7 in /Library/Python/2.7/site-packages (from python-pptx) 
Installing collected packages: python-pptx 
  Running setup.py install for python-pptx 

Successfully installed python-pptx 
Cleaning up...

已经安装了模块吗?让我们开始吧!

如何做到这一点…

  1. 我们首先使用 Microsoft PowerPoint 2013 创建一个 PPTX 文件。我们将使用这个文件作为样本来学习如何从演示文稿中读取和提取数据。如果你下载了这本书的代码示例,你也会得到这个文件。我们称这个文件为myprofile.pptx;它包含有关本书作者的信息,其中包含两张幻灯片。以下截图显示了文件内容:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_07_001.jpg

  2. 如果你查看演示文稿,第一张幻灯片有两个文本项:标题项是Chetan Giridhar,副标题是世界充满知识…… 第二张幻灯片有更多数据;它有不同的布局,幻灯片的标题是他希望,包含作者的所有四个愿望和一个圆形内容,这是生命的循环。很有趣!

  3. 在你的电脑上,转到终端并使用 vim 或选择你喜欢的编辑器。为了读取 PPT 文件,让我们首先使用 Python 代码为 myprofile.pptx 创建一个演示文稿对象:

            from pptx import Presentation 
            path_to_presentation = 'myprofile.pptx' 
            prs = Presentation(path_to_presentation) 
            print "Presentation object for myprofile file: ", prs 
    
    

    上述代码片段的输出如下:

            Presentation object for myprofile file:  
            <pptx.presentation.Presentation object at 0x10e56e550> 
    
    
  4. 好的;现在我们有了演示文稿对象,让我们使用它来获取幻灯片对象。我们知道演示文稿有两张幻灯片。以下代码获取了幻灯片对象。幻灯片在slide对象中以 Python 列表的形式表示,并且可以在 for 循环中迭代:

            print "Slides are:" 
            for slide in prs.slides: 
                print "Slide object:", slide 
    
    
  5. 上述代码片段使用演示文稿对象prs来检索幻灯片对象。代码片段的输出如下:

     Slides are: 
            Slide object: <pptx.slide.Slide object at 0x10e59f500> 
            Slide object: <pptx.slide.Slide object at 0x10e59f460>
    
    
  6. 好的,这很整洁!让我们再深入一层,看看slides对象的一些属性。以下代码打印了slide对象的一些属性:

            print "Slide has following objects:" 
            slide1, slide2 = prs.slides[0], prs.slides[1] 
            print "Slide Ids: \n", slide1.slide_id, ",", slide2.slide_id 
            print "Slide Open XML elements: \n", slide1.element, ",", 
                   slide2.element 
            print "Slide layouts: \n", slide1.slide_layout.name, ",",
                   slide2.slide_layout.name 
    
    
  7. 注意到第一张幻灯片的布局是标题幻灯片,下一张幻灯片是标题和内容,这确实是如此。我们还打印了幻灯片 IDOpen XML 元素

    Slide has following objects:
    Slide Ids: 
    256, 257
    Slide Open XML elements: 
    <Element {http://schemas.openxmlformats.org/
                      presentationml/2006/main}sld at 0x109fc2d60>, 
            <Element {http://schemas.openxmlformats.org/
                      presentationml/2006/main}sld at 0x109fc23c0>
    Slide layouts: 
    Title Slide, Title and Content
    
    
  8. 现在,每个幻灯片都包含一些形状。例如,第一张幻灯片有两个文本占位符,标题和副标题。在第二张幻灯片中,我们有两个占位符,但还有一个圆形形状。以下代码打印了这些信息:

            print "Shapes in the slides" 
            i=1 
            for slide in prs.slides: 
                print 'Slide', i 
                for shape in slide.shapes: 
                    print "Shape: ", shape.shape_type 
                i +=1 
    
    

    上述代码片段的输出如下。你可以观察到第一张幻灯片包含文本框,但第二张幻灯片也有一个自动形状:

            Shapes in the slides 
            Slide 1 
            Shape:  PLACEHOLDER (14) 
            Shape:  PLACEHOLDER (14) 
    
            Slide 2 
            Shape:  PLACEHOLDER (14) 
            Shape:  PLACEHOLDER (14) 
            Shape:  AUTO_SHAPE (1) 
    
    
  9. 好的,所以现在我们有了幻灯片、幻灯片布局和幻灯片形状。让我们尝试从两张幻灯片和所有形状中获取文本内容。以下代码正好做到了这一点:

            text_runs = [] 
            for slide in prs.slides: 
    
                for shape in slide.shapes: 
                    if not shape.has_text_frame: 
                    continue 
                for paragraph in shape.text_frame.paragraphs: 
                    for run in paragraph.runs: 
                        text_runs.append(run.text) 
    
            print "Text is: ", text_runs 
    
    

代码示例的输出如下。它包含了两张幻灯片上的所有文本。在python-pptx的世界里,这些被称为文本运行

Text is:  [u'Chetan Giridhar', u'World is full of knowledge..', u'He wishes to', u'Travel round the world', u'Build apps', u'Have fun', u'Die peacefully', u'This is circle of life'] 

它是如何工作的…

在这个菜谱中,我们读取了整个演示文稿,并获取了两张幻灯片的内容。

我们首先使用 Microsoft PowerPoint 2013 手动创建了一个 PPTX 文件,并使用 PPTX 模块的 Presentation 类创建了一个 myprofilepptx 文件的 prs 对象。使用此对象,我们通过 presentation 对象的 prs.slides 方法获得了对幻灯片的访问权限。

接下来,我们使用了 slides 对象来获取所有可用的形状,包括 slides.shapes 中的幻灯片。遍历此对象帮助我们获取幻灯片中的形状,如 PLACEHOLDERAUTO_SHAPE。我们将在本章的后续部分学习更多关于幻灯片和形状的内容。

然后,我们使用了 shape.has_text_frame 属性来检查形状是否有文本框架,如果有,则从文本框架中获取 paragraphs 对象。段落对象的 runs 属性包含了实际的文本数据列表,然后这些数据被存储在数组 text_runs 中。

还有更多…

太酷了!所以,我们在一个菜谱中学习了很多东西:演示文稿对象、幻灯片、布局、形状、文本框架和段落。有了这些,我们处于一个很好的位置来读取 PPTX 文件。

所有这些都很棒,但是嘿,我们想要创建新的 PPTX 文件,对吧?并且希望自动化创建演示文稿?那么,让我们继续看看如何在 Python 中实现这一点。

创建和更新演示文稿,以及添加幻灯片

在本节中,我们继续使用 python-pptx 模块。因此,我们不需要安装任何新的模块。我们将学习如何创建一个空白演示文稿并将其添加到幻灯片中,当然,还有一些内容。

如何操作…

  1. 让我们从创建一个带有Yo! Python字样的非常简单的示例 PPT 开始。以下代码帮助我们创建演示文稿:

            from pptx import Presentation 
    
            prs = Presentation() 
            slide = prs.slides.add_slide(prs.slide_layouts[0]) 
            slide.shapes.title.text = "Yo, Python!" 
            slide.placeholders[1].text = "Yes it is really awesome" 
    
            prs.save('yoPython.pptx') 
    
    

    如果我们运行前面的代码片段,它将创建一个标题为Yo, Python!,副标题为是的,它真的很棒的 PPTX 文件。以下截图显示了幻灯片的内容:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_07_003.jpg

    此外,请注意幻灯片布局是标题幻灯片的布局。

  2. 我们也可以从一个现有的演示文稿中创建一个新的演示文稿。在下一个代码示例中,我们使用 PowerPoint 模板创建一个新的 PPT,并向其中添加了一个带有文本内容的幻灯片。我们使用以下模板进行此示例:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_07_004.jpg

    如果我们在模板演示文稿上运行以下程序,我们将得到一个新的 PPT,如下一张截图所示:

            from pptx import Presentation 
            prs = Presentation('sample_ppt.pptx') 
    
            first_slide = prs.slides[0] 
            first_slide.shapes[0].text_frame.paragraphs[0]
                                 .text = "Hello!" 
    
            slide = prs.slides.add_slide(prs.slide_layouts[1]) 
            text_frame = slide.shapes[0].text_frame 
            p = text_frame.paragraphs[0] 
            p.text = "This is a paragraph" 
    
            prs.save('new_ppt.pptx')
    
  3. 第一张幻灯片更新了标题文本Hello!,并添加了一个带有布局、标题和内容的新的幻灯片,文本为这是一个段落。以下截图显示了新创建的演示文稿new_ppt.pptxhttps://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_07_005.jpg

它是如何工作的…

在本节中,我们学习了如何使用 Python 创建演示文稿。在前面的代码片段中,我们实现了三个不同的功能。

首先,我们使用默认模板创建了一个标题幻灯片,并向其中添加了标题文本和副标题文本。我们通过以下步骤实现了这一点:

  1. 使用 pptx 模块的 Presentation 类创建了一个演示文稿对象 prs

  2. 然后,使用 prs 对象通过 add_slide() 方法添加了一个新的幻灯片。将布局 0 作为参数传递给 add_slide(),这表示新幻灯片是类型 Title,并由变量 slide 引用。

  3. 标题布局通常包含一个标题和副标题。标题文本的内容是通过 slide.shape.title.text 属性添加的,而副标题的内容是通过 slide.placeholders.text 属性添加的。

接下来,我们从一个现有的 PPT 模板中创建了一个新的演示文稿。该模板存储在 sample_ppt.pptx 文件中,并且已经包含了一个空白布局幻灯片。这是我们在这个菜谱中实现的内容:

  1. 我们从模板 PPT 中创建了一个演示文稿对象 prs。然后我们使用演示文稿对象来引用第一个幻灯片,prs.slides[0],它存储在变量 first_slide 中。

  2. 然后使用 first_slide 对象访问第一个形状,即标题文本。标题文本随后更新为内容 Hello!

  3. 之后,我们添加了一个新的幻灯片,其布局为 Layout 一个(标题和内容),并用 slide 变量引用它。新创建的幻灯片的第一个形状是一个文本框,其中添加了内容 这是一个段落

  4. 最后,我们将新创建的演示文稿以 new_ppt.pptx 的名称保存。

还有更多…

太棒了!所以,我们学习了如何从头创建新的演示文稿,更新现有的模板,向其中添加新内容并创建演示文稿,最后,创建具有不同类型布局和项目符号数据的演示文稿。在下一个菜谱中,让我们看看我们可以使用 Python 做些什么其他的事情。

玩转布局、占位符和文本框

现在,让我们继续做一些有趣的 PPT 操作。重要的是,我们将讨论最常用的操作。

准备工作

此菜谱不需要特定的模块;我们将使用为早期菜谱安装的 python-pptx 模块。在这个菜谱中,我们将使用不同的幻灯片布局,并玩转形状和文本。

如何操作…

  1. 让我们更进一步,使用不同类型的幻灯片布局,并在其中添加项目符号内容。以下代码片段完成了我们所需的工作:

            from pptx import Presentation
    
            prs = Presentation()
            two_content_slide_layout = prs.slide_layouts[3]
            slide = prs.slides.add_slide(two_content_slide_layout)
            shapes = slide.shapes
    
            title_shape = shapes.title
            title_shape.text = 'Adding a Two Content Slide'
    
            body_shape = shapes.placeholders[1] 
            tf = body_shape.text_frame 
            tf.text = 'This is line 1.' 
    
            p = tf.add_paragraph() 
            p.text = 'Again a Line 2..' 
            p.level = 1 
    
            p = tf.add_paragraph() 
            p.text = 'And this is line 3...' 
            p.level = 2 
    
            prs.save('two_content.pptx') 
    
    

    当我们运行前面的 Python 代码时,我们得到一个新的 PPT,它包含了一个带有添加的项目符号内容的 两内容 幻灯片。以下截图显示了创建的新演示文稿的输出。不错,对吧?我们可以将项目符号内容添加到幻灯片左侧的占位符中:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_07_006.jpg

  2. 现在,让我们发挥创意,在我们的幻灯片中添加另一种类型的形状,即文本框。以下代码片段向我们的幻灯片中添加了一个文本框:

            from pptx import Presentation 
            from pptx.util import Inches, Pt 
            prs = Presentation() 
            blank_slide_layout = prs.slide_layouts[6] 
            slide = prs.slides.add_slide(blank_slide_layout) 
    
            txBox = slide.shapes.add_textbox(Inches(2), 
                    Inches(2), Inches(5), Inches(1)) 
            tf = txBox.text_frame 
            tf.text = "Wow! I'm inside a textbox" 
    
            p = tf.add_paragraph() 
            p.text = "Adding a new text" 
            p.font.bold = True 
            p.font.italic = True 
            p.font.size = Pt(30) 
    
            prs.save('textBox.pptx')
    

    以下截图显示了新创建的 PPTX 文件的外观。如果你仔细观察,你会注意到我们添加了一个文本框,并且文本框内的第二行文本是粗体、斜体,字体大小为 30。

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_07_007.jpg

它是如何工作的…

在这个示例中,我们使用了空白模板来添加一个双内容布局幻灯片。让我们看看我们还做了些什么:

  1. 在代码中,我们使用prs.slide_layouts[3]将演示文稿对象prs传递给添加具有双内容布局的幻灯片。双内容布局还有一个标题文本,它通过使用shapes.title属性更新为添加双内容幻灯片

  2. 接下来,我们查看占位符。占位符是一个预先格式化的容器,可以向其中添加内容。占位符是形状的类别,这意味着多个形状可以有占位符。例如,一个自动形状(如第一个示例中的圆形)是一个占位符;图片或图形框架也可以是占位符。在一个双内容幻灯片中,我们有两个占位符,一个在左侧,一个在右侧。我们使用shapes.placeholders[1]定位左侧的占位符,并将第一行文本**这是第一行。**添加到由shapes.placeholders[1].text_frame引用的文本框架中。

  3. 然后,我们通过向text_frame添加一个段落并使用add_paragraph()方法添加文本**再次是第二行…在一级和这是第三行…**在二级。

  4. 基本上,并非所有形状都包含文本,但可以通过使用shape.has_text_frame属性来检查一个形状是否支持文本。在这个示例中,我们知道我们的形状包含一个可以处理文本内容的占位符。因此,我们使用text_frame属性添加了第一行文本。同样,我们使用add_paragraph()方法添加后续的文本,使用level属性以项目符号的形式添加。

  5. 最后,我们将新的演示文稿以two_content.pptx为名保存。如果你查看截图,你会看到文本被添加到幻灯片左侧文本框上的项目符号样式。

接下来,我们在演示文稿中添加了一个文本框。文本框在演示文稿中非常常用。人们使用文本框来突出重点,并利用文本框的调整大小和移动功能。以下是我们示例中做了什么:

  1. 我们首先创建了一个布局为六的空白幻灯片,并使用add_slide()方法将其添加到演示文稿中。

  2. 接下来,我们创建了一个具有适当尺寸的文本框。我们使用Inches(2)作为左上角坐标,然后分别使用Inches(5)Inches(1)来管理宽度和高度。在这里,英寸映射到相同的现实世界实体,即1 英寸=2.54 厘米。我们使用add_textbox()方法将这个文本框添加到幻灯片中。

  3. 使用文本框对象,我们通过text_frame属性添加了一个文本框架对象tf

  4. 如前一个示例所示,我们在文本框架中添加了文本哇!我就在文本框里面

  5. 我们通过使用add_paragraph()方法添加一个段落,并将文本添加新文本添加到这个段落中,使文本加粗、斜体,并将字体大小增加到30

  6. 最后,我们将文件保存为textBox.pptx

你学习了关于占位符和文本框的内容。你学习了如何使用文本框和段落向我们的幻灯片添加文本。你还学习了如何向我们的幻灯片添加所需尺寸的文本框。

使用不同形状和添加表格

好的,让我们继续前进,通过添加不同的形状、表格甚至图片来使我们的演示文稿更有趣!为什么等待?让我们迅速采取行动。

准备工作

对于这个食谱,不需要特定的模块;我们将使用为早期食谱安装的python-pptx模块。

如何做到这一点…

  1. 在这个食谱中,我们将向我们的演示文稿添加一些形状。形状在演示文稿中非常有用,因为它们可以代表现实世界中的对象,可以指示关系,并为听众(正在听演示的人)提供很好的视觉反馈。在这个食谱中,我们添加了一个主页按钮,然后添加了一个矩形标注来显示我们的主页按钮的位置。我们还将自定义颜色填充到我们的Callout元素中。以下代码将形状添加到演示文稿中:

            from pptx import Presentation 
            from pptx.enum.shapes import MSO_SHAPE 
            from pptx.util import Inches 
            from pptx.dml.color import RGBColor 
    
            prs = Presentation() 
            title_only_slide_layout = prs.slide_layouts[5] 
            slide = prs.slides.add_slide(title_only_slide_layout) 
            shapes = slide.shapes 
            shapes.title.text = 'Adding Shapes' 
    
            shape1 = shapes.add_shape(MSO_SHAPE.RECTANGULAR_CALLOUT,
            Inches(3.5), Inches(2), Inches(2), Inches(2)) 
            shape1.fill.solid() 
            shape1.fill.fore_color.rgb = RGBColor(0x1E, 0x90, 0xFF) 
            shape1.fill.fore_color.brightness = 0.4 
    
            shape1.text = 'See! There is home!' 
    
            shape2 = shapes.add_shape(MSO_SHAPE.ACTION_BUTTON_HOME,
            Inches(3.5), Inches(5), Inches(2), Inches(2)) 
            shape2.text = 'Home' 
    
            prs.save('shapes.pptx') 
    
    

    在运行前面的代码后,我们得到了一个新的演示文稿,shapes.pptx,其外观如下所示:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_07_008.jpg

  2. 真是 neat!现在让我们看看我们是否可以向我们的演示文稿添加一个表格。同样,表格在演示文稿中用于展示数据和做出明智的决策。演讲者(负责制作演示的人)经常通过表格展示某些项目的相关事实,并从观众那里征求讨论或反馈。使用 Python 在演示文稿中添加表格是微不足道的;请参考以下代码。在这个例子中,我们添加了一个包含三名学生信息的表格:

            from pptx import Presentation 
            from pptx.util import Inches 
    
            prs = Presentation() 
            title_only_slide_layout = prs.slide_layouts[5] 
            slide = prs.slides.add_slide(title_only_slide_layout) 
            shapes = slide.shapes 
            shapes.title.text = 'Students Data' 
    
            rows = 4; cols = 3 
            left = top = Inches(2.0) 
            width = Inches(6.0) 
            height = Inches(1.2) 
    
            table = shapes.add_table(rows, cols, left, top,
                                     width, height).table 
            table.columns[0].width = Inches(2.0) 
            table.columns[1].width = Inches(2.0) 
            table.columns[2].width = Inches(2.0) 
    
            table.cell(0, 0).text = 'Sr. No.' 
            table.cell(0, 1).text = 'Student Name' 
            table.cell(0, 2).text = 'Student Id' 
    
            students = { 
                1: ["John", 115], 
                2: ["Mary", 119], 
                3: ["Alice", 101] 
            } 
    
            for i in range(len(students)): 
                table.cell(i+1, 0).text = str(i+1) 
                table.cell(i+1, 1).text = str(students[i+1][0]) 
                table.cell(i+1, 2).text = str(students[i+1][1]) 
    
            prs.save('table.pptx')
    

    如果我们运行这个代码片段,你将在演示文稿中看到一个包含所有学生数据的表格。请参考以下截图:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_07_009.jpg

它是如何工作的…

在第一个代码片段中,我们通过以下操作向我们的演示文稿添加了一些形状:

  1. 我们通过使用add_shapes()方法添加形状,该方法接受形状类型作为输入。

  2. 在我们的代码中,我们使用了MSO_SHAPE枚举(其中列出了所有形状)并选择了两个形状,即MSO_SHAPE.RECTANGULAR_CALLOUTMSO_SHAPE.ACTION_BUTTON_HOME。就像在文本框的情况下,add_shapes()方法也需要使用Inches()方法定义形状的大小。

  3. 我们还成功地使用SlideShape类的fill方法定义了调用形状的自定义颜色。我们使用shape.fill.fore_color.rgb属性设置了调用形状的颜色。使用的 RGB 颜色是1E90FF,这是一种浅蓝色,如截图所示。我们还使用shape.fill.fore_color.brightness属性设置了颜色亮度。

  4. 当然,我们通过设置shape.text属性在形状中添加了文本。最后,我们将文件保存为shapes.pptx

在第二个例子中,我们使用 Python 代码的帮助在演示文稿中添加了一个漂亮的表格。这是我们的做法:

  1. 我们使用add_slide()方法创建了一个仅包含标题布局的演示文稿,并添加了一个幻灯片。我们还定义了幻灯片的标题为学生数据

  2. 添加表格就像添加形状一样简单。我们使用add_table()方法将表格添加到演示文稿中。正如预期的那样,add_table()方法期望输入行数和列数,同时期望表格的大小。在我们的例子中,我们将行数设置为4,列数设置为3,表格的大小为坐标Inches(2)Inches(2)Inches(6)Inches(8),这意味着表格距离左侧 2 英寸,距离幻灯片顶部 2 英寸以下,表格宽度为 6 英寸,高度为 1.2 英寸(15.3cm x 3.1cm)。

  3. 我们定义了表格具有三个列;每个列的宽度设置为 2 英寸。我们使用table.columns.width属性来设置这一点。我们还使用table.cell(row, column).text属性设置了列标题的文本为序号学生姓名学生 ID。请注意,在这里,行值始终为0,表示第一行或标题行,而列从02变化,表示三个列。

  4. 为了本例的目的,我们使用了一个预定义的字典students,其中包含学生姓名和学生 ID 等信息。我们遍历所有学生信息,并更新表格的单元格以填充适当的信息,因此表格中包含了所有学生数据,如截图所示。

  5. 最后,我们将演示文稿保存为table.pptx

还有更多…

太棒了!使用 Python 制作演示文稿我们还能做什么呢?或者你们中的一些人已经期待我谈论图表或图片了,不是吗?哦,是的,我们也会涵盖这一点。让我们来点图形的,换句话说!

视觉盛宴:图片和图表

是时候了。在本节中,我们将探讨如何向您的演示文稿添加图片和图表。他们说“一图胜千言”,确实,您一定见过包含大量图片和图表的演示文稿。它们的存在是有原因的。您可以在一张幻灯片中传达尽可能多的信息。图表和图片都具有这种力量,如果不了解它们,本章就不完整。所以,让我们开始吧!

准备工作

此菜谱不需要特定的模块;我们将使用为早期菜谱安装的python-pptx模块。

如何操作…

我们将这个菜谱分为两部分。首先,我们将介绍如何向幻灯片添加图片,在下一部分,我们将处理图表。

  1. 以下代码片段帮助我们向幻灯片添加图片:

            from pptx import Presentation 
            from pptx.util import Inches 
    
            img_path = 'python.png' 
            img_path2 = 'learn_python.jpeg' 
            prs = Presentation() 
            blank_slide_layout = prs.slide_layouts[6] 
            slide = prs.slides.add_slide(blank_slide_layout) 
    
            left = top = Inches(2) 
            pic = slide.shapes.add_picture(img_path, left,
                  top, height=Inches(2), width=Inches(3)) 
    
            left = Inches(2) 
            top = Inches(5) 
            height = Inches(2) 
            pic = slide.shapes.add_picture(img_path2, left,
                                           top, height=height) 
    
            prs.save('picture.pptx') 
    
    

    当我们运行上述程序时,我们得到一个包含两张图片的幻灯片,如下面的截图所示:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_07_010.jpg

  2. 现在,让我们看看我们是否可以用 Python 代码向幻灯片添加图表。python-pptx模块支持多种类型的图表,如折线图、柱状图和气泡图,但我的最爱一直是饼图。在我们的菜谱中添加一个如何?是的,以下代码向演示文稿添加了一个饼图:

            from pptx import Presentation 
            from pptx.chart.data import ChartData 
            from pptx.enum.chart import XL_CHART_TYPE 
            from pptx.enum.chart import XL_LABEL_POSITION,
                                        XL_LEGEND_POSITION 
            from pptx.util import Inches 
    
            prs = Presentation() 
            slide = prs.slides.add_slide(prs.slide_layouts[5]) 
            slide.shapes.title.text = 'Data based on regions' 
    
            chart_data = ChartData() 
            chart_data.categories = ['West', 'East', 
                                     'North', 'South'] 
            chart_data.add_series('Series 1', (0.35, 
                                     0.25, 0.25, 0.15)) 
    
            x, y, cx, cy = Inches(2), Inches(2), Inches(6),
                           Inches(4.5) 
            chart = slide.shapes.add_chart( 
                XL_CHART_TYPE.PIE, x, y, cx, cy, chart_data 
            ).chart 
    
            chart.has_legend = True 
            chart.legend.position = XL_LEGEND_POSITION.BOTTOM 
            chart.legend.include_in_layout = False 
    
            chart.plots[0].has_data_labels = True 
            data_labels = chart.plots[0].data_labels 
            data_labels.number_format = '0%' 
            data_labels.position = XL_LABEL_POSITION.OUTSIDE_END 
    
            prs.save('chart.pptx') 
    
    

    上述程序输出的结果如下:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_07_011.jpg

它是如何工作的…

在第一个代码片段中,我们向幻灯片添加了两张图片。我们是如何做到的?听起来就像逻辑一样,我们使用了add_picture()方法。这个库不是很好吗?使用add_textbox()添加文本框,使用add_slide()添加幻灯片,现在使用add_picture()添加图片。让我们更深入地看看我们在菜谱的第一部分中做了什么:

  1. 如预期的那样,add_picture()期望从图像需要添加到演示文稿的路径,就像其他方法一样,图片的坐标和大小。在我们的例子中,我们添加了两张图片。第一张图片是python.org,我们将其配置为从左侧 2 英寸和从顶部 2 英寸处显示。我们还配置了图片的大小,使其宽度为 3 英寸,高度为 2 英寸。

  2. 我们添加的第二张图片是learn_python.jpeg;它被配置为从左侧 2 英寸,从顶部 5 英寸处显示,高度为 2 英寸,宽度与图片宽度相同。

  3. 在我们的例子中,我们创建了一个新的幻灯片,使用了空白幻灯片布局,并添加了这两张图片,最后将文件保存为picture.pptx

在第二部分,我们在幻灯片演示文稿中添加了一个饼图。我们这样做:

  1. 我们添加了一个幻灯片,并将标题文本设置为基于区域的数据

  2. 然后,我们创建了一个ChartData()类的对象,并将其命名为chart_data。我们使用chart_data.categories属性定义了饼图的类别,并将其设置为区域数组['西', '东', '北', '南']。我们还使用add_series()方法为所有区域配置了chart_data对象的数据。

  3. 我们是如何在演示文稿幻灯片上添加这个图表的?是的,你猜对了:add_chart()方法为我们做到了这一点。add_chart()方法期望图表类型作为其中一个参数,并且像其他方法一样,它期望维度。在我们的代码中,我们还设置了has_legendnumber_format和数据标签的属性,使饼图看起来很棒!

更多内容…

太棒了!所以我们在这个章节中学到了很多有趣的东西。但将知识应用于解决现实世界的用例不是更有趣吗?你听说过亚历克斯在每周销售报告中遇到的问题吗?

自动化每周销售报告

亚历克斯是 Innova 8 Inc 公司的销售总监,该公司销售笔记本电脑和商业软件。他有一群销售经理向他汇报。他负责衡量下属的成功,并每周向销售副总裁汇报。亚历克斯的老板主要对两件事感兴趣:来自商业账户产生的收入以及销售经理的业绩。亚历克斯需要在每周的员工会议上报告这些数字。他使用 PowerPoint 作为工具,每周将数据汇总并展示给销售副总裁。

然而,亚历克斯有一些问题。他从销售经理那里得到的数据通常在 Excel 表格中。此外,数据非常动态,根据客户是否在会议前付款,数据会一直变化到最后一刻。由于这种变化,亚历克斯很难提前为会议制作演示文稿。此外,亚历克斯分析数据并生成图表的过程非常繁琐——这是一个完全手动的过程。

你能帮助亚历克斯用你在本章中学到的知识吗?

准备工作

如果分析这些问题,我们可以为亚历克斯自动化整个流程。亚历克斯的数据在 Excel 表格中;我们可以使用 Python 的pandas模块轻松读取这些数据。此外,我们可以使用python-pptx模块创建一个新的演示文稿。

以下步骤可以帮助解决亚历克斯的问题:

  1. 读取 Excel 表格的内容并获取所需数据。

  2. 创建一个新的 PowerPoint 演示文稿,并向其中添加两张幻灯片。

  3. 在第一张幻灯片上,创建一个饼图来展示不同账户的收入数据,并根据百分比进行比较。

  4. 在第二张幻灯片上,添加一个柱状图来比较销售经理基于收入的业绩。

对于这个菜谱,让我们安装pandas模块。我们使用我们最喜欢的 Python 实用工具pip来安装pandas。我们使用以下命令来安装pandas

pip install pandas

注意

这只是一个使用pandas处理 Excel 表格的简单示例。pandas模块有一套全面的 API,可用于数据分析、过滤和聚合。我们在处理数据分析和可视化的章节中讨论了所有这些以及更多内容。

现在我们准备好了,让我们看看帮助 Alex 自动化这个过程的代码。

如何操作…

  1. 让我们先看看包含每周销售数据的 Excel 表格。我们称这个文件为Sales_Data.xlsx,它看起来如下截图所示:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_07_012.jpg

  2. 现在,让我们看看代码片段,这将帮助 Alex 读取这些数据并生成他需要的精确演示文稿:

            from pptx import Presentation 
            from pptx.chart.data import ChartData 
            from pptx.enum.chart import XL_CHART_TYPE 
            from pptx.enum.chart import XL_LABEL_POSITION, 
                                        XL_LEGEND_POSITION 
            from pptx.util import Inches 
            from datetime import datetime 
            import pandas as pd 
    
            xls_file = pd.ExcelFile('Sales_Data.xlsx') 
    
            prs = Presentation('sample_ppt.pptx') 
    
            first_slide = prs.slides[0] 
            first_slide.shapes[0].text_frame.paragraphs[0]
                       .text = "Weekly Sales Report %s" \ 
                            % datetime.now().strftime('%D') 
           first_slide.placeholders[1].text = 
                              "Author: Alex, alex@innova8" 
           blank_slide_layout = prs.slide_layouts[6] 
           slide = prs.slides.add_slide(blank_slide_layout) 
           slide.shapes.title.text = '% Revenue for Accounts' 
           df = xls_file.parse('Sales') 
           df['total'] = df['Items'] * df['UnitPrice'] 
           plot = df.groupby('Account')['total']
                    .sum().plot(kind='pie', \ 
                    autopct='%.2f', fontsize=20) 
           f=plot.get_figure() 
           f.savefig("result.png", bbox_inches='tight',
                                                dpi=400) 
           left = Inches(2.5);  top = Inches(3) 
           pic = slide.shapes.add_picture("result.png", left, top,
           height=Inches(4), width=Inches(5)) 
    
           slide = prs.slides.add_slide(prs.slide_layouts[6]) 
           slide.shapes.title.text = 'Sales Manager Performance' 
           df = xls_file.parse('Sales') 
           df['total'] = df['Items'] * df['UnitPrice'] 
           mgr_data = df.groupby(['Manager'])['total'].sum() 
           managers = mgr_data.index.values.tolist() 
           sales = [] 
           for mgr in managers:  
               sales.append(mgr_data.loc[mgr])  
    
           chart_data = ChartData() 
           chart_data.categories = managers 
           chart_data.add_series('Series 1', tuple(sales)) 
           x, y, cx, cy = Inches(2), Inches(3), Inches(6),
                          Inches(4) 
           chart = slide.shapes.add_chart( 
           XL_CHART_TYPE.COLUMN_CLUSTERED, x, y, cx, cy, 
                                              chart_data 
           ).chart 
    
           chart.has_legend = True 
           chart.legend.position = XL_LEGEND_POSITION.BOTTOM 
           chart.legend.include_in_layout = False 
    
           prs.save('sales.pptx') 
    
    
  3. 现在,如果我们运行前面的代码片段,我们将得到一个包含所有必要图表和数据点的 PowerPoint 演示文稿,这正是 Alex 每周销售报告中需要的。看看演示文稿的所有幻灯片的截图。这正是 Alex 所需要的!第一张幻灯片是演示文稿的标题幻灯片,标题为每周销售报告 <日期>。它还提到了 Alex 的名字和电子邮件地址,作为本演示文稿的作者:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_07_013.jpg

    第二张幻灯片展示了通过饼图帮助展示账户间的收入分布:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_07_014.jpg

    最后,最后一张幻灯片通过柱状图比较了所有销售经理的表现。酷吧?

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_07_015.jpg

它是如何工作的…

在前面的例子中,我们首先为包含所有销售数据的 Excel 表格Sales_Data.xlsx创建了一个读取对象。我们通过使用pandas模块的ExcelFile()方法实现了这一点。

接下来,我们通过使用sample_ppt.pptx文件创建了一个新的演示文稿。如果你还记得,我们的示例演示文稿有一个没有任何文本的标题幻灯片。因此,在我们的代码片段中,我们通过设置标题为每周状态报告 <YYYY - MM - YY>来更新了这个标题幻灯片。我们还添加了一个包含作者名字的副标题,在这个例子中是作者:Alex alex@innova8。我们通过文本框中的占位符来设置这些标题。

接下来,我们在新的演示文稿中添加了一个新的空白幻灯片,布局为六。我们使用这个幻灯片来添加账户的营收数据。我们通过prs.slides.add_slide()方法实现了这一点。然而,我们的数据存储在 Excel 表格中,因此我们使用 Excel 表格的reader对象来读取销售工作表。销售工作表包含了亚历克斯用于分析的所有数据。pandas 模块以数据框的形式读取 Excel 数据(以矩阵格式存储的数据)。如果你查看 Excel 表格的截图,我们可以看到两列,价格数量,这表示了销售的笔记本电脑或软件许可证的数量以及每单位的价格。因此,在我们的代码中,我们首先将这些值相乘以获取 Excel 记录中每个条目的收入,并将其存储在以total为列名的数据框中。现在,我们不仅需要总收入数字;还需要根据账户对其进行分类。使用数据框,获取这些信息非常简单;就像运行一个 SQL 查询一样。如果你查看代码,我们已经通过Account对数据框进行了分组,并汇总了所有的total数据(通过数量乘以价格获得)并使用这些数据点绘制饼图,这样亚历克斯就可以更容易地比较每个账户的收入占总收入的比例。对于数据分组,我们使用了groupby()方法,然后使用sum()方法汇总了收入,并使用plot()方法绘制饼图。再次强调,如果图表在pandas中可用但不在 PowerPoint 中,那么它就没有用了,所以我们使用pandassavefig()方法将图表保存为result.png文件;我们就是这样做的。最后,我们使用add_picture()方法将这张图片文件添加到我们的演示文稿中,并管理了图片的坐标和大小,以确保其可见且看起来很棒。

亚历克斯还需要绘制所有销售经理的表现。为此,我们使用了相同的方法来读取 Excel 表格数据,并以数据框的形式存储。对于这个问题,我们通过销售经理对数据进行了分组,并获取了每个销售经理分配的总收入。在这里,我们也使用了groupby()方法,但是在 Excel 数据的Manager列上。我们将所有销售经理的姓名存储在数组managers中,遍历每个销售经理的所有记录,获取他们的销售数据,并将其添加到列表sales中。然后我们将这个列表转换为元组以供以后使用。就像我们在前面的菜谱中看到的那样,我们使用ChartData()方法创建了一个图表数据对象,使用sales元组作为输入创建了一个簇状柱形图,并使用add_chart()方法将其添加到演示文稿的第二张幻灯片中。

最后,我们将这个新创建的演示文稿保存为sales.pptx,这作为亚历克斯的每周销售报告。就这样了!

我希望您喜欢本章中的食谱、我们讨论的示例和用例。我相信您也迫不及待地想要自动化您的演示文稿。我什么时候去您的办公桌欣赏您的工作呢?

第七章. API 的力量

本章将带你进入有趣的 API 世界。API 是当今商业世界的一个关键部分。查询数据、在服务之间交换信息等任务都依赖于 Web API 和 Webhooks。

在本章中,我们将涵盖以下主题:

  • 设计你自己的 REST API

  • 使用 Twitter API 自动化社交媒体营销

  • Webhooks 简介

  • 实现 Webhooks

  • 使用 Webhooks 自动化潜在客户管理

简介

在由互联网驱动的世界中,API 变得绝对不可或缺。你必须与之交互的每个网络应用程序都在其后端使用 API 来实现其核心功能——亚马逊、谷歌、推特,等等!更重要的是,你看到所有这些应用程序都是基于 API 繁荣起来的。亚马逊用它来驱动其支付交易,谷歌用它来显示所有这些花哨的地图。API 对商业如此重要,以至于从 CEO 到经理,再到软件开发者,你都能听到 API 这个词。总的来说,使用 API 是使不同的软件相互通信的基本方式。操作系统操作也是通过 API 执行的。它们从一开始就是至关重要的。

但 API 是什么?它们有什么用?我们如何开发自己的 API?它们是如何进入业务流程自动化的?在本章中,我们将找到所有这些问题的答案。

我们从一个更熟悉且更古老的网络术语开始:网络服务。网络服务本质上是在不同平台和不同语言上使用独立系统托管的不同应用程序之间的集成关键点。网络服务通过 WWW 相互通信,通常涉及两方:一方暴露一组 API,也称为服务器,另一方调用或消费服务器 API,也称为消费者或客户端。网络服务独立于客户端实现,因此与浏览器、手机或任何可以发起 API 调用的软件都很好地协同工作。

它们使用不同的协议进行通信,有不同的消息和 URI 合约。最常见网络服务的实现包括:

  • 基于 HTTP 的REST表示状态转移)网络服务

  • 基于 SOAP 的(简单对象访问协议)网络服务

  • XML RPC远程过程调用

这些服务常用的消息格式包括:

  • JSONJavaScript 对象表示法

  • XML可扩展标记语言

今天的网络应用程序的核心是网络服务,因此需要提供良好的性能;它们需要可扩展和可靠。

对了,所以在本章中,我们将介绍基于 HTTP 的 REST API。你将详细了解如何使用 Python 开发 RESTful 网络服务。你还将学习客户端如何使用 RESTful 网络服务自动化他们的业务流程。

注意

注意,有不同术语可用于引用 API,例如 HTTP API、Web API 等。我建议您阅读有关它们的资料以获得更好的清晰度。然而,本质上,它们的核心理念是应用程序中两个服务或跨应用程序的多个服务器/服务之间的集成点。

在本章中,我们将查看以下列表中提到的多个 Python 模块:

设计自己的 REST API

表征状态转移REST)在社区中获得了许多偏好和流行,几乎成为设计实现 RESTful 网络服务的默认架构风格。

注意

注意,还有其他可能的网络服务实现方式,例如遵循 SOAP 和 XML-RPC 方式,这些内容不在本章范围内。

在本食谱中,我们将学习如何使用 Python flask 微框架实现一个简单的 RESTful 网络服务。我们将实现一个用户服务用于用户管理,这是任何网络应用的一个强制方面。

REST 架构旨在与 HTTP 协议兼容,并具有资源概念,即统一资源标识符URIs)。客户端通过不同的 HTTP 请求方法向这些 URI 发送请求,并作为响应返回受影响资源的状态。

那么,我们还在等什么呢?让我们设计和实现用户网络服务。

如何实现…

  1. 让我们从定义我们的模型——用户开始。我们的用户资源通常由以下属性标识:

    • id: 用于识别用户的唯一标识符

    • username: 在应用中使用的用户名

    • email: 用于电子邮件通知的用户电子邮件地址

    • status: 检查用户是否活跃或已验证

  2. 设计 REST API 涉及到识别资源(URIs)和动词(HTTP 方法),这些动词对用户模型进行操作。我们需要执行创建新用户、更新用户某些属性、获取用户或用户列表,或必要时删除用户等操作。我们还需要将我们的操作与 HTTP 动词相关联,并为我们服务定义 CRUD 操作。CRUD 表示在用户模型上执行创建、读取、更新和删除操作。以下表格显示了我们需要的内容:

    URI方法操作
    http://v1/users/GET获取可用用户列表
    http://v1/users/POST创建新用户
    http://v1/users/1/GET获取 ID 等于 1 的现有用户详细信息
    http:///v1/users/1/PUT/DELETE更新或删除 ID 等于 1 的用户
  3. 现在让我们编写代码来实现 RESTful 用户服务。我们首先创建一个虚拟环境。我希望我们所有人都知道virtualenv,但对于初学者来说,虚拟环境是一个隔离 Python 模块的工具。这有助于解决与权限相关的问题;它还有助于避免污染全局 Python 安装,并管理跨应用使用的同一模块的版本。如果你系统上没有virtualenv,你可以使用 Python 的pip安装它,或者从pypi.python.org/pypi/virtualenv下载它。

     chetans-MacBookPro:ch07 Chetan$ pip install virtualenv 
    
            chetans-MacBookPro:ch07 Chetan$ virtualenv user 
            New python executable in user/bin/python2.7 
            Also creating executable in user/bin/python 
            Installing setuptools, pip, wheel...done. 
    
    
  4. 一旦我们安装了virtualenv,我们需要使用简单的命令来激活它。正如你在下面的第二行中看到的,virtualenv用户已经被激活:

     chetans-MacBookPro:ch07 Chetan$ source user/bin/activate 
             (user)chetans-MacBookPro:ch07 Chetan$ 
    
    
  5. 让我们继续在虚拟环境中安装flask。我们使用 Python 的pip通过pip install flask命令来完成此操作:

     (user)chetans-MacBookPro:ch07 Chetan$ pip install flask 
            Collecting flask 
              Using cached Flask-0.11.1-py2.py3-none-any.whl 
            Collecting click>=2.0 (from flask) 
              Using cached click-6.6.tar.gz 
            Collecting its dangerous>=0.21 (from flask) 
            Collecting Werkzeug>=0.7 (from flask) 
              Using cached Werkzeug-0.11.10-py2.py3-none-any.whl 
            Collecting Jinja2>=2.4 (from flask) 
              Using cached Jinja2-2.8-py2.py3-none-any.whl 
            Collecting MarkupSafe (from Jinja2>=2.4->flask) 
            Building wheels for collected packages: click 
              Running setup.py bdist_wheel for click 
              Stored in directory: /Users/chetan/Library/ 
              Caches/pip/wheels/b0/6d/8c/ 
              cf5ca1146e48bc7914748bfb1dbf3a40a440b8b4f4f0d952dd 
            Successfully built click 
            Installing collected packages: click, itsdangerous,  
              Werkzeug, MarkupSafe, Jinja2, flask 
            Successfully installed Jinja2-2.8 MarkupSafe-0.23  
              Werkzeug-0.11.10 click-6.6 flask-0.11.1  
              itsdangerous-0.24 
    
    
  6. 如果你查看安装日志,我们会发现我们似乎已经安装了flask以及模板引擎Jinja2Werkzeug,这是一个支持多种操作的 WSGI 实用工具,例如处理 cookie、文件上传和请求/响应对象。

  7. 好的!我们已经安装了flask,环境也设置得很好。让我们编写一个简约的 Web 应用程序,命名为app.py。我们的 Web 服务代码如下:

            from flask import Flask 
            app = Flask(__name__) 
    
            @app.route('/') 
    
            def index(): 
                return "Hello, Python!" 
    
            if __name__ == '__main__': 
                app.run(debug=True) 
    
    
  8. 如果你运行应用程序,你会看到 Flask 服务器正在 5000 端口上运行:

            (user)chetans-MacBookPro:ch07 Chetan$ python app.py  
             * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) 
             * Restarting with stat 
             * Debugger is active! 
             * Debugger pin code: 272-029-183 
    
    
  9. 如果你尝试访问 5000 端口的服务器,你会看到我们预期的效果:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_08_001.jpg

  10. 太好了!现在让我们改进这个来实现用户 REST API。我们首先在/v1/users/资源上实现 HTTP 的GET。在以下代码中,我们实现了一个flask路由,名为get_users() API,以返回所有用户的 JSON 格式:

            from flask import Flask, jsonify 
            app = Flask(__name__) 
    
            users = [ 
                { 
                    'id': 1, 
                    'username': u'cjgiridhar', 
                    'email': u'abc@xyz.com', 
                    'active': True 
                }, 
                { 
                    'id': 2, 
                    'username': u'python', 
                    'email': u'py@py.org', 
                    'active': False 
                } 
            ] 
    
            @app.route('/v1/users/', methods=['GET']) 
            def get_users(): 
                return jsonify({'users': users}) 
    
            if __name__ == '__main__': 
                app.run(debug=True) 
    
    
  11. 如果我们重新运行应用程序(如果你使用的是 PyCharm 等编辑器,每次你保存代码时,它都会自动重新加载应用程序),我们的flask路由就会被加载;我们现在可以在/v1/users/ API 上发出 HTTP GET请求。请求的输出将产生响应,如下一张截图所示。嘿,太酷了!我们为 RESTful 用户服务编写了第一个资源。

  12. 注意响应的头部部分:

    • Content-Type是 application/JSON(我们将在本章后面讨论消息格式)

    • 服务器是基于 Werkzeug 的 Flask

    • 日期指的是服务器响应请求的时间

    • 响应体包含以下内容:

    • 包含所有用户信息的users键输出

    • 关于用户的信息包括所需的属性,如 ID、用户名、电子邮件和账户状态

    响应格式是 JSON,如头部所示

    注意

    注意,我们使用 Firefox 的 RESTED 插件来发送这些请求。

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_08_002.jpg

  13. 太好了!我们已经将第一个 URI 作为用户服务的一部分实现了。现在让我们快速继续实现下一个资源。在这里,我们需要根据 ID 获取用户。以下 flask 路由将为我们完成这项工作:

            @app.errorhandler(404) 
            def not_found(error): 
                return make_response(jsonify({'error': 'Not found'}), 404) 
    
            @app.route('/v1/users/<int:id>/', methods=['GET']) 
            def get_user(id): 
                for user in users: 
                    if user.get("id") == id: 
                        return jsonify({'users': user}) 
                abort(404) 
    
    
  14. 在前面的代码中,我们定义了一个 flask 路由,其 API 为 get_user(id),它接受用户 ID 作为参数。当我们对这个 URI 发起 HTTP GET 请求时,get_user() API 被调用;它内部查找所有可用的用户以定位到具有所需 ID 的用户。如果找到用户,则将用户记录以 JSON 格式返回;如果没有找到,服务器发送 HTTP 404 响应。以下是一个说明此过程的截图:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_08_003.jpg

  15. 您希望用户在您的网络应用程序上注册,对吧?所以让我们编写一个 flask 路由,它将帮助创建一个新的用户。以下代码执行此操作:

            @app.route('/v1/users/', methods=['POST']) 
            def create_user(): 
                if not request.json or not 'email' in request.json: 
                    abort(404) 
                user_id = users[-1].get("id") + 1 
                username = request.json.get('username') 
                email = request.json.get('email') 
                status = False 
                user = {"id": user_id, "email": email, 
                        "username": username, "active": status} 
                users.append(user) 
                return jsonify({'user':user}), 201 
    
    
  16. 现在如果您在 /v1/users/ 资源上发起一个 HTTP POST 请求并将用户信息传递到请求体中,您将能够创建一个新的用户。默认情况下,用户的状 态将是未激活('active': False);当用户验证她的/他的电子邮件地址时,您可以将其设置为 'active': Falsehttps://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/B05370_08_56.jpg

  17. 好吧!现在让我们快速看一下将编辑用户详情并在需要时删除用户的 REST API。以下 Flask 路由将编辑用户详情:

            @app.route('/v1/users/<int:id>/', methods=['PUT']) 
            def update_user(id): 
                user = [user for user in users if user['id'] == id] 
                user[0]['username'] = request.json.get( 
                        'username', user[0]['username']) 
                user[0]['email'] = request.json.get( 
            'email', user[0]['email']) 
                user[0]['active'] = request.json.get( 
            'active', user[0]['active']) 
                return jsonify({'users': user[0]}) 
    
    
  18. 现在如果我们对 /v1/users/:id/ 资源执行 HTTP PUT 操作并传递更改后的数据,我们应该能够更新用户信息。在以下截图中,请求体包含需要更新为用户 ID 等于 1 的新电子邮件地址。当我们发起 HTTP PUT 请求时,信息得到更新,我们有了用户的新电子邮件地址:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/B05370_08_78.jpg

  19. 现在唯一待定的操作是实现 DELETE 操作。我们可以使用这个操作来删除一个用户。但您可能会问,“我为什么要删除用户?”所以您可以为 DELETE 操作实现自己的实现;可能的话,您可以通过将 active 属性设置为 False 来使用户未激活。但为了这次讨论,让我们随意删除用户。

  20. 以下代码根据用户 ID 删除用户:

            @app.route('/v1/users/<int:id>/', methods=['DELETE']) 
            def delete_user(id): 
                user = [user for user in users if user['id'] == id] 
                users.remove(user[0]) 
                return jsonify({}), 204 
    
    
  21. DELETE 操作通常返回状态码 204 NO CONTENT,如下截图所示:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_08_008.jpg

  22. 太棒了!所以我们已经完全实现了 RESTful 用户服务并使其运行。太好了!

它是如何工作的…

REST 是万维网的一种架构风格。它由一组协调的组件组成,其中重点在于组件角色和数据元素之间的交互,而不是实现。其目的是使网络更具可扩展性、可移植性和可靠性,并提高性能。

REST 架构根据以下约束工作:

  • 客户端-服务器统一资源定位符URL)将 REST API 与客户端分开。服务器不关心用户界面或状态;因此,REST API 更具可扩展性。

  • 无状态:这意味着每个请求都是独立的,并且与之前的请求或客户端没有关联。客户端必须包含完成请求所需的所有信息,会话状态保持在客户端,因此不会存储在服务器上。

  • 可缓存:RESTful 网络服务可以缓存或不缓存响应。服务必须让客户端知道响应是否被缓存。这有助于提高系统的性能,因为某些请求可能不再需要,基于缓存过期时间。

  • 分层系统:客户端可能直接与服务器交互,也可能不直接交互;它们始终可以拥有中介服务器,如缓存或负载均衡器。

  • 统一资源:每个 REST 资源应该是独立的;这允许你有一个关注点的分离,并且解耦了架构。

  • 按需代码:服务器可以为客户端提供在其上下文中执行的代码。这是一个可选的要求。

GETHEAD方法是安全方法的例子,因为它们不会改变资源的状态。PUT/DELETE方法是幂等的。这意味着客户端可以对资源进行多次类似的调用,资源将以完全相同的方式表现;当然,响应本身也会发生变化。

太棒了!现在我们处于创建自己的 RESTful API 的位置。我们可以在互联网上托管这些 API,供我们的客户使用,或在我们的网络应用程序中实现功能。做得好!

还有更多…

我们研究了 REST 架构的基础,并学习了如何设计 RESTful 网络服务。我们得到了 Flask 微框架的帮助,并学习了如何编写我们自己的 REST API。

在下一个菜谱中,我们将看到客户端如何根据其需求消费 REST API。我们还将学习如何使用 REST API 来自动化业务流程。

使用 Twitter API 自动化社交媒体营销

乔伊是一家世界知名消费品牌的营销经理。她负责公司的内容营销组合,并严重依赖博客和社交媒体来展示公司的产品线并在市场上制造轰动。

毫无疑问,她有几个问题!她所营销的一些产品针对不同的市场设计不同,因此她必须跨越时区工作,以确保她的内容在正确的时间发布。她还觉得有必要重复发布帖子,以确保她能够接触到大多数客户;这也有助于提高品牌知名度。

准备中

如果你仔细分析她的情况,Joy 有两个问题。一是,她必须确保她的社交媒体内容在正确的时间发布,基于她的客户市场。所以如果她的产品在澳大利亚销售,她需要确保她的推文在澳大利亚时间发布,那时她的客户最有可能查看。二是,为了使她的产品公告(如周末优惠)获得更多关注,她可能希望在稍后的时间重复几条推文。

好的!现在我们了解了她的问题,让我们尝试制定一个解决方案。看起来我们需要注意以下要点:

  • 我们应该提供她自动发布推文的 capability

  • 她的推文应该在期望的时间发布,即使 Joy 睡着了

  • 我们还应该提供安排重复推文的 capability

如何做…

REST APIs 来拯救!Twitter 提供了一套令人惊叹的 REST APIs,用户可以使用这些 API 来玩转 Twitter 数据、用户信息,当然还有发布推文。你还可以执行多个操作,如上传图片、查询时间线、发送私信。哇!太酷了!但让我们不要分心,而是继续解决手头的这个问题:

  1. 首先,让我们看看我们如何使用 Python 发布推文,即使不登录到 Twitter。要发布推文,我们将使用一个名为 twython 的 Python 库。所以让我们使用我们的朋友 Python pip 安装 twython

     (user)chetans-MacBookPro:ch07 Chetan$ pip install twython 
            Collecting twython 
              Downloading twython-3.4.0.tar.gz 
            Collecting requests>=2.1.0 (from twython) 
              Downloading requests-2.11.1-py2.py3-none-any.whl (514kB) 
                100% |████████████████████████████████| 516kB 495kB/s  
            Collecting requests-oauthlib>=0.4.0 (from twython) 
              Downloading requests_oauthlib-0.6.2-py2.py3-none-any.whl 
            Collecting oauthlib>=0.6.2 (from requests-oauthlib>=0.4.0-
            >twython) 
              Downloading oauthlib-1.1.2.tar.gz (111kB) 
                100% |████████████████████████████████| 114kB 80kB/s  
            Building wheels for collected packages: twython, oauthlib 
              Running setup.py bdist_wheel for twython 
              Stored in directory: /Users/chetan/Library/Caches/pip/ 
              wheels/48/e9/f5/a4c968725948c73f71df51a3c6 
              759425358c1eda2dcf2031f4 
              Running setup.py bdist_wheel for oauthlib 
              Stored in directory: /Users/chetan/Library/Caches/pip/ 
              wheels/e6/be/43/e4a2ca8cb9c78fbd9b5b14b9 
              6cb7a5cc43f36bc11af5dfac5b 
            Successfully built twython oauthlib 
            Installing collected packages: requests, oauthlib,  
                                   requests-oauthlib, twython 
            Successfully installed oauthlib-1.1.2  
              requests-2.11.1 requests-oauthlib-0.6.2 twython-3.4.0 
             (user)chetans-MacBookPro:ch07 Chetan$ 
    
    
  2. 但在我们开始玩转我们的 Twitter 账户之前,我们需要在 Twitter 上注册一个应用程序。这确保了 Twitter 了解我们的 API 调用,并认为它们是合法的。我们可以通过导航到 apps.twitter.com/ 并点击 Create New App 来注册一个应用程序。你可以填写以下截图所示的详细信息并创建你的应用程序。

    请注意你需要填写的一些细节:

    • 应用程序名称在 Twitter 的所有用户中是唯一的,所以尽量让它对你来说非常独特,但同时也要保持简单

    • 制作一个精确定义你的 use case 的描述,这样你以后就能记住它

    • 填写你的网站名称;保持简短

    • 只有当你想让 Twitter 发送有关你的 authentication 的数据给你时,才需要回调 URL,而这在这个练习中不是必需的

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_08_009.jpg

  3. 你还需要获取你的 App Key 和 App Secret,为此你需要 OAuth Token 和 OAuth Token Secret。这些基本上是用于验证你的 API 调用与 Twitter,否则 Twitter 会拒绝你的 REST API 调用,认为它是恶意行为。你可以通过点击你新创建的应用程序并浏览到页面顶部的 Keys and Access Tokens 选项卡来获取这些详细信息:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_08_010.jpg

  4. 好的!让我们写一些代码,检查我们是否可以与 Twitter 的 REST API 一起工作。以下代码调用 Twitter 时间轴 REST API,并获取您时间轴上最顶部推文的详细信息。在这里,我们对dev.twitter.com/rest/reference/get/statuses/home_timeline REST API 执行 HTTP GET操作:

            from twython import Twython 
    
            APP_KEY = '' 
            APP_SECRET = '' 
            OAUTH_TOKEN ='' 
            OAUTH_TOKEN_SECRET = '' 
            twitter = Twython(APP_KEY, APP_SECRET, 
                              OAUTH_TOKEN, OAUTH_TOKEN_SECRET) 
    
            tweet = twitter.get_home_timeline()[1] 
            print "Tweet text: ", tweet["text"] 
            print "Tweet created at: ", tweet["created_at"] 
            print "Tweeted by: ",  
            tweet["entities"]["user_mentions"][0]["name"] 
            print "Re Tweeted?: ", tweet["retweet_count"] 
    
    

    前面代码片段的输出如下:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_08_011.jpg

    这很酷!我们从时间轴上的帖子中获取所有必要的信息。看起来在 Twitter 应用和密钥方面我们已经准备好了。

  5. 现在,让我们尝试通过向它发送数据来使用状态 REST API 进行推文。这里使用的 REST API 是dev.twitter.com/rest/reference/post/statuses/update。以下 Python 代码将对这个 REST 资源发起POST请求,并在我的推特账户上创建一条推文:

            from twython import Twython 
    
            APP_KEY = '' 
            APP_SECRET = '' 
            OAUTH_TOKEN ='' 
            OAUTH_TOKEN_SECRET = '' 
            twitter = Twython(APP_KEY, APP_SECRET, 
                              OAUTH_TOKEN, OAUTH_TOKEN_SECRET) 
            twitter.update_status(status='Python import antigravity
            https://xkcd.com/353/') 
    
    

    在运行前面的代码后,我查看了推特,哇!我的名字下自动出现了一条推文。以下是这条推文的截图:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_08_012.jpg

    因此,我们已经为乔伊解决了第一个问题。即使她不在或无法登录互联网,也可以代表她发布推文。这可以通过前面的 Python 代码片段完成。但她还不能按照澳大利亚时区安排她的推文。嗯,让我们现在看看如何解决调度问题。

  6. 在我们查看如何安排推文之前,我们将安装一个对下一个菜谱非常有用的模块。我们将安装pytz。它帮助我们处理时区,并将有助于解决乔伊的问题:

     (user)chetans-MacBookPro:ch07 Chetan$ pip install pytz 
            Collecting pytz 
              Using cached pytz-2016.6.1-py2.py3-none-any.whl 
            Installing collected packages: pytz 
            Successfully installed pytz-2016.6.1 
    
    
  7. 为了解决调度问题,我们需要两样东西。首先,我们需要一个配置,可以用来决定推文的内容、时间和时区。其次,我们需要一个运行程序,将使用这个配置在推特上发布推文。现在让我们看看以下代码,它正好是我们需要的:

            scheduled_tweets.py 
    
            from twython import Twython 
    
            APP_KEY = '' 
            APP_SECRET = '' 
            OAUTH_TOKEN ='' 
            OAUTH_TOKEN_SECRET = '' 
            twitter = Twython(APP_KEY, APP_SECRET, 
                      OAUTH_TOKEN, OAUTH_TOKEN_SECRET) 
    
            from datetime import datetime 
            import pytz, time 
            from pytz import timezone 
            import tweet_config as config 
    
            while True: 
    
                for msg in config.scheduled_messages: 
                    print msg["timezone"] 
                    tz = timezone(msg["timezone"]) 
                    utc = pytz.utc 
                    utc_dt = datetime.utcnow().replace(tzinfo=utc) 
                    au_dt = utc_dt.astimezone(tz) 
                    sday = au_dt.strftime('%Y-%m-%d') 
                    stime = au_dt.strftime('%H:%M') 
                    print "Current Day:Time", sday, stime 
    
                    if sday == msg["day"]: 
                        if stime == msg["time"]: 
                            print "Time", stime 
                            print "Content", msg["content"] 
                            twitter.update_status(status='%s' %
                            msg["content"] ) 
    
                print "Running.. Will try in another min" 
                time.sleep(60) 
                tweet_config.py
            offers_sydney = { 
                "content":"Weekend Offers, avail 30% discount today!", 
                "day":"2016-08-27", 
                "time":"13:25", 
                "timezone":"Australia/Sydney" 
            } 
    
            post_newyork = { 
                "content":"Introducing sun glasses at your favorite stores 
                in NY!", 
                "day":"2016-08-27", 
                "time":"12:41", 
                "timezone":"America/New_York" 
            } 
    
            scheduled_messages = [offers_sydney, post_newyork] 
    
    

    前面代码的输出如下。这是第一次迭代:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_08_013.jpg

    这是第二次迭代:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_08_014.jpg

    这是实际的推文:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_08_015.jpg

  8. 太酷了!所以我们有了乔伊需要的东西。一条自动推文,在正确的时间,向全球的正确受众推送正确的内容。哇哦!

它是如何工作的…

在前面的代码片段中,我们有两个文件。我们有tweet_config.py,它包含一个配置字典,用于指定推文的内容和安排。它还提到了需要发布推文的时区。

第二个文件scheduled_tweets.py是一个运行程序。它每分钟检查一次配置,看看当天是否有安排的推文。

当运行程序scheduled_tweets.py运行时,它会检查是否有任何安排的消息。在迭代 1 中,运行程序没有找到需要处理的内容;它只是返回当前时区的日期和时间。

在迭代 2 中,它确实发现有一个在澳大利亚时区,即悉尼,8 月 27 日 13:25 安排的推文;^,因为时间匹配,它发布了一条推文。当然,这里采用的例子非常简单。我们可能想要安排 Cron 作业而不是无休止的 while 循环。但是嘿,这是一个为了说明自动安排推文这一点的例子。

在本节中,我们自动化了 Joy 的营销过程。现在她不仅可以在睡觉时发推文,还可以为不同的时区和不同的内容安排推文。这就是自动化的力量。

“但是嘿,这只是一个社交媒体平台;那 Facebook 呢?”你可能想知道。是的,我们这里有一个小技巧。Twitter 提供了连接你到多个服务的应用程序,包括 Facebook。所以为你的账户配置一个应用程序,以便你发布的每条推文也会被发布在 Facebook 上。这是配置看起来像什么。它发布你的原始推文,并在你的 Facebook 个人资料上转发它们:

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_08_016.jpg

记得我们最初发布的关于 Python 反重力(antigravity)的消息吗?是的,它实际上也被发布在了 Facebook 墙上。看看推文日期和时间旁边的源代码;是的,那是 Twitter!也许,Twitter 使用 Facebook API 来自动化这个过程:

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_08_017.jpg

Webhooks 简介

在上一节中,我们了解了如何设计和开发 REST API,以及如何通过 Twitter/Facebook 自动化的例子来利用 REST API 为我们带来好处。让我们看看另一个令人惊叹的部分:Webhooks。Webhook 是一种 HTTP 回调——当发生有利事件时,向用户定义的 URL(实现为 HTTP API)发送的 HTTP POST请求。Webhooks 通常被称为反向 API,用于跨服务的实时通信或集成。但在我们深入之前,让我们了解一下轮询。

你可能见过应用程序长时间轮询以检查是否发生了某个事件,以便它们可以对该事件执行一些后续操作。以一个现实世界的例子来说明。你去了自助餐厅,为午餐点了一份你最喜欢的披萨。柜台上的那个人给你一个订单号,并告诉你去观察那个标记机,以便你可以领取你的披萨。当周围的人都忙着吃饭时,你饿了,每隔 5 秒钟就会看一次这个标记机,希望看到你的订单号在上面闪烁。现在这就是轮询。你正在轮询这个标记机。在 API 世界中,客户端会轮询披萨店的 API 来检查订单的状态。

服务台的人当订单准备好时大声喊出订单号不是足够简单吗?所以在你下单后,你可以忙于检查你的官方电子邮件。当服务人员叫出你的订单号时,你可以从配送柜台取走你的披萨。这确保了你的时间得到更好的利用。现在这就是一个 Webhook。当发生有利的事件(你的订单准备好了)时,你会在你的 URL(在这种情况下,你的耳朵)上收到回调(服务人员大声喊出你的订单号),这个 URL 实际上是在监听并响应回调。在 API 世界中,你会注册你的 URL(HTTP API),当你的订单准备好时,披萨店会调用这个 URL。

Webhooks 可以用于三个主要目的:

  • 实时接收数据

  • 接收数据并将其推送到另一个服务

  • 接收数据然后处理并返回它

你可以思考在前面三个场景中如何使用 Webhooks 的许多不同方式。

如果你想到轮询和 Webhooks,它们都使用 API 来满足集成需求。虽然轮询是一种客户端驱动的集成技术,但 Webhooks 是服务器驱动的。轮询在效率上非常低,因为客户端不断地通过时间戳调用服务器 API 来检查资源的状态(在我们的例子中,是一个订单资源)。轮询可以每 x 分钟、x 小时甚至 x 秒进行,以变得更加实时,但我认为你已经了解了轮询的低效率。另一方面,Webhooks 在有利事件发生时将数据发送回回调 URI。这比持续的轮询更有效率,但不利的一面是你最终需要在客户端开发 API,所以你的客户端倾向于表现得像服务器本身。

实现 Webhooks

带着这些知识,让我们开始并在这个菜谱中实现 Webhooks。

准备工作

对于这个菜谱,我们将使用一个著名的 Python 网络框架,称为 Django。它允许你使用多个插件,这些插件可以简单地插入。在这里,我们将使用 Zapier 开发的 django-rest-hooks 插件来实现 Webhooks。

所以让我们开始并安装所需的包。我们使用我们最喜欢的工具 Python pip 安装 Django==1.10django-rest-hooks==1.3.1

 (user)chetans-MacBookPro:ch07 Chetan$ pip install Django==1.10 
        Collecting Django==1.10 
          Downloading Django-1.10-py2.py3-none-any.whl (6.8MB) 
            100% |████████████████████████████████| 6.8MB 71kB/s  
        Installing collected packages: Django 
        Successfully installed Django-1.10 

         (user)chetans-MacBookPro:ch07 Chetan$ pip install django-rest-hooks 
        Collecting django-rest-hooks 
          Downloading django-rest-hooks-1.3.1.tar.gz 
        Requirement already satisfied (use --upgrade to upgrade): Django>=1.4 in ./user/lib/python2.7/site-packages (from django-rest-hooks) 
        Requirement already satisfied (use --upgrade to upgrade): requests in ./user/lib/python2.7/site-packages (from django-rest-hooks) 
        Building wheels for collected packages: django-rest-hooks 
          Running setup.py bdist_wheel for django-rest-hooks 
          Stored in directory: /Users/chetan/Library/Caches/pip/wheels/96/93/12/3ec10693ee2b394a7d8594e8939f7506d7231fab69c8e69550 
        Successfully built django-rest-hooks 
        Installing collected packages: django-rest-hooks 
        Successfully installed django-rest-hooks-1.3.1 

如何做到这一点…

  1. 好的,让我们创建一个 Django 应用。我们通过以下命令来完成这个操作:

     python manage.py startproject bookstore 
               cd bookstore 
               python manage.py startapp book 
    
    
  2. 接下来,让我们配置 Django 以在应用中使用 rest_hooks 模块。我们通过将 rest_hooks 添加到 bookstore/settings.py 中的 INSTALLED_APPS 来完成这个操作。我们还把我们的应用 book 添加到这个列表中。通过使用 HOOK_EVENTS 常量,将一个事件 user.signup 添加到 settings.py 中。在这里,我们没有将事件 user.signup 绑定到任何操作,所以它是空的。这就是 settings.py 应该看起来像的:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_08_018.jpg

  3. 现在,让我们将此事件注册到回调 URL 上。但在我们继续之前,导航到你的项目根目录并运行以下命令以初始化你的 Django 模型:

     (user)chetans-MacBookPro:bookstore Chetan$ python manage.py
            migrate 
            Operations to perform: 
              Apply all migrations: admin, contenttypes, rest_hooks, auth,
              sessions 
            Running migrations: 
              Rendering model states... DONE 
              Applying contenttypes.0001_initial... OK 
              Applying auth.0001_initial... OK 
              Applying admin.0001_initial... OK 
              Applying admin.0002_logentry_remove_auto_add... OK 
              Applying contenttypes.0002_remove_content_type_name... OK 
              Applying auth.0002_alter_permission_name_max_length... OK 
              Applying auth.0003_alter_user_email_max_length... OK 
              Applying auth.0004_alter_user_username_opts... OK 
              Applying auth.0005_alter_user_last_login_null... OK 
              Applying auth.0006_require_contenttypes_0002... OK 
              Applying auth.0007_alter_validators_add_error_messages... OK 
              Applying rest_hooks.0001_initial... OK 
              Applying sessions.0001_initial... OK 
    
    
  4. 一旦初始化了模型,转到数据库 shell 并运行以下 Python 代码片段。它们将在 Django 用户表中创建一个用户并为该用户注册一个 Webhook:

     >>> from django.contrib.auth.models import User 
            >>> from rest_hooks.models import Hook 
            >>> usr=User.objects.create(username='chetan') 
            >>> hook = Hook(user=usr, event='user.signup',
            target='http://localhost:8000/hook/') 
            >>> hook.save() 
            >>> hook 
            <Hook: user.signup => http://localhost:8000/hook/> 
    
    
  5. 现在将一个名为 urls.py 的文件添加到 Book 应用程序中,并添加以下代码:

            from django.conf.urls import url 
            from . import views 
            urlpatterns = [ 
                url(r'event/$', views.event), 
                url(r'hook/$', views.webhook),] 
    
    
  6. 将以下方法添加到 book/views.py 中以创建 Django 视图:

            from django.shortcuts import render
            from django.views.decorators.csrf import csrf_exempt 
            from rest_hooks.signals import raw_hook_event 
            from django.contrib.auth.models import User 
            import datetime 
            from django.http.response import HttpResponse 
            # Create your views here. 
    
            @csrf_exempt 
            def webhook(request): 
                print request.body 
                return HttpResponse() 
    
            def event(request): 
                user = User.objects.get(username='chetan') 
                raw_hook_event.send( 
                    sender=None, 
                    event_name='user.signup', 
                    payload={ 
                        'username': user.username, 
                        'email': user.email, 
                        'when': datetime.datetime.now().isoformat() 
                    }, 
                    user=user # required: used to filter Hooks 
                ) 
                return HttpResponse() 
    
    
  7. 此外,将以下 URL 包含在项目中的 bookstore.urls.py 文件中,如下所示:

            from django.conf.urls import url, include 
            from django.contrib import admin 
    
            urlpatterns = [ 
                url(r'^admin/', admin.site.urls), 
                url(r'^', include('book.urls')) 
            ] 
    
    
  8. 现在按照以下方式运行 Django 服务器:

     (user)chetans-MacBookPro:bookstore Chetan$ python manage.py 
            runserver 
            Performing system checks... 
    
            System check identified no issues (0 silenced). 
            August 27, 2016 - 11:14:52 
            Django version 1.9, using settings 'bookstore.settings' 
            Starting development server at http://127.0.0.1:8000/ 
            Quit the server with CONTROL-C. 
    
    
  9. 从您的浏览器中,访问 http://localhost:8000/event/ 并查看您的服务器日志。您将看到已注册的 Webhook 被调用,这意味着向目标 URL http://localhost:8000/hook/ 发送了一个包含我们配置在视图中的所有信息的 HTTP POST 请求。服务器日志如下所示:

            [27/Aug/2016 10:53:29] "GET /event/ HTTP/1.1" 200 0 
    
            {"hook": {"target": "http://localhost:8000/hook/", "id": 1,
            "event": "user.signup"}, "data": {"username": "chetan", "when": 
            "2016-08-27T10:53:29.301317", "email": ""}} 
    
             [27/Aug/2016 10:53:29] "POST /hook/ HTTP/1.1" 200 0 
    
    

太棒了!你看了吗?我们调用了 /event URL,它反过来将所需信息发布到目标 URL,该 URL 已在我们的 Webhook 中注册为 user.signup 事件。

与自定义 Webhook 类似,也可以开发 RESTful Webhook。RESTful Webhook 通过 RESTful 接口支持订阅、通知和发布操作。RESTful Webhook 必须支持四种事件类型,即 ACCESSEDCREATEDUPDATEDDELETED,它们对应于四个 HTTP 动词;对于应用于资源的操作,应发送通知。例如,当资源被创建时,会生成一个事件;因此,Webhook 必须被触发,并且目标 URL 应该被发布。在我们的示例中,我们可以定义两个额外的钩子事件,即 book.addedbook.deleted,以及如 book.Book.addedbook.Book.deleted 的操作。当我们对模型执行 book.save() 操作时,book.added 事件将被触发,并且如果我们为该用户定义了此事件的钩子,则将在目标 URL 上调用 HTTP POST 请求。

它是如何工作的…

在前面的代码片段中,我们首先在 settings.py 文件中定义了一个事件。该事件名为 user.signup。由于它是一个自定义事件,因此没有定义任何操作。

我们随后使用默认的 Django 用户模型在 auth_user 表中创建了一个新的用户 chetan

之后,我们为用户 chetan 定义了一个 Webhook。此 Webhook 被配置为 user.signup 事件,目标 URL 设置为 http://localhost:8000/hook/

我们还在我们的 Django 应用程序中定义了两个视图。第一个视图负责触发对应用户和事件的 Webhook,并发送有效负载信息。第二个视图 Webhook 定义为目标 URL。

我们随后启动了 Django 开发服务器,并导航到 http://localhost:8000/event/,该地址将有效负载信息发布到目标 URL,即 http://localhost:8000/hook/。目标 URL 接收了所有有效负载数据,例如用户名、电子邮件以及注册发生的时间。

还有更多…

在本节中,我们探讨了轮询和 Webhooks,它们是 Web 上使用 API 的另一种集成形式。我们学习了轮询的效率低下,以及 Webhooks 如何更加有用。在前面的菜谱中,我们介绍了一个对用户注册有用的自定义事件,因为我希望以通用的方式解释这个概念。覆盖范围很简短,所以我希望你能更多地研究 RESTful Webhooks,因为它们展示了自动化强大的用例。有了这个理解,让我们看看 Oliver 遇到了什么问题,以及我们如何帮助他。

使用 Webhooks 自动化潜在客户管理

Oliver 是 Joy 的同事,在市场营销部门工作。他负责用户入职流程。他的主要职责包括:

  • 向在您的网站上注册的用户发送欢迎邮件

  • 将新签约者的记录添加到 CRM 中

以前,由于网站上的注册数量很少,他很容易手动执行这两个任务。但随着网站知名度的增长,他开始看到每天注册数量的激增。毫无疑问,他认为这是一个非常耗时且可以轻松自动化的活动。你能帮助 Oliver 吗?

如果我们仔细分析问题,Oliver 的主要问题是服务之间的集成。他需要集成的两个服务是电子邮件和 CRM。他需要跟踪一个注册事件并对这个事件采取行动。Webhooks 是这个用例的完美解决方案。让我们看看我们如何帮助 Oliver 自动化他的任务。

如何操作…

我们将使用同一个 Django 项目来解决这个问题。我们还将使用外部服务 Zapier,看看它如何帮助使事情变得如此简单。让我们开始吧:

  1. 从终端进入 Django 项目的根目录,并运行 Python manage.py shell 命令以登录到 DB shell。在这里,更新我们的用户chetan的电子邮件地址。这可以通过以下命令集实现:

     >>> from django.contrib.auth.models import User 
            >>> from rest_hooks.models import Hook 
            >>> usr = User.objects.get(username='chetan') 
            >>> usr.email='chetan@email.io' 
            >>> usr.save() 
    
    
  2. 现在通过导航到zapier.com/创建 Zapier 应用的账户。一旦创建了账户,点击创建 ZAP进入选择应用,然后在内置应用部分下点击Webhookshttps://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_08_019.jpg

  3. 一旦你选择了一个 Webhook,你将得到一个屏幕,在左侧面板上创建一个触发器操作。在右侧,选择捕获钩子选项。点击保存+继续。参考以下截图:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_08_020.jpg

  4. 接下来,你将获得一个页面,让你提供你想要从有效载荷中选择的一个 JSON 密钥。这是一个可选步骤,可以忽略。点击继续进入下一步。在这里,你会得到一个自定义的 Webhook URL。复制这个 URL;它将作为目标 URL。

  5. 现在,回到您的 Django 项目并导航到 DB shell。创建一个新的钩子,使用相同的事件 user.signup 并将目标 URL 定位到您在早期步骤中收到的 URL。命令如下所示:

     >>> hook = Hook(user=usr, event='user.signup', 
            target=
            'https://hooks.zapier.com/hooks/catch/<Id>/<Webhook_Id>/') 
            >>> hook.save() 
            >>> hook 
            <Hook: user.signup => 
            https://hooks.zapier.com/hooks/catch/<Id>/<Webhook_Id>/> 
    
    
  6. 使用 Python manage.pyrunserver 命令运行 Django 开发服务器。一旦服务器启动,请访问 http://localhost:8000/event/;这将向 Zapier 获得的目标 URL 发起回调请求。您可以通过再次访问 Zapier 并在捕获钩子部分左侧的测试此步骤中查看来验证这一点:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_08_021.jpg

  7. 太棒了!我们现在已经设置了触发器。接下来,让我们设置操作。为此,转到您的左侧面板,并在操作下点击设置此步骤。从屏幕右侧显示的应用程序列表中选择 Gmail:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_08_022.jpg

  8. 一旦点击Gmail,你将获得选择下一步操作的下拉菜单,例如创建草稿发送电子邮件。点击发送电子邮件,并通过允许 Zapier 访问您的电子邮件账户来激活您的电子邮件账户。以下截图将展示如何执行这些步骤:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_08_023.jpg

    在下一个截图中,我们允许 Zapier 访问 Gmail 应用程序:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_08_024.jpg

  9. 好的!现在唯一要做的就是创建电子邮件模板。我们的模板包含收件人电子邮件地址、主题和正文。Zapier 提供了一个很好的选项来配置您的模板。如果您已经通过向目标 URL 发送数据来测试了触发器,您将在电子邮件模板中每个字段的极端右侧看到一组选项。在接下来的两个截图中,我在收件人字段中输入了数据电子邮件,在主题字段中输入了欢迎数据用户名,并将电子邮件正文设置为您的注册让我们的日子变得美好

  10. 以下截图显示了目标 URL 在触发器的测试此步骤部分接收到的有效载荷中的所有可用选项下拉列表。我刚刚展示了用户名。看看模板的字段名收件人如何从有效载荷中选择数据用户名https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_08_025.jpg

    在以下截图中,您可以看到配置了所有必要字段的电子邮件模板。我们在 Zapier 中配置了电子邮件的收件人、主题和正文部分:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_08_026.jpg

  11. 就这样!点击屏幕底部的继续;Zapier 将测试您的操作,您就完成了。以下截图显示了成功的确认!!https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_08_027.jpg

  12. 现在,如果你检查你的电子邮件,你应该已经收到了来自 Zapier 的测试电子邮件,这是用来测试 Zapier 操作的。电子邮件的内容正是我们想要的样子。非常酷!所以现在,当任何人注册奥利弗的产品网站时,视图将 POST 注册者的信息作为有效载荷到 Zapier 的 Webhook(目标 URL),Zapier 将自动化电子邮件部分。https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_08_028.jpg

它是如何工作的…

Zapier 为我们提供了一个创建自定义 Webhooks 的功能。它与几乎所有应用程序都有集成,例如 Gmail、Trello、Slack 等。我们只是创建了一个 Webhook 作为触发器,然后跟随 Gmail 的一个操作。

每当用户注册(新用户创建)时,Django 应用程序会将用户的数据作为有效载荷 POST 到我们在 Zapier 中创建触发器时得到的 Zapier 目标 URL。

一旦 Zapier 收到目标 URL 的有效载荷数据,它会检查操作并发现需要向一个 Gmail 账户发送电子邮件。Zapier 还足够智能,能够从有效载荷中获取数据并将电子邮件发送到用户的电子邮件地址;它还允许配置电子邮件的主题和正文。

太棒了!奥利弗很高兴!那么第二步呢?嗯,它又是另一个 Zapier 触发器,可以是 Salesforce 或 Pipedrive CRM,用于在 CRM 中创建潜在客户记录。轻而易举!

在本节中,我们探讨了使用用户注册事件来自动化用户入职。我们以 Zapier 为例,因为它是最理想的自动化应用程序的方式。如果我们没有这样做,我们可能最终会理解所有这些应用程序提供的 API,并为它们各自编写代码,这可能并不是你的产品或服务的核心。

好了,就是这样,朋友们!希望你们喜欢这篇自动化文章,我确信你们一定会将其应用到你们组织中。

第八章. 与机器人对话

哇,机器人?!真的吗?我会学习如何为娱乐或商业用途构建机器人吗?当然,本章将带你进入使用 Python 的全新机器人世界。

在本章中,我们将涵盖以下内容:

  • 构建一个情绪化的 Telegram 机器人

  • 不同类型的机器人:无状态、有状态和智能型

  • 拥有人工智能的智能机器人

  • 使用机器人自动化业务流程

引言

过去的几十年是数字转型和自动化的时代。如今,大多数企业更倾向于选择线上销售模式,而不是传统的实体销售方式。

网站不仅帮助公司扩大了其影响力,还降低了他们销售产品的成本(没有固定的成本,如租金)。一个响应式的图形用户界面GUI),结合实时技术的力量,使得销售过程变得更加容易;现在高管们可以直接与潜在客户聊天,并引导他们购买产品,从而提高转化率。

随着人工智能(AI)和语言处理技术的进步,企业正在缓慢但稳步地采用对话界面来自动化他们的业务流程。对话用户界面是指具有自由文本的自然语言界面。通过对话界面和自然语言处理技术,企业认为机器可以通过分析上下文来响应某些客户查询。在当今世界,这些机器被称为聊天机器人

在本章中,你将了解不同类型的机器人,学习如何开发简单的聊天机器人,以及了解机器人如何用于自动化业务流程。此外,请注意,在本章中提到机器人时,我们指的是聊天机器人或基于文本的机器人。

机器人是什么?

好的,让我们来看一个简单的例子。假设你想要在下个周末和朋友们的聚会上订购必胜客的披萨。通常情况下,你会去必胜客的网站,花时间寻找你喜欢的某种披萨或特定的配料,然后下单。很多时候,你其实已经知道你想要订购什么;那么真正的问题其实是,为什么还要在必胜客网站上费劲去找呢?

别再担心了!只需登录Facebook并使用 Facebook Messenger 聊天机器人从必胜客购买你需要的东西。不仅如此,聊天机器人还会告诉你必胜客的最新优惠和更新。所以聊天机器人可以给你在最喜欢的社交网络平台上访问网站的同一种体验。看看blog.pizzahut.com/press-center/pizza-hut-announces-new-social-ordering-platform/上的公告,必胜客宣布与 Facebook Messenger 合作。

你可能会说:“是的,我们理解了使用场景,但聊天机器人到底是什么?”

聊天机器人是由规则和人工智能驱动的服务,作为客户,你可以通过聊天(文本)界面与之互动。机器人执行半智能或日常任务,并以软件应用的形式运行。聊天机器人可以提供多种服务,并且可以在FacebookTelegramSlack等社交平台上运行。聊天机器人仍在积极研究中,是一个新兴的计算机科学领域。

机器人是如何工作的?

根据我们迄今为止的讨论,你可能正在想:“这些机器人是如何工作的?它们如何理解人类的语言或情感?它们如何理解上下文?”那么,这就是答案。通常有两种类型的聊天机器人:

  • 基于规则引擎的机器人:这种类型的机器人理解某些词语或命令(可以说是这样的)并且行为非常有限。它非常直接:如果x是输入,那么y应该是输出。它们在存在固定问题集或问题作为查询的情况下非常有用。例如,CNN 聊天机器人可以帮助你获取那一刻的头条新闻,而且你还可以选择询问有关特定主题(如政治商业)的头条新闻。!(太好了!那么我为什么还要去 CNN 网站呢?)看看我从我的 Facebook Messenger 应用中拍摄的关于我与 CNN 聊天机器人交互的一些截图。第一个屏幕要求你点击GET STARTED,当你这样做时,机器人会带你到下一个屏幕,在那里它给你一个查看头条新闻的选项:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_09_0011.jpg

    当你点击TOP STORIES时,它会显示Yahoo新闻,并询问你是否对某些主题感兴趣,例如政治

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_09_0012.jpg

  • 一个基于机器学习的智能机器人:智能机器人利用人工智能和情感分析来理解对话的上下文,并响应语言语义。因此,它们适用于复杂的使用场景,例如购买产品或回答客户支持查询。更重要的是,这些机器人可以从过去的交互中学习。这不是很神奇吗?

注意

情感分析也被称为意见挖掘,旨在从可用的文本中识别和提取主观信息,并确定作者的情绪,同时注意文本的上下文属性。

为什么现在要使用机器人?

你可能会问,“世界已经谈论机器学习有一段时间了,聊天功能也已经存在很长时间了,那么为什么机器人现在变得如此重要?”这是因为以下原因:

  • 使用模式:公司已经发现,用户在聊天平台上花费的时间比在社交媒体平台或网站上更多。因此,企业可以通过聊天平台以更好的方式与用户互动。

  • 成本效益:不需要人力——听起来似乎没有成本!企业正在利用机器人来自动化流程,如客户服务,而不需要人力资源投资。

  • 规模:通过 Facebook 或 Telegram 等作为机器人分发渠道的社交平台,很容易接触到数百万用户。这样,企业可以在不考虑人力成本的情况下,尽可能多地吸引潜在客户。

  • 高效技术:人工智能或自然语言处理(NLP)的增长使得将这些算法插入这些机器人变得更加容易。算法会随着时间的推移而成熟,并将更好地服务于客户。

好的,太棒了!既然我们已经更好地理解了机器人和它们的实用性,那就让我们动手开发自己的机器人吧。

构建一个情绪化的 Telegram 机器人

在我们开始开发机器人之前,我们应该清楚我们的目标:我的机器人将要做什么?我们以创建一个根据用户情绪返回表情的机器人为例。它之所以被称为情绪化机器人,是因为它代表了用户的情绪。听起来像是一个有趣的用例?让我们试试吧!

在这个菜谱中,我们将使用python-telegram-bot (github.com/python-telegram-bot/) 库来开发 Telegram 机器人。所以,让我们首先使用我们最喜欢的工具,即 python pip来安装python-telegram-bot模块:

(bots)chetans-MacBookPro:ch09 Chetan$ pip install python-telegram-bot --upgrade 

Collecting python-telegram-bot 
  Downloading python_telegram_bot-5.1.0-py2.py3-none-any.whl (134kB) 
    100% |████████████████████████████████| 135kB 681kB/s  
Collecting certifi (from python-telegram-bot) 
  Downloading certifi-2016.8.31-py2.py3-none-any.whl (379kB) 
    100% |████████████████████████████████| 380kB 612kB/s  
Collecting future>=0.15.2 (from python-telegram-bot) 
  Downloading future-0.15.2.tar.gz (1.6MB) 
    100% |████████████████████████████████| 1.6MB 251kB/s  
Collecting urllib3>=1.10 (from python-telegram-bot) 
  Downloading urllib3-1.17-py2.py3-none-any.whl (101kB) 
    100% |████████████████████████████████| 102kB 1.2MB/s  
Building wheels for collected packages: future 
  Running setup.py bdist_wheel for future 
  Stored in directory: /Users/chetan/Library/Caches/pip/wheels/11/c5/d2/ad287de27d0f0d646f119dcffb921f4e63df128f28ab0a1bda 
Successfully built future 
Installing collected packages: certifi, future, urllib3, python-telegram-bot 
Successfully installed certifi-2016.8.31 future-0.15.2 python-telegram-bot-5.1.0 urllib3-1.17

我们还安装了emoji (github.com/carpedm20/emoji) 库来处理表情符号,这样我们就可以根据用户的情绪返回适当的表达:

(bots)chetans-MacBookPro:ch09 Chetan$ pip install emoji --upgrade 

Collecting emoji 
  Downloading emoji-0.3.9.tar.gz 
Building wheels for collected packages: emoji 
  Running setup.py bdist_wheel for emoji 
  Stored in directory: /Users/chetan/Library/Caches/pip/wheels/94/fc/67/441fb0ca2ed262d6db44d9ac2dfc953e421f57730004dff44d 
Successfully built emoji 
Installing collected packages: emoji 
Successfully installed emoji-0.3.9

你已经安装了模块吗?太酷了!让我们继续前进。

如何操作…

  1. 要开发自己的机器人,首先在手机上下载 Telegram 应用。注册一个账户并验证你的手机号码。假设你已经这样做,恭喜你!你离创建一个 Telegram 机器人又近了一步。

  2. 现在,你需要做的下一件事是联系另一个名为BotFather的机器人。在你的 Telegram 应用中搜索BotFather并点击它,开始与之对话(或他?)。它看起来是这样的:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_09_004-1.jpg

  3. 一旦你与BotFather开始对话,按照步骤操作,并使用如/newbot/enable等命令来配置你的机器人。仔细遵循步骤,你将创建一个新的机器人。以下截图将指导你完成创建新机器人的过程:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_09_005-1.jpg

  4. 当你创建一个新的机器人时,你会得到一个特定于你的机器人的令牌。请妥善保管并安全地保存它;不要与任何人分享。以下截图显示了BotFather的工作方式和令牌的外观:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_09_006-1.jpg

  5. 很好!所以你已经创建了自己的机器人。但机器人目前还没有功能,也没有做什么引人注目的东西。让我们按照菜谱开头计划的那样,让它做一些酷的事情。创建一个名为bot.py的文件,并将以下代码复制到其中。同时,确保将令牌更改为你的机器人令牌 ID:

            import logging 
            from telegram import InlineKeyboardButton, 
              InlineKeyboardMarkup 
            from telegram.ext import Updater,  
              CommandHandler, CallbackQueryHandler 
            import emoji 
    
            logging.basicConfig(format='%(asctime)s  
              - %(name)s - %(levelname)s - %(message)s', 
            level=logging.INFO) 
    
            def start(bot, update): 
                keyboard = [ 
                    [InlineKeyboardButton("Happy", callback_data='1'), 
                    InlineKeyboardButton("Whatever", callback_data='2')], 
                    [InlineKeyboardButton("Sad", callback_data='3')]] 
    
                reply_markup = InlineKeyboardMarkup(keyboard) 
    
                update.message.reply_text('Hey there!  
                  How do you feel today?', reply_markup=reply_markup) 
    
            def button(bot, update): 
                query = update.callback_query 
            if query.data == "1": 
                    em = emoji.emojize(':smile:', use_aliases=True) 
                    bot.editMessageText(text="Oh wow! %s " % em, 
            chat_id=query.message.chat_id, 
            message_id=query.message.message_id) 
    
            if query.data == "2": 
                    em = emoji.emojize(':expressionless:', use_aliases=True) 
                    bot.editMessageText(text="Does it matter? %s " % em, 
            chat_id=query.message.chat_id, 
            message_id=query.message.message_id) 
    
            if query.data == "3": 
                em = emoji.emojize(':disappointed:', use_aliases=True) 
                bot.editMessageText(text="Oh man! %s " % em, 
            chat_id=query.message.chat_id, 
            message_id=query.message.message_id) 
    
            def help(bot, update): 
                update.message.reply_text("Use /start to test this bot.") 
    
            def error(bot, update, error): 
                logging.warning('Update "%s" caused error "%s"' % (update,
                error)) 
    
            # Create the Updater and pass it your bot's token. 
            updater = Updater('Token') 
    
            updater.dispatcher.add_handler( 
              CommandHandler('start', start)) 
            updater.dispatcher.add_handler( 
              CallbackQueryHandler(button)) 
            updater.dispatcher.add_handler( 
              CommandHandler('help', help)) 
            updater.dispatcher.add_error_handler(error) 
    
            # Start the Bot 
            updater.start_polling() 
    
            # Run the bot until the user presses Ctrl-C  
              or the process receives SIGINT, 
            # SIGTERM or SIGABRT 
            updater.idle() 
    
    
  6. 好的,太棒了!我们现在已经为我们机器人添加了所需的功能,并期待它能良好运行。但我们如何测试它呢?首先,使用以下命令运行 Python 文件:

    python bot.py
    
    
  7. 我们接下来搜索我们的机器人,并与之开始对话。在我的情况下,这个机器人叫做Chetbot,我使用标准的/start命令与之开始了对话:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_09_007-2.jpg

  8. 在前面的截图中,当我开始与我的机器人对话时,它询问我当天的情绪,并给了我三个选项。这三个选项是快乐随便悲伤

  9. 真不错!但当我点击这些选项之一时会发生什么?哇!它会返回我当天的情绪表情符号。太棒了!!如何操作…

  10. 注意,如果我需要再次开始对话,我需要重新输入/start命令来与机器人交谈。在以下截图中,机器人识别了启动命令,并再次询问我的情绪。不错,对吧?https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_09_009-2.jpg

它是如何工作的…

python-telegram-bot模块基于标准的事件驱动哲学。一个机器人可以被视为一个单线程的事件循环,它持续轮询事件。事件循环也与命令处理器(也称为分发器)注册。一旦事件被触发,回调就会处理事件并返回给用户期望的响应。

在前面的代码片段中,我们注册了两个命令处理器:start()help()。当用户与机器人开始对话(/start命令)或请求帮助(/help命令)时,会调用start()方法。

我们还添加了一个带有button()作为回调方法的回调查询处理器;当用户对机器人的选项做出响应时,它会调用这个方法。

因此,最初,机器人正在运行,等待输入。当用户说/start时,请求会被分发到start()方法,该方法反过来会询问用户嗨,你今天感觉怎么样?并展示一个内联键盘,有三个选项:快乐随便悲伤

当用户选择任何一个选项时,会生成一个事件,该事件由button()回调方法处理。回调方法预先加载了数据,根据选择的选项进行操作。根据用户的选择,机器人会向用户发送正确的情绪。通过包含所有表情的emoji库,表情符号被发送回用户。

还有更多…

太棒了!你已经创建了自己的机器人了吗?你能想到其他简单的例子,说明 Telegram 机器人会有用吗?有很多 Python 模块可以用来开发 Telegram 机器人,例如telepot(github.com/nickoala/telepot)或twx.botapi(github.com/datamachine/twx.botapi);两者都很好。你可以使用其中任何一个来让你的机器人启动并运行。为什么不试试看它们能提供什么?

不同的机器人类型

从我们自己构建机器人的自信中汲取力量,让我们更进一步,看看机器人可以如何分类。

在上一个菜谱中我们开发的机器人可能被标记为不智能。这里的“不智能”是指它会质疑用户,并根据选项以表情符号的形式做出回应。但是当用户再次输入/start时,机器人会提出同样的问题。这有帮助吗?

那么设想一下这样的场景:机器人会记住你的之前的选项,并尝试通过一些美好的文章或你可以在城市中去的地点来激励你?只是为了改变你的心情?或者实际上提高你的幸福感?

为了使前面的讨论更有意义,根据实现方式,机器人可以分为三类:

  • 无状态机器人:这些也可以被称为“不记得任何东西”的机器人。它们不持续信息;也就是说,对于它们来说,每一次交互都是一个新会话,并且它们会独立处理每一个问题。例如,一个新闻机器人可以持续提供最新故事的更新,或者总是返回政治类别的头条新闻;然而,如果它不记得对话的状态,它将被视为无状态的,并且不会被认为是有用的。大多数今天构建的机器人都属于这一类别,这也是它们提供的价值非常有限的原因。

  • 有状态机器人:我们在前面讨论了新闻机器人。如果新闻机器人能记住用户感兴趣的新闻类别,并据此推荐更多过去的故事,用户可能会觉得有趣,那么我们现在就进入正题了。这样,我们可以让用户与机器人保持更长时间的互动。

    这种机器人会跟踪用户的身份,并持续记录当前和之前的会话信息。例如,这些机器人可能会存储今天和过去搜索的新闻类别,然后可以推荐与搜索类别匹配的新闻源。

    这样的机器人很有用,但它们并不聪明;它们不理解上下文和语言语义。

  • 智能机器人:智能机器人插入了多个电池。它们使用机器学习,理解语言语义,并可以根据它们拥有的数据构建预测算法。

    让我们以尿布和啤酒的著名例子为例。据说,如果你分析购买模式,啤酒和尿布的购买之间存在高度相关性,这意味着购买尿布的人或多或少肯定会购买啤酒。智能机器人可以持续数据并找出这样的模式,从而对对话产生有意义的见解。让我们再举一个语言语义的例子。想想看“脏得要命”这个短语;现在,脏意味着肮脏,而“要命”是一个非常积极的词。智能机器人将理解这些短语,并能更好地理解用户的上下文。

根据前面的分类,决定我们为特定用例开发哪种类型的机器人就取决于我们了。在需要远比人类互动更人性化、更复杂的场合,例如客户支持,通常需要智能机器人,但想象一下企业通过使用智能机器人可以获得的效率提升。

拥有人工智能的智能机器人

在上一节中了解了不同类型的机器人之后,让我们尝试编写一个使用 Python 中的人工智能和情感分析的机器人。但在那之前,让我们简要了解这两个领域。

人工智能AI)是计算机科学的一个领域,强调创建能够像人类一样反应的机器。本质上,人工智能与能够感知其上下文并采取与内容相关的行动以最大化成功机会的机器相关。例如,机器可以根据某些规则和上下文做出决策,以最大化决策的结果。

情感分析,另一方面,是关于识别和分类一段文本,以确定涉及人员的观点或态度是对产品或事件的正面、中性还是负面。它指的是使用自然语言处理算法进行文本分析并提取内容的主观信息,或情感。

我想,到现在为止,你一定已经开始思考如何将人工智能和情感分析用于我们的机器人以满足各种需求了。在这个菜谱中,让我们构建一个拥有这些技术的智能机器人。

注意

智能机器人可以建立在多种技术之上,如预测智能、人工智能、自然语言处理等;然而,完全取决于你决定使用哪种技术来满足你的目标。此外,机器人不需要在网络上或应用中;它们可以是简单的基于命令行的机器人。一个 Web 用户界面、命令行界面或移动应用可以用作机器人的分发渠道,但这并不是构建机器人的必要条件。

准备工作

要在我们的机器人中包含人工智能,我们将使用一个名为 aiml 的知名 Python 模块。AIML 代表 人工智能标记语言,但它本质上是一个 XML 文件。AIML 是一种 XML,它定义了匹配模式和确定响应的规则。因此,让我们开始安装 aiml 模块:

chetans-MacBookPro:ch09 Chetan$ source bots/bin/activate 
(bots)chetans-MacBookPro:ch09 Chetan$  
(bots)chetans-MacBookPro:ch09 Chetan$ pip install aiml 

Collecting aiml 
Installing collected packages: aiml 
Successfully installed aiml-0.8.6

如何操作…

  1. 作为第一步,我们首先创建 AIML 文件。前往你喜欢的编辑器并创建一个 AIML 文件,就像一个普通的 XML 文件一样,内容如下:

            <aiml version="1.0.1" encoding="UTF-8"> 
            <!-chat.aiml à 
    
              <category> 
                <pattern>HELLO</pattern> 
                <template> 
                    Hi, hello! 
                </template> 
              </category> 
    
              <category> 
                <pattern>WHO ARE *</pattern> 
                <template> 
                  <random> 
                    <li>I'm a bot!</li> 
                    <li>Bad guy!</li> 
                    <li>My name is superman!</li> 
                  </random> 
                </template> 
              </category> 
    
              <category> 
                <pattern>AWESOME *</pattern> 
                <template> 
                    You're nice too! J 
                </template> 
              </category> 
    
            </aiml> 
    
    
  2. 接下来,我们创建一个启动 XML 文件,该文件将加载 AIML 文件;这也会加载我们添加到前面 AIML 文件中的人工智能。让我们称这个文件为 init.xml

            <aiml version="1.0.1" encoding="UTF-8">
                <!-- init.xml -->
    
                <!-- Category is an atomic AIML unit -->
                <category>
    
                    <!-- Pattern to match in user input -->
                    <!-- If user enters "LOAD AIML B" -->
                    <pattern>LOAD AIML B</pattern>
    
                    <!-- Template is the response to the pattern -->
                    <!-- This learn an aiml file -->
                    <template>
                        <learn>chat.aiml</learn>
                        <!-- You can add more aiml files here -->
                        <!--<learn>more_aiml.aiml</learn>-->
                    </template>
    
                </category>
    
            </aiml> 
    
    
  3. 现在,让我们开发运行我们的聊天机器人的 Python 代码。以下代码正好是我们需要的。我们称这个文件为 aibot.py

            import aiml 
    
            # Create the kernel and learn AIML files 
            kernel = aiml.Kernel() 
            kernel.learn("init.xml") 
            kernel.respond("load aiml b") 
    
            # Press CTRL-C to break this loop 
            while True: 
                print kernel.respond(raw_input("Enter your message >>")) 
    
    
  4. 如果我们使用 python aibot.py 命令运行这个机器人,它会显示一个输入屏幕,等待用户的输入。查看以下截图以了解其工作原理:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_09_010-1.jpg

它是如何工作的…

上述 Python 代码模拟了一个基于人工智能的典型机器人。当我们运行 Python 代码时,amil.Kernel() 将加载 AI 内核。

一旦内核被加载,kernel.learn() 将调用启动 xml 文件。当向内核发送 load aiml b 命令时,AIML 规则引擎被加载。

一旦引擎被加载到内核中,我们就可以自由地与机器人聊天。

在前面的截图中,当我们说 你好 时,机器人理解它(来自 chat.aiml 文件)并以配置在 chat.aiml 中的 嗨,你好 响应。

在第二种情况下,当用户询问 你是谁 时,AI 机器人会匹配 WHO ARE * 模式;该模式再次在 chat.aiml 中定义。

如果你观察,WHO ARE * 模式在 chat.aiml 文件中配置为多个响应,因此,在运行时,机器人会随机选择一个响应并返回 我的名字是超人!

使用机器人自动化业务流程

到目前为止,在本章中,你已经学习了什么是机器人,它们是如何构建的,以及一些简单的机器人使用案例。让我们看看我们如何利用我们至今为止的知识来解决杰伊的问题,也许还能更多地了解如何构建机器人。

杰伊是一家著名图书出版公司的市场营销经理,在 MyBooks 公司。他的任务是想出图书推广电子邮件。他觉得他发送的推广电子邮件太通用,没有有效地针对读者。例如,关于 Python 学习路径的电子邮件可能不会鼓励 Java 开发者花钱。他认为,如果他了解受众的兴趣并使他的互动更加相关,他可以做得更好;读者更有可能以这种方式购买书籍。他还觉得很多读者(潜在买家)在 Facebook 上,但出版社目前还没有接触到他们。我们能帮杰伊解决这个问题吗?

准备工作

是的,让我们通过开发一个出色的机器人来帮助 Jay。如果你研究 Jay 的问题,他需要了解受众(在这种情况下,对购买书籍感兴趣的读者)并根据他们的兴趣向他们推荐书籍。因此,我们的机器人应该足够智能,能够从读者那里获取相关信息。

此外,由于读者已经在 Facebook 上,我们可以创建一个 MyBooks Facebook 页面并构建一个 Facebook Messenger 机器人,以便联系读者。让我们看看如何操作。

在我们开始构建机器人之前,让我们安装一些将在这个练习中需要的 Python 模块。我们使用 Python 的pip安装flaskrequests模块:

(bots)chetans-MacBookPro:ch09 Chetan$ pip install flask 

Collecting flask 
  Using cached Flask-0.11.1-py2.py3-none-any.whl 
Collecting click>=2.0 (from flask) 
Collecting itsdangerous>=0.21 (from flask) 
Collecting Werkzeug>=0.7 (from flask) 
  Downloading Werkzeug-0.11.11-py2.py3-none-any.whl (306kB) 
    100% |████████████████████████████████| 307kB 1.4MB/s  
Collecting Jinja2>=2.4 (from flask) 
  Using cached Jinja2-2.8-py2.py3-none-any.whl 
Collecting MarkupSafe (from Jinja2>=2.4->flask) 
Installing collected packages: click, itsdangerous, Werkzeug, MarkupSafe, Jinja2, flask 
Successfully installed Jinja2-2.8 MarkupSafe-0.23 Werkzeug-0.11.11 click-6.6 flask-0.11.1 itsdangerous-0.24 

(bots)chetans-MacBookPro:ch09 Chetan$ pip install requests 

Collecting requests 
  Using cached requests-2.11.1-py2.py3-none-any.whl 
Installing collected packages: requests 
Successfully installed requests-2.11.1

如何操作…

  1. 要开发一个 Facebook Messenger 机器人,首先创建一个 Facebook 账号(谁没有 Facebook 账号呢?)。登录您的账号,并前往www.facebook.com/pages/create/创建一个新的页面。

  2. 在我们的案例中,因为我们正在为 MyBook 公司建立一个页面,我们可以将我们的页面命名为MyBooks,并选择一个合适的组织类型,即媒体/新闻公司。以下是页面的样子:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/B05370_08_new.jpg

  3. 创建 Facebook 页面的第二步是填写 Facebook 请求的其他详细信息,如下面的截图所示。我们为我们的页面提供了一个很好的描述:“获取我们最新书籍的更新”:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_09_012-1.jpg

  4. 我们已经为 Jay 填写了所有详细信息,MyBooks 的 Facebook 页面已经准备好,看起来很棒:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_09_013-1.jpg

    现在,这是一个良好的开始。读者们将开始关注这个页面,但我们真的需要添加让我们的读者通过 Facebook 页面进行交流的能力;我们通过 Facebook Messenger 机器人来实现这一点。所以,让我们继续工作,解决我们解决方案的这个方面。

  5. 要创建一个 Facebook Messenger 机器人,我们需要一个 Facebook 应用。我们将通过导航到developers.facebook.com/quickstarts/?platform=web并点击跳过并创建 App ID来创建一个应用,如下面的截图所示:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_09_014-1.jpg

  6. 我们现在填写所需的详细信息,并点击创建 App ID按钮来创建应用。以下截图显示了创建应用时添加的详细信息:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_09_015-1.jpg

  7. 一旦我们填写了详细信息并点击创建 App ID,就会为我们创建一个新的应用。这个 Facebook 应用是为我们的机器人准备的。我们在页面的右上角可以看到应用 ID,但要与机器人关联,我们需要向下滚动并点击开始部分中的Get Startedhttps://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_09_016-1.jpg

  8. 为了让机器人访问 Messenger,我们将生成页面访问令牌,如下面的截图所示。

    小贴士

    请妥善保管此令牌,不要与任何人分享。

  9. 此令牌用于响应从MyBooks Facebook 页面与机器人开始对话的读者:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_09_017-1.jpg

  10. 好的,还有最后一件事待办。我们还需要接收读者的消息;只有在这种情况下,我们才能回应他们。为此,我们进入Webhooks部分并添加一些设置:

    • 回调 URL:这是我们的服务器链接,我们通过 Facebook 页面从读者那里接收消息

    • 验证令牌:这里可以使用任何一组字符,比如token

    • 订阅字段:我们选择消息作为我们机器人的订阅字段(这可以在以后更改)

    如你所见,我们需要有一个回调 URL。这将由 Facebook 用来验证我们的回调 URL 是否设置正确。为此,我们创建一个 Flask 服务器并配置用于回调 URL的路由。以下代码创建了一个名为/bot的路由,用作回调 URL进行验证:

            from flask import Flask 
            from flask import request 
            import sys, requests, json, os 
    
            app = Flask(__name__) 
    
            @app.route("/bot/", methods=['GET', 'POST']) 
            def hello(): 
            if request.method == 'GET': 
            return request.args.get('hub.challenge') 
    
    

    如果我们在 5000 端口运行服务器,并且也使用ngrok在相同端口运行,我们将得到一个可以放置在Webhook设置中的回调 URL。这是回调 URL 的样式:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_09_018-1.jpg

    可以通过点击按钮来验证和保存设置,如下面的截图所示:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_09_019-1.jpg

    当我们验证并保存设置时,会向我们的 Flask 服务器发送一个带有hub.challenge代码的GET请求。我们从flask路由返回此代码给 Facebook,并验证Webhook设置:

     /Users/chetan/book/ch09/bots/bin/python  
            /Users/chetan/book/ch09/bookbot.py 
             * Running on http://127.0.0.1:5000/ 
              (Press CTRL+C to quit) 
    
            127.0.0.1 - - [01/Oct/2016 10:17:43] "GET  
            /bot/?hub.mode=subscribe&hub 
            .challenge=1742124657&hub.verify_token= 
            token HTTP/1.1" 200 -
    
    

    为了让机器人正常工作,我们还需要确保 Facebook 页面允许某些事件,如阅读或回显消息。我们在Webhooks部分启用这些设置:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_09_020-1.jpg

  11. 太好了!所以现在我们已经准备好了一个Webhook来接收读者的消息,并且还有一个访问令牌来回应用户。如果你意识到,Webhook将成为我们的机器人服务器!让我们继续让我们的机器人做更智能的事情。以下代码将使我们的机器人完成 Jay 需要的所有伟大事情:

            from flask import Flask 
            from flask import request 
            import requests, json
    
            app = Flask(__name__) 
    
            def send_weburl(payload, recipient_id): 
                headers = { 
                   "Content-Type": "application/json" 
                } 
                token = { 
                   "access_token": 
                   "TOKEN" 
                 } 
    
            if payload == 'Python': 
              data = json.dumps({ 
                "recipient": { 
                  "id": recipient_id 
                }, 
                "message":{ 
                  "attachment":{ 
                    "type":"template", 
                    "payload":{ 
                      "template_type":"generic", 
                      "elements":[ 
                      { 
                        "title":"Learn Python Design Patterns: Chetan
                        Giridhar", 
                        "item_url":"https://www.amazon.com/Learning-Python-
                        Design-Patterns-Second/dp/178588803X", 
                        "image_url":"https://images-na.ssl-images-
                        amazon.com/images/I/51bNOsKpItL._SX404_BO1,
                        204,203,200_.jpg", 
                        "subtitle":"Python Book for software architects and
                        developers", 
                        "buttons":[ 
                        { 
                          "type":"web_url", 
                          "url":"https://www.amazon.com/Learning-Python-
                          Design-Patterns-Second/dp/178588803X", 
                          "title":"Buy", 
                          "webview_height_ratio":"full" 
                        } 
                        ] 
                      } 
                      ] 
                  } 
                } 
                } 
              }) 
    
            if payload == 'Java': 
              data = json.dumps({ 
                "recipient": { 
                  "id": recipient_id 
                }, 
                "message":{ 
                  "attachment":{ 
                  "type":"template", 
                  "payload":{ 
                    "template_type":"generic", 
                    "elements":[ 
                    { 
                      "title":"RESTful Java Patterns and Best 
                      Practices: Bhakti Mehta", 
                      "item_url":"https://www.amazon.com/RESTful-Java-
                      Patterns-Best-Practices/dp/1783287969", 
                      "image_url":"https://images-na.ssl-images-
                      amazon.com/images/I/51YnSP6uqeL._SX403_BO1,
                      204,203,200_.jpg", 
                      "subtitle":"Python Book for software architects and 
                      developers", 
                      "buttons":[ 
                        { 
                          "type":"web_url", 
                          "url":"https://www.amazon.com/RESTful-Java-
                          Patterns-Best-Practices/dp/1783287969", 
                          "title":"Buy", 
                          "webview_height_ratio":"full" 
                        } 
                      ] 
                    } 
                    ] 
                  } 
                  } 
                } 
              }) 
    
            r = requests.post("https://graph.facebook.com/v2.6/me/messages", 
            params=token, headers=headers, data=data) 
    
            def send_postback(recipient_id): 
              headers = { 
                "Content-Type": "application/json" 
              } 
              token = { 
                "access_token": 
                  "TOKEN" 
              } 
    
            data = json.dumps({ 
              "recipient": { 
                "id": recipient_id 
              }, 
              "message": { 
                "attachment": { 
                  "type": "template", 
                  "payload": { 
                    "template_type": "button", 
                    "text": "Hey there, Welcome to MyBooks.  
                    What are you interested in?", 
                    "buttons": [ 
                    { 
                      "type":"postback", 
                      "title":"Java", 
                      "payload":"Java" 
                    }, 
                    { 
                      "type":"postback", 
                      "title":"Python", 
                      "payload":"Python" 
                    } 
                    ] 
                  } 
                } 
              } 
            }) 
    
            r = requests.post("https://graph.facebook.com/v2.6/me/messages", 
            params=token, headers=headers, data=data) 
    
            @app.route("/bot/", methods=['GET', 'POST']) 
            def hello(): 
              print request.data 
            if request.method == 'GET': 
              return request.args.get('hub.challenge') 
    
            data = request.get_json() 
            if data["object"] == "page": 
              for entry in data["entry"]: 
                for messaging_event in entry["messaging"]: 
                  if messaging_event.get("postback"): 
                    sender_id = messaging_event["sender"]["id"] 
                    payload = messaging_event["postback"]["payload"] 
                    send_weburl(payload, sender_id) 
    
                  if messaging_event.get("message"):  # readers send us a
                  message 
                    sender_id = messaging_event["sender"]["id"] 
                    send_postback(sender_id) 
    
                    return "ok", 200 
    
                   if __name__ == "__main__": 
                     app.run() 
    
    
  12. 我们运行前面的 Flask 服务器来激活我们的机器人。现在,让我们通过导航到 Facebook 页面来看看机器人是如何工作的。在 Facebook 页面上,如果我们点击消息,我们就可以在MyBooks页面上与机器人开始聊天:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_09_021.jpg

  13. 让我们用简单的Hi消息开始与机器人的对话。机器人会向我们提出关于我们是否需要有关 Python 或 Java 书籍信息的问题。太棒了!!如何操作…

  14. 现在,如果我们点击Python,机器人会推荐一本用 Python 编写的架构书籍,并鼓励读者购买。当读者点击Java时,也会发生这种情况。请看以下截图:https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_09_023.jpg

    以下截图演示了一个 Java 示例,其中当用户选择Java时,推荐了RESTful Java Patterns and Best Practices这本书:

    https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/autoit-rcp/img/image_09_024.jpg

  15. 太酷了,对吧?这正是 Jay 所需要的。所以,当读者到达MyBooks页面时,他们可以与机器人交谈,机器人根据他们的兴趣推荐一本书。由于机器人提出的建议与读者的相关性比通用促销电子邮件要高得多,读者购买书籍的可能性也更高。太棒了!

它是如何工作的…

我们首先为 Jay 的出版社创建了一个 Facebook 页面:MyBooks。然后我们将一个 Facebook Messenger 机器人与这个页面关联起来,并获得了访问令牌,以便向与机器人聊天的读者发送消息。我们还设置了Webhooks,以便我们的机器人能够接收读者的消息,并使用访问令牌向他们发送消息。在这里,Webhook是机器人的大脑。

当读者到达MyBooks页面时,他们点击消息传递者与机器人开始对话。当他或她说Hi时,HTTP 的POST请求会发送到Webhook https://2d7d823f.ngrok.io/bot/,并带有消息。

机器人读取读者的消息,并向读者发送带有postback选项的通用模板消息。机器人使用 Facebook 的 Graph APIs 发送此消息。

注意

Facebook 有模板消息用于发送postback消息、按钮、图片、URL 和音频/视频媒体文件。

当读者选择Python时,机器人收到这条消息,并根据有效载荷返回书籍的图片以及 URL,以便用户购买。用户然后可以点击购买按钮,进入书籍的 URL 并从那里购买书籍,这正是 Jay 所希望的!

更多…

在本章中,我们基于 CLI、Web UI 和移动应用构建机器人。这些机器人可以驻留在其他聊天系统中,例如拥有良好 API 集的 Slack。你可能想尝试编写一个。如果你写了,请把链接发给我;我很乐意尝试它们。

注意

你可以通过 Twitter 联系我,或者直接给我发消息,我会回复你。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值