Skip to content

Latest commit

 

History

History
813 lines (548 loc) · 42.8 KB

File metadata and controls

813 lines (548 loc) · 42.8 KB

九、为什么不让你的营销活动自动化?

在本章中,我们将介绍与营销活动相关的以下食谱:

  • 发现机会
  • 创建个性化优惠券代码
  • 通过客户的首选渠道向客户发送通知
  • 准备销售信息
  • 生成销售报告

介绍

在本章中,我们将创建一个完整的营销活动,通过我们将要采取的每一个自动步骤。我们将在一个需要不同步骤的项目中利用本书中的所有概念和配方

让我们举个例子。对于我们的项目,我们公司希望建立一个营销活动来提高参与度和销售。非常值得称赞的努力。为此,我们可以将行动分为几个步骤:

  1. 我们希望发现发起活动的最佳时机,因此我们将收到来自不同来源的关于关键字的通知,这些关键字将帮助我们做出明智的决定

  2. 该活动将包括生成发送给潜在客户的单个代码

  3. 这些代码的一部分将通过用户首选的频道、短信或电子邮件直接发送给用户

  4. 为了监控活动的结果,将编辑销售信息并生成销售报告

本章将介绍这些步骤中的每一步,并根据本书中介绍的模块和技术提出一个组合解决方案。

While these examples have been created with real-life requirements in mind, take into account that your specific environment will always surprise you. Don't be afraid to experiment, tweak, and improve your system as you learn more about it. Iterating is the way to create great systems.

让我们开始吧!

发现机会

在本食谱中,我们介绍了一个营销活动,该活动分为几个步骤:

  1. 发现发起活动的最佳时机
  2. 生成要发送给潜在客户的单个代码
  3. 通过用户首选的频道、短信或电子邮件将代码直接发送给用户
  4. 整理活动结果并生成销售报告,并对结果进行分析

此食谱显示了活动的第一步。

我们的第一阶段是确定发起活动的最佳时间。为此,我们将监控新闻网站列表,搜索包含我们定义的关键字之一的新闻。任何与这些关键字匹配的文章都将添加到将通过电子邮件发送的报告中。

准备

在这个配方中,我们将使用前面在书中介绍的几个外部模块,deloreanrequestsBeautifulSoup。我们需要将它们添加到我们的虚拟环境中(如果还没有):

$ echo "delorean==1.0.0" >> requirements.txt
$ echo "requests==2.18.3" >> requirements.txt
$ echo "beautifulsoup4==4.6.0" >> requirements.txt
$ echo "feedparser==5.2.1" >> requirements.txt
$ echo "jinja2==2.10" >> requirements.txt
$ echo "mistune==0.8.3" >> requirements.txt
$ pip install -r requirements.txt

您需要制作一个 RSS 提要列表,我们将从中放置数据

In our example, we use the following feeds, which are all technology feeds in well-known news sites:  http://feeds.reuters.com/reuters/technologyNews http://rss.nytimes.com/services/xml/rss/nyt/Technology.xml http://feeds.bbci.co.uk/news/science_and_environment/rss.xml

从 GitHub 的下载将执行操作的search_keywords.py脚本 https://github.com/PacktPublishing/Python-Automation-Cookbook/blob/master/Chapter09/search_keywords.py 。 您还需要下载电子邮件模板,可在找到 https://github.com/PacktPublishing/Python-Automation-Cookbook/blob/master/Chapter09/email_styling.htmlhttps://github.com/PacktPublishing/Python-Automation-Cookbook/blob/master/Chapter09/email_template.md 。 在中有一个配置模板 https://github.com/PacktPublishing/Python-Automation-Cookbook/blob/master/Chapter09/config-opportunity.ini

您需要电子邮件服务的有效用户名和密码。查看第 8 章关于沟通渠道的中的发送个人邮件配方。

怎么做。。。

  1. 创建一个config-opportunity.ini文件,文件格式如下。请记住填写您的详细信息:
[SEARCH]
keywords = keyword, keyword
feeds = feed, feed

[EMAIL]
user = <YOUR EMAIL USERNAME>
password = <YOUR EMAIL PASSWORD>
from = <EMAIL ADDRESS FROM>
to = <EMAIL ADDRESS TO>

您可以使用 GitHub 的模板https://github.com/PacktPublishing/Python-Automation-Cookbook/blob/master/Chapter09/config-opportunity.ini 搜索关键字cpu和一些测试提要。请记住在EMAIL字段中填写您自己的帐户详细信息。

*2. 调用脚本生成电子邮件和报告:

$ python search_keywords.py config-opportunity.ini
  1. 查看to电子邮件,您将收到一份报告,其中包含找到的文章。它应该类似于这样:

它是如何工作的。。。

在步骤 1 中为脚本创建了正确的配置之后,在步骤 2 中通过调用search_keywords.py完成 web 抓取并发送带有结果的电子邮件。

让我们看一看{ To.T0}脚本。代码分为以下几部分:

  • IMPORTS部分提供了以后使用的所有 Python 模块。它还定义了EmailConfig namedtuple以帮助处理电子邮件参数
  • READ TEMPLATES检索电子邮件模板并将其存储在EMAIL_TEMPLATEEMAIL_STYLING常量中,以备日后使用。
  • __main__块通过获取配置参数、解析配置文件,然后调用 main 函数来启动该过程。
  • main功能结合了其他功能。首先,它检索文章,然后获取正文并发送电子邮件。
  • get_articles浏览所有提要,丢弃任何超过一周的文章,检索每一篇文章,并在关键词上搜索匹配项。将返回所有匹配的文章,包括有关链接的信息和摘要。
  • compose_email_body使用邮件模板编译邮件正文。请注意,模板处于标记状态,它被解析为 HTML,以提供纯文本和 HTML 中相同的信息。
  • send_email获取身体信息,以及用户名/密码等所需信息,最后发送电子邮件。

还有更多。。。

从不同来源检索信息的主要挑战之一是在所有情况下解析文本。某些提要可能以不同的格式返回信息。

例如,在我们的示例中,您可以看到 Reuters 提要摘要包含在生成的电子邮件中呈现的 HTML 信息。如果您有这种问题,您可能需要进一步处理返回的数据,直到它变得一致。这在很大程度上取决于结果报告的预期质量

When developing automatic tasks, especially when dealing with multiple input sources, expect to spend a lot of time cleaning the input in a way that's consistent. But, on the other hand, find a balance and keep in mind the final recipient. If the email is to be received, for example, by yourself or an understanding teammate, you can be a little bit more permissive than in the case of an important client.

另一种可能是增加比赛的复杂性。在这个配方中,检查是通过一个简单的in来完成的,但请记住第 1 章、*中的所有技术,让我们开始我们的自动化旅程,*包括所有正则表达式功能,都可以供您使用。

This script is automatable through a cron job as described in Chapter 2, Automating Tasks Made Easy. Try to run it every week!

另见

  • 第一章添加命令行参数的配方让我们开始我们的自动化之旅
  • 第一章中介绍正则表达式的配方让我们开始我们的自动化之旅
  • 第二章中的准备任务配方使任务自动化变得简单
  • 第 2 章中的设置 cron 作业**使任务自动化变得容易
  • 第 3 章中的解析 HTML构建您的第一个网页抓取应用
  • 第 3 章中的抓取网页配方构建您的第一个网页抓取应用
  • 订阅第 3 章中的饲料配方,构建您的第一个网页抓取应用
  • 第 8 章处理沟通渠道中的发送个人邮件配方

创建个性化优惠券代码

在本章中,我们将介绍分为以下步骤的营销活动:

  1. 发现发起活动的最佳时机
  2. 生成要发送给潜在客户的单个代码
  3. 通过用户首选的频道、短信或电子邮件将代码直接发送给用户
  4. 收集活动的结果
  5. 生成带有结果分析的销售报告

此食谱显示了活动的第 2 步。

检测到商机后,我们决定为所有客户生成活动。为了指导促销并避免重复,我们将生成 100 万张独特优惠券,分为三批:

  • 一半的代码将在营销活动中打印和分发
  • 如果活动达到某些目标,将保留 300000 代码供以后使用
  • 剩下的 20 万将通过短信和电子邮件发送给客户,我们将在后面看到

这些优惠券可以在在线系统中兑换。我们的任务是生成适当的代码,这些代码应满足以下要求:

  • 代码必须是唯一的
  • 代码需要可打印且易于阅读,因为一些客户将通过电话听写这些代码
  • 在检查代码之前,应该有一种快速丢弃代码的方法(避免垃圾邮件攻击)
  • 代码应以 CSV 格式显示,以便打印

准备

从 GitHub 的下载create_personalised_coupons.py脚本,该脚本将以 CSV 文件的形式生成优惠券 https://github.com/PacktPublishing/Python-Automation-Cookbook/blob/master/Chapter09/create_personalised_coupons.py

怎么做。。。

  1. 调用create_personalised_coupons.py脚本。这将需要一到两分钟运行,这取决于您的计算机速度。它将在屏幕上显示生成的代码:
$ python create_personalised_coupons.py
Code: HWLF-P9J9E-U3
Code: EAUE-FRCWR-WM
Code: PMW7-P39MP-KT
...
  1. 检查是否创建了三个 CSV 文件,代码分别为codes_batch_1.csvcodes_batch_2.csvcodes_batch_3.csv,每个文件的代码数量正确:
$ wc -l codes_batch_*.csv
  500000 codes_batch_1.csv
  300000 codes_batch_2.csv
  200000 codes_batch_3.csv
 1000000 total
  1. 检查每个批处理文件是否包含唯一代码。您的代码将是唯一的,并且与此处显示的代码不同:
$ head codes_batch_2.csv
9J9F-M33YH-YR
7WLP-LTJUP-PV
WHFU-THW7R-T9
...

它是如何工作的。。。

步骤 1 调用生成所有代码的脚本,步骤 2 检查结果是否正确。步骤 3 显示了代码的存储格式。让我们分析一下create_personalised_coupons.py脚本。

总之,它具有以下结构:

# IMPORTS

# FUNCTIONS
def random_code(digits)
def checksum(code1, code2)
def check_code(code)
def generate_code()

# SET UP TASK

# GENERATE CODES

# CREATE AND SAVE BATCHES

不同的函数一起工作以创建代码。random_code 生成取自CHARACTERS的随机字母和数字的组合。此字符串包含所有可供选择的有效字符。

The selection of characters is defined as symbols that are easy to print and not mistake for each other. For example, it will be difficult to distinguish between a letter O and the number 0, or the number 1 and the letter I, depending on the font. This may depend on the specifics, so check printing tests if necessary to tailor the characters. But avoid using all letter and numbers, as it may cause confusion. Increase the length of the codes if necessary.

checksum函数根据两个代码生成一个从这两个代码派生的额外数字。这个过程被称为散列,它是计算中的一个众所周知的过程,特别是对于密码学。

The basic function of hashing is to produce an output from an input that is smaller and is not reversible, meaning it's very difficult to guess unless the input is known. Hashing has a lot of common applications in computing, normally under the hood. For example, Python dictionaries make extensive use of hashing.

在我们的配方中,我们将使用 SHA256,这是 Pythonhashlib模块中包含的一种众所周知的快速哈希算法:

def checksum(code1, code2):
    m = hashlib.sha256()
    m.update(code1.encode())
    m.update(code2.encode())
    checksum = int(m.hexdigest()[:2], base=16)
    digit = CHARACTERS[checksum % len(CHARACTERS)]
    return digit

两个代码都被添加为输入,产生的哈希值的两个十六进制数字被应用于CHARACTERS以获得一个可用字符。这些数字被转换成一个数字(因为它们在基数 16 中),我们应用modulo操作符以确保获得一个可用字符。

此校验和的目的是能够快速检查代码是否正确,并丢弃可能的垃圾邮件。我们可以在代码上再次生成该操作,以查看校验和是否相同。请注意,这不是加密散列,因为在操作的任何一点上都不需要保密。考虑到这个特定的用例,这种(低)级别的安全性对于我们的目的来说可能是好的。

Cryptography is a much bigger theme and ensuring that security is strong can be difficult. The main strategy in cryptography involving hashing is probably to store just the hash to avoid storing passwords in a readable format. You can read a quick intro to that here: https://crackstation.net/hashing-security.htm.

然后,generate_code函数生成一个随机码,由四位数字、五位数字和两位校验和数字组成,用破折号除以。第一个数字是按顺序生成的前九个数字(四个然后五个),第二个数字是按顺序反转的(五个然后四个)。

check_code函数反转过程,如果代码正确,则返回True,否则返回False

基本元素就绪后,脚本首先定义所需的批次,即 500000、300000 和 200000。

所有代码都在同一个池中生成,称为codes。这是为了避免池之间的重复。注意,由于过程的随机性,我们不能排除生成重复代码的可能性,尽管这很小。我们允许最多重试三次以避免生成重复代码。这些代码被添加到集合累加器中,以保证它们的唯一性,并加快检查代码是否已经存在的速度。

sets are another of the places where Python uses hashing under the hood, so it hashes the element to be added and compares it with the hashes of the elements already there. This makes checking in sets very quick.

为了确保流程正确,在生成代码的同时,验证并打印每个代码以显示进度,并允许检查一切是否按预期工作。

最后,将代码分为适当数量的批次,并将每个批次保存在单独的.csv文件中。使用.pop()codes中逐个删除代码,直到batch大小合适:

batch = [(codes.pop(),) for _ in range(batch_size)]

请注意前一行如何使用单个元素创建一批大小合适的行。每一行仍然是一个列表,对于 CSV 文件应该是这样。

然后,创建一个文件,并使用csv.writer将代码存储为行。

作为最终测试,验证剩余的codes为空。

还有更多。。。

在此配方中,流程中使用了直接方法。这与第 2 章准备任务以运行配方中提出的原则背道而驰使任务自动化变得简单。请注意,与这里介绍的任务相比,此脚本旨在运行一次以生成代码,仅此而已。它还使用已定义的常量(如BATCHES)进行配置。

考虑到这是一项独特的任务,设计为只运行一次,花时间将其构建为可重用组件可能不是我们时间的最佳利用。

Over-engineering is definitively possible, and choosing between a pragmatic design and a more future-facing approach may not be easy. Be realistic about maintenance costs and try to find your own balance.

同样地,这个关于校验和的配方设计的目的是提供一种检查代码是否完全是虚构的或看起来合法的最低限度的方法。考虑到代码将根据系统进行检查,这似乎是一种明智的方法,但要注意您的特定用例。

我们的代码空间为22 characters ** 9 digits = 1,207,269,217,792 possible codes,这意味着在生成的一百万个代码中,猜测其中一个的概率非常小。也不太可能两次生成相同的代码,但是我们通过最多三次重试来保护代码。

在开发这种脚本时,这些检查以及检查每个代码是否经过验证以及最终是否没有剩余代码都非常有用。它确保我们朝着正确的方向前进,事情按照计划进行。请注意asserts在某些情况下可能无法执行。

As described in the Python documentation, assert commands are ignored if the Python code is optimized (run with the -O command). See the documentation here https://docs.python.org/3/reference/simple_stmts.html#the-assert-statement. This is normally not done, but can be confusing if that's the case. Avoid depending heavily on asserts.

学习密码学的基础知识并不像你想象的那么困难。有一小部分基本的模式是众所周知的,并且容易学习。一篇好的介绍文章是这篇,https://thebestvpn.com/cryptography/ 。Python 还集成了大量加密函数;参见中的文档 https://docs.python.org/3/library/crypto.html 。最好的方法是找到一本好书,并且知道,虽然这是一门很难真正掌握的学科,但它绝对是平易近人的。

另见

  • 第 1 章引入正则表达式的配方,让我们开始我们的自动化之旅
  • 第 4 章中的读取 CSV 文件配方搜索和读取本地文件

通过客户的首选渠道向客户发送通知

在本章中,我们将介绍分为几个步骤的营销活动:

  1. 发现发起活动的最佳时机
  2. 生成要发送给潜在客户的单个代码
  3. 通过用户首选的频道、短信或电子邮件将代码直接发送给用户
  4. 收集活动的结果
  5. 生成带有结果分析的销售报告

此食谱显示了活动的第 3 步。

一旦我们为直接营销创建了代码,我们就需要将其分发给我们的客户。

对于该配方,从 CSV 文件输入所有客户及其首选联系方式的信息,我们将使用先前生成的代码填充该文件,然后通过适当的方法发送通知,该方法将包括促销代码。

准备

在本配方中,我们将使用已经介绍的几个模块-deloreanrequeststwilio。我们需要将它们添加到我们的虚拟环境中(如果还没有):

$ echo "delorean==1.0.0" >> requirements.txt
$ echo "requests==2.18.3" >> requirements.txt
$ echo "twilio==6.16.3" >> requirements.txt
$ pip install -r requirements.txt

我们需要为要使用的服务、Mailgun 和 Twilio 定义一个config-channel.ini文件,该文件的模板可以在 GitHub 中找到:https://github.com/PacktPublishing/Python-Automation-Cookbook/blob/master/Chapter09/config-channel.ini

For information on how to obtain the credentials, refer to the Sending notifications via emails and Producing SMS recipes Chapter 8Dealing with Communication Channels

该文件具有以下格式:

[MAILGUN]
KEY = <YOUR KEY>
DOMAIN = <YOUR DOMAIN>
FROM = <YOUR FROM EMAIL>
[TWILIO]
ACCOUNT_SID = <YOUR SID>
AUTH_TOKEN = <YOUR TOKEN>
FROM = <FROM TWILIO PHONE NUMBER>

对于所有目标联系人的描述,我们需要生成以下格式的 CSV 文件notifications.csv

| 名称 | 接触法 | 目标 | 地位 | 密码 | 时间戳 | | 约翰·史密斯 | 电话 | +1-555-12345678 | NOT-SENT | | | | 保罗·史密斯 | 电子邮件 | [email protected] | NOT-SENT | | | | … | | | | | |

注意Code列为空,所有状态应为NOT-SENT或空。

If you are using a test account in Twilio and Mailgun, be aware of its limitations. For example, Twilio only allows you to send messages to authenticated phone numbers. You can create a small CSV with only two or three contacts to test the script.

要使用的优惠券代码应在 CSV 文件中准备好。您可以使用create_personalised_coupons.py脚本生成多个批次,可在 GitHub 的中找到 https://github.com/PacktPublishing/Python-Automation-Cookbook/blob/master/Chapter09/create_personalised_coupons.py

从 GitHub 的下载要使用的脚本send_notifications.pyhttps://github.com/PacktPublishing/Python-Automation-Cookbook/blob/master/Chapter09/send_notifications.py

怎么做。。。

  1. 运行send_notifications.py查看其选项和用法:
$ python send_notifications.py --help
usage: send_notifications.py [-h] [-c CODES] [--config CONFIG_FILE] notif_file

positional arguments:
  notif_file notifications file

optional arguments:
  -h, --help show this help message and exit
  -c CODES, --codes CODES
                        Optional file with codes. If present, the file will be
                        populated with codes. No codes will be sent
  --config CONFIG_FILE config file (default config.ini)
  1. 将代码添加到notifications.csv文件中:
$ python send_notifications.py --config config-channel.ini notifications.csv -c codes_batch_3.csv 
$ head notifications.csv
Name,Contact Method,Target,Status,Code,Timestamp
John Smith,PHONE,+1-555-12345678,NOT-SENT,CFXK-U37JN-TM,
Paul Smith,EMAIL,paul.smith@test.com,NOT-SENT,HJGX-M97WE-9Y,
...
  1. 最后,发送通知:
$ python send_notifications.py --config config-channel.ini notifications.csv
$ head notifications.csv
Name,Contact Method,Target,Status,Code,Timestamp
John Smith,PHONE,+1-555-12345678,SENT,CFXK-U37JN-TM,2018-08-25T13:08:15.908986+00:00
Paul Smith,EMAIL,paul.smith@test.com,SENT,HJGX-M97WE-9Y,2018-08-25T13:08:16.980951+00:00
...
  1. 检查电子邮件和电话,确认收到了信息。

它是如何工作的。。。

步骤 1 显示了脚本的使用。一般的想法是多次调用它,第一次用代码填充,第二次发送消息。如果出现错误,可以再次执行脚本,并且只重试以前未发送的消息。

notifications.csv文件获取将在步骤 2 中注入的代码。代码最终在步骤 3 中发送。

让我们分析一下send_notifications.py的代码。此处仅显示最相关的位:

# IMPORTS

def send_phone_notification(...):
def send_email_notification(...):
def send_notification(...):

def save_file(...):
def main(...):

if __name__ == '__main__':
    # Parse arguments and prepare configuration
    ...

主函数逐行遍历文件,并分析在每种情况下要执行的操作。如果条目为SENT,则跳过该条目。如果它没有代码,它会尝试填充它。如果它试图发送它,它将在发送或试图发送时将时间戳附加到记录上。

对于每个条目,整个文件再次保存在名为save_file的文件中。请注意,文件光标是如何定位在文件的开头、写入文件,然后刷新到磁盘的。这使文件在每次输入操作时被覆盖,而无需再次关闭和打开文件。

Why write the whole file for each entry? This is to allow you to retry. If one of the entries produces an unexpected error or a timeout, or even if there's a general failure, all the progress and previous codes will be marked as SENT already, and not sent a second time. This means the operation can be retried needed. For a huge number of entries, this is a good way of ensuring that a problem in the middle of the process doesn't make us resend messages to our customers.

对于要发送的每个代码,send_notification函数决定调用send_phone_notificationsend_email_notification。在这两种情况下,它都会附加当前时间。

如果两个send函数无法发送消息,则返回错误。这允许您在生成的notifications.csv中标记它,然后重试。

The notifications.csv file can also be changed manually. For example, imagine there's a typo in an email and that's the reason for the error. It can be changed and retried.

send_email_notification基于 Mailgun 接口发送消息。更多信息请参考第 8 章中的通过电子邮件发送通知配方处理沟通渠道。请注意,此处发送的电子邮件仅为文本。

send_phone_notification基于 Twilio 接口发送消息,详见第 8 章处理通信通道制作短信配方。

还有更多。。。

时间戳的格式是特意用 ISO 格式编写的,因为它是一种可解析的格式。这意味着我们可以用一种简单的方式取回一个合适的对象,如下所示:

>>> import datetime
>>> timestamp = datetime.datetime.now(datetime.timezone.utc).isoformat()
>>> timestamp
'2018-08-25T14:13:53.772815+00:00'
>>> datetime.datetime.fromisoformat(timestamp)
datetime.datetime(2018, 9, 11, 21, 5, 41, 979567, tzinfo=datetime.timezone.utc)

这允许您轻松地来回解析时间戳。

ISO 8601 time format is well supported in most programming languages and very precisely defines the time, as it includes the time zone. It is an excellent choice for recording times, if you can use it.

send_notification中用于路由通知的策略很有趣:

# Route each of the notifications
METHOD = {
    'PHONE': send_phone_notification,
    'EMAIL': send_email_notification,
}
try:
    method = METHOD[entry['Contact Method']]
    result = method(entry, config)
except KeyError:
    result = 'INVALID_METHOD'

METHOD字典将每个可能的Contact Method分配给具有相同定义的函数,同时接受条目和配置。

然后,根据特定的方法,从字典中检索并调用该函数。注意,method变量包含要调用的正确函数。

This acts in a similar way to the switch operation that is available in other programming languages. It is also possible to achieve this through if…else blocks. For simple cases like this code, the dictionary method makes the code very readable.

invalid_method功能用作默认值。如果Contact Method不是可用的PHONEEMAIL之一,则会引发并捕获KeyError,并将结果定义为INVALID METHOD

另见

  • 第 8 章处理沟通渠道中的通过电子邮件发送通知配方
  • 第 8 章处理通信信道中的制作短信配方

准备销售信息

在本章中,我们将介绍分为几个步骤的营销活动:

  1. 发现发起活动的最佳时机
  2. 生成要发送给潜在客户的单个代码
  3. 通过用户首选的频道、短信或电子邮件将代码直接发送给用户
  4. 收集活动的结果
  5. 生成带有结果分析的销售报告

此食谱显示了活动的第 4 步。

在将信息发送给用户之后,我们需要从商店收集销售日志,以监控其进展情况以及活动的影响有多大。

销售日志作为每个相关商店的单独文件进行报告,因此在本食谱中,我们将看到如何将所有信息聚合到电子表格中,以便能够将信息作为一个整体进行处理。

准备

对于此配方,我们需要安装以下模块:

$ echo "openpyxl==2.5.4" >> requirements.txt
$ echo "parse==1.8.2" >> requirements.txt
$ echo "delorean==1.0.0" >> requirements.txt
$ pip install -r requirements.txt

我们可以从 GitHub 的获取该配方的测试结构和测试日志 https://github.com/PacktPublishing/Python-Automation-Cookbook/tree/master/Chapter09/sales 。请下载完整的sales目录,其中包含大量测试日志。为了显示结构,我们将使用tree命令(http://mama.indstate.edu/users/ice/tree/ ),默认在 Linux 中安装,可以在 macOs(中使用brew安装 https://brew.sh/ 。您也可以使用图形工具检查目录。

我们还需要sale_log.py模块和parse_sales_log.py脚本,可在 GitHub 的上找到 https://github.com/PacktPublishing/Python-Automation-Cookbook/blob/master/Chapter09/parse_sales_log.py

怎么做。。。

  1. 检查sales目录的结构。每个子目录表示已提交该期间销售日志的店铺:
$ tree sales
sales
├── 345
│   └── logs.txt
├── 438
│   ├── logs_1.txt
│   ├── logs_2.txt
│   ├── logs_3.txt
│   └── logs_4.txt
└── 656
 └── logs.txt
  1. 检查日志文件:
$ head sales/438/logs_1.txt
[2018-08-27 21:05:55+00:00] - SALE - PRODUCT: 12346 - PRICE: $02.99 - NAME: Single item - DISCOUNT: 0%
[2018-08-27 22:05:55+00:00] - SALE - PRODUCT: 12345 - PRICE: $07.99 - NAME: Family pack - DISCOUNT: 20%
...
  1. 调用parse_sales_log.py脚本生成存储库:
$ python parse_sales_log.py sales -o report.xlsx
  1. 检查生成的 Excel 结果,report.xlsx

它是如何工作的。。。

步骤 1 和 2 显示了数据的结构。步骤 3 调用parse_sales_log.py 读取所有日志文件并对其进行解析,然后将其存储在 Excel 电子表格中。电子表格的内容将在步骤 4 中显示。

让我们看看parse_sales_log.py是如何构造的:

# IMPORTS
from sale_log import SaleLog

def get_logs_from_file(shop, log_filename):
    with open(log_filename) as logfile:
        logs = [SaleLog.parse(shop=shop, text_log=log)
                for log in logfile]
    return logs

def main(log_dir, output_filename):
    logs = []
    for dirpath, dirnames, filenames in os.walk(log_dir):
        for filename in filenames:
            # The shop is the last directory
            shop = os.path.basename(dirpath)
            fullpath = os.path.join(dirpath, filename)
            logs.extend(get_logs_from_file(shop, fullpath))

    # Create and save the Excel sheet
    xlsfile = openpyxl.Workbook()
    sheet = xlsfile['Sheet']
    sheet.append(SaleLog.row_header())
    for log in logs:
        sheet.append(log.row())
    xlsfile.save(output_filename)

if __name__ == '__main__':
  # PARSE COMMAND LINE ARGUMENTS AND CALL main()

命令行参数在第 1 章中解释,让我们开始我们的自动化之旅。注意,进口包括SaleLog

主功能遍历整个目录,通过os.walk抓取所有文件。您可以在第二章中获得更多关于os.walk的信息,使任务自动化变得容易。然后将每个文件传递给get_logs_from_file解析其日志,并将其添加到全局logs列表中。

请注意,特定的店铺存储在最后一个子目录中,因此使用os.path.basename进行提取。

完成日志列表后,将使用openpyxl模块创建新的 Excel 工作表。SaleLog模块有.row_header方法添加第一行,然后使用.row将所有日志转换成行格式。最后,保存文件。

为了解析日志,我们创建了一个名为sale_log.py的模块。这将抽象分析和处理一行。其中大部分都很简单,并且正确地构造了每个不同的参数,但是解析方法需要注意:

    @classmethod
    def parse(cls, shop, text_log):
        '''
        Parse from a text log with the format
        ...
        to a SaleLog object
        '''
        def price(string):
            return Decimal(string)

        def isodate(string):
            return delorean.parse(string)

        FORMAT = ('[{timestamp:isodate}] - SALE - PRODUCT: {product:d} '
                  '- PRICE: ${price:price} - NAME: {name:D} '
                  '- DISCOUNT: {discount:d}%')

        formats = {'price': price, 'isodate': isodate}
        result = parse.parse(FORMAT, text_log, formats)

        return cls(timestamp=result['timestamp'],
                   product_id=result['product'],
                   price=result['price'],
                   name=result['name'],
                   discount=result['discount'],
                   shop=shop)

sale_log.py类方法,表示可以通过调用SaleLog.parse来使用,返回类的新元素。

Classmethodsare called with a first argument that stores the class, instead of the object normally stored in self. The convention is to use cls to represent it. Calling cls(...)  at the end is equivalent to SaleFormat(...), so it calls the __init__ method.

该方法使用parse模块从模板中检索值。注意两个元素,timestampprice是如何进行自定义解析的。delorean模块帮助我们解析日期,最好将价格描述为Decimal,以保持正确的分辨率。自定义过滤器应用于formats参数。

还有更多。。。

Decimal类型在这里的 Python 文档中有详细描述:https://docs.python.org/3/library/decimal.html

完整的openpyxl可以在这里找到:https://openpyxl.readthedocs.io/en/stable/ 。此外,查看第 6 章电子表格乐趣,了解更多关于如何使用该模块的示例。

完整的parse文档可在此处找到:https://github.com/r1chardj0n3s/parse第一章让我们开始我们的自动化之旅,也更详细地描述了这个模块。

另见

  • 使用第三方工具解析第一章中的配方让我们开始我们的自动化之旅
  • 第 4 章中的抓取和搜索目录配方搜索和读取本地文件
  • 第 4 章中的读取文本文件配方搜索和读取本地文件
  • 更新第 6 章中的 Excel 电子表格配方,电子表格乐趣

生成销售报告

在本章中,我们将介绍分为几个步骤的营销活动:

  1. 发现发起活动的最佳时机
  2. 生成要发送给潜在客户的单个代码
  3. 通过用户首选的频道、短信或电子邮件将代码直接发送给用户
  4. 收集活动的结果
  5. 生成带有结果分析的销售报告

此食谱显示了活动的第 5 步。

作为最后一步,有关每个销售的所有信息将聚合并显示在销售报告中。

在本食谱中,我们将了解如何利用从电子表格中读取数据、创建 PDF 和生成图表来自动生成综合报告,以便分析我们活动的绩效。

准备

在此配方中,我们将需要虚拟环境中的以下模块:

$ echo "openpyxl==2.5.4" >> requirements.txt
$ echo "fpdf==1.7.2" >> requirements.txt
$ echo "delorean==1.0.0" >> requirements.txt
$ echo "PyPDF2==1.26.0" >> requirements.txt
$ echo "matplotlib==2.2.2" >> requirements.txt
$ pip install -r requirements.txt

我们需要在 GitHub 的提供sale_log.py模块 https://github.com/PacktPublishing/Python-Automation-Cookbook/blob/master/Chapter09/sale_log.py

The input spreadsheet is generated in the previous recipe, preparing sales information. Check there for more information.

您可以从 GitHub 的下载脚本以生成输入电子表格parse_sales_log.pyhttps://github.com/PacktPublishing/Python-Automation-Cookbook/blob/master/Chapter09/parse_sales_log.py

从 GitHub 的下载原始日志文件 https://github.com/PacktPublishing/Python-Automation-Cookbook/tree/master/Chapter09/sales 。请下载完整的sales目录。

从 GitHub 的下载generate_sales_report.py脚本 https://github.com/PacktPublishing/Python-Automation-Cookbook/blob/master/Chapter09/generate_sales_report.py

怎么做。。。

  1. 检查输入文件和generate_sales_report.py的使用:
$ ls report.xlsx
report.xlsx
$ python generate_sales_report.py --help
usage: generate_sales_report.py [-h] input_file output_file

positional arguments:
  input_file
  output_file

optional arguments:
  -h, --help show this help message and exit
  1. 使用输入文件和输出文件调用generate_sales_report.py脚本:
$ python generate_sales_report.py report.xlsx output.pdf
  1. 检查output.pdf输出文件。它将包含三页,第一页是简要总结,第二页和第三页是按天和按商店列出的销售额图表:

第二页显示了按天划分的销售图表:

第三页按店铺划分销售额:

工作原理

步骤 1 显示了如何使用脚本,步骤 2 在输入文件中调用它。让我们看一看这个短语的基本结构:

# IMPORTS
def generate_summary(logs):

def aggregate_by_day(logs):
def aggregate_by_shop(logs):

def graph(...):

def create_summary_brief(...):

def main(input_file, output_file):
  # open and read input file
  # Generate each of the pages calling the other calls
  # Group all the pdfs into a single file
  # Write the resulting PDF

if __name__ == '__main__':
  # Compile the input and output files from the command line
  # call main

有两个关键要素:以不同方式(按商店和按天)聚合日志,以及在每种情况下生成摘要。摘要由generate_summary生成,该generate_summary从日志列表中生成一个包含聚合信息的字典。在aggregate_by函数中,以不同的方式聚合日志。

generate_summary produces a dictionary with the aggregated information, including start and end time, total income of all logs, total units, average discount, and a breakdown of the same data by product.

从结尾开始可以更好地理解脚本。主函数连接所有不同的操作。阅读每个日志并将其转换为本机SaleLog对象。

然后,它将每个页面生成一个中间 PDF 文件:

  • create_summary_brief对所有数据的总体总结生成的摘要。
  • 日志为aggregate_by_day。将创建摘要并生成图形。
  • 日志为aggregate_by_shop。将创建摘要并生成图形

使用PyPDF2将所有中间 PDF 页面合并到一个文件中。最后,删除中间页。

aggregate_by_dayaggregate_by_shop都返回一个列表,其中包含每个元素的摘要。在aggregate_by_day中,我们通过使用.end_of_day区分一天和另一天来检测一天何时结束。

graph功能执行以下操作:

  1. 准备要显示的所有数据。这包括每个标签(每天或商店)的单位数量,以及每个标签的总收入。
  2. 创建包含总收入的顶部图表,按产品拆分为堆叠的条形图。为了做到这一点,在计算总收入的同时,计算基线(下一个堆栈所在的位置)。
  3. 它将图形的底部划分为与产品数量相同的多个图形,并显示每个标签(每天或商店)上的销售量。

For a better display, the graph is defined to be the size of an A4 sheet. It also allows us, using skip_labels, to print one of each X label on the second graph on the X axis to avoid overlapping. This is useful when displaying the days, and it's set to show only one label per week.

生成的图形将保存到文件中。

create_summary_brief使用fpdf模块保存包含汇总信息的文本 PDF 页面

The template and information in create_summary_brief has been left deliberately simple to avoid complicating this recipe, but it can be complicated with better descriptive text and formatting. Refer to Chapter 5Generating Fantastic Reports, for more details on how to use fpdf.

如前所示,main功能将所有 PDF 页面分组,并将它们合并到一个文档中,稍后删除中间页面。

还有更多。。。

此配方中包含的报告可以扩展。例如,可以在每个页面中计算平均折扣并显示为一行:

# Generate a data series with the average discount
discount = [summary['average_discount'] for _, summary in full_summary]
....
# Print the legend
# Plot the discount in a second axis
plt.twinx()
plt.plot(pos, discount,'o-', color='green')
plt.ylabel('Average Discount')

不过,要小心,不要在单个图表中放入太多信息。它可能会降低可读性。在这种情况下,另一个图形可能是更好的显示方式。

Be careful to print the legend before creating the second axis, or it will display only the information on the second axis.

图形的大小和方向可以决定使用更多还是更少的标签,因此它们清晰易读。这在使用skip_labels避免混乱中得到了证明。注意生成的图形,并在某些情况下通过更改尺寸或限制标签来尝试适应该区域可能出现的问题。

For example, a possible limit is to have no more than three products, as printing four graphs in the second row in our graphs will probably make the text illegible. Feel free to experiment and check the limits of the code.

完整的matplotlib文件可在找到 https://matplotlib.org/ 。 可在此处找到delorean文档:https://delorean.readthedocs.io/en/latest/ 。 可在上获取openpyxl的所有文件 https://openpyxl.readthedocs.io/en/stable/ 。 PDF 操作模块的完整文档可在中找到 PyPDF2https://pythonhosted.org/PyPDF2/pyfdf处 https://pyfpdf.readthedocs.io/en/latest/

This recipe makes use of different concepts and techniques that are available in Chapter 5Generating Fantastic Reports, for PDF creation and manipulation, Chapter 6, Fun with Spreadsheets, for spreadsheet reading, and Chapter 7, Developing Stunning Graphs, for graph creation. Check them out to know more.

另见

  • 聚合 PDF第 5 章中报告配方生成奇妙的报告
  • 阅读第 6 章中的 Excel电子表格配方电子表格乐趣
  • 第 7 章中的绘制堆叠钢筋配方绘制惊人图形
  • 第 7 章显示多行配方开发惊人图形
  • 第 7 章中添加图例和注释配方开发惊人的图形
  • 第 7 章中的组合图形配方开发惊人图形
  • 第 7 章中的储蓄图表配方*开发惊人图表**