Python

С помощью Python можно с легкостью автоматизировать ежедневные задачи. Используя комбинацию API и простых для понимания библиотек, можно настроить системы, которые выполняют скрейпинг веб-сайтов, отправляют электронные письма и управляют данными и аналитикой.

Одной из наиболее распространенных задач, которые необходимо автоматизировать в корпоративной среде, является извлечение открытых государственных данных. Обычно для этого используют такие сайты, как data.gov, и другие конечные точки, в которых содержится информация о здравоохранении, торговле, транспорте, юриспруденции и многом другом.

В этой статье мы расскажем, как написать скрипт Python, который может скачивать определенный набор правительственных данных, а затем загружать его в электронное письмо и отправлять кому угодно.

Здесь можно найти документацию, которая поможет разобраться в том, как использовать библиотеку запросов Python, а также API Gmail. Так что если вы хотите автоматизировать электронные письма с помощью Python, то это отличное место для начала.

Загрузка файла с помощью Python

В первую очередь нам потребуется загрузить файл данных с помощью HTTP-запроса.

Преимущество Python в том, что большая часть сложности написания кода, такого как HTTP-запросы, была удалена. Вместо этого можно импортировать библиотеку запросов и использовать ее для публикации и получения запросов.

В этом случае файл, который мы будем загружать, даже получит простую конечную точку по ссылке ниже. Таким образом, вам не нужно использовать такую библиотеку, как Selenium, чтобы нажимать на кнопку загрузки.

Даже при наличии кнопки загрузки, вам следует проверить, есть ли у нее URL-адрес. Как правило, если есть URL-адрес, файл действительно легко загрузить по ссылке, вместо создания веб-краулера для поиска кнопки, которую нужно нажать.

https://data.medicaid.gov/api/views/u72p-j37s/rows.json?accessType=DOWNLOAD

Этот файл содержит информацию о Медикейд (американская государственная программа медицинской помощи нуждающимся) и отдельных участниках. Она будет полезна для поставщиков медицинских услуг, которые могут использовать ее совместно со своими внутренними данными для лучшего понимания рынка.

Чтобы скачать файл, мы будем использовать функцию requests.get() для получения данных на указанный нами URL.

Для примера рассмотрим скрипт ниже:

# Часть 1: извлекаем данные.

# Получаем веб-страницу, сохраняем ее в объекте Response и присваиваем текст.
# Дополнительная информация: https://requests.readthedocs.io/en/master/api/#requests.Response.

# Этот URL содержит .csv-файл загрузки для
# 'https://catalog.data.gov/dataset/' \
#	'share-of-medicaid-enrollees-in-managed-care',
# который используется для отправки на адрес электронной почты.
csvFileURL = 'https://data.medicaid.gov/api/' \
	'views/u72p-j37s/rows.csv?accessType=DOWNLOAD'
csvFileRequest = requests.get(csvFileURL)
csvFile = csvFileRequest.content

Это легкий и быстрый способ, возвращающий CSV как часть запроса, который задан в качестве переменной. Мы будем использовать его позже, когда создадим письмо. А пока нам нужно настроить учетные данные для API Gmail.

Настройка API Gmail

Google значительно упростил настройку API. Перейдите в консоль API Google, затем выберите ENABLE APIS AND SERVICES и выполните поиск API Gmail.

Консоль API выглядит следующий образом:

После нажатия этой кнопки вы получаете поисковую систему, с помощью которой можно искать различные API. Она показана на изображении ниже.

Вы можете ввести Gmail, и он должен отобразиться только один раз.

Затем выберите API Gmail, и под ним появится кнопка ENABLE.

После нажатия этой кнопки вы можете загрузить свои учетные данные или использовать ключ API и секрет.

Наш код будет использовать JSON-загрузку, но затем вы можете преобразовать его в любой желаемый формат.

Теперь, когда все настроено, мы можем приступить к созданию функций для настройки, а затем автоматически отправлять электронные письма.

Отправка писем с помощью API Gmail

Теперь, когда мы нашли способ получить необходимые данные, нам нужно выяснить, как отправить электронное письмо.

Для этого воспользуемся библиотекой email. С ее помощью можно настроить различные части электронного письма: отправитель, получатель, тема и т. д.

Мы будем использовать класс MIMEBase в электронной почте, чтобы упростить настройку правильных точек данных и предоставить простой класс, который API Gmail будет использовать в дальнейшем для парсинга.

Использовать класс MIMEBase действительно просто, так как вы можете инициировать новый класс, а затем ссылаться на многие необходимые компоненты, такие как:

message[‘from’] = [email protected]

Ниже представлена вся функция, в которой установлены эти параметры:

# Функция, необходимая для части 2: отправка электронной почты с помощью API Google.
# a) Создание сообщения
def create_message(sender, to, subject, csv):
	#message = MIMEMultipart()
	message = MIMEMultipart()
	message['from'] = sender
	message['to'] = to
	message['subject'] = subject

	# Отправка времени его обновления в качестве тела письма
	dt_object = datetime.utcnow() - timedelta(hours = 7)
	msg = MIMEText('Hi! Your file was updated.' \
		'\nTime of update: ' + dt_object.strftime('%m/%d/%Y, %I:%M:%S %p') \
		+ ' (Los Angeles Time)')

	message.attach(msg)

	# Прикрепление файла .csv
	record = MIMEBase('application', 'octet-stream')
	# print(csv)
	record.set_payload(csv)
	encoders.encode_base64(record)
	record.add_header('Content-Disposition', 'attachment', filename='medicare.csv')
	message.attach(record)

	# Возврат сообщения
	raw = base64.urlsafe_b64encode(message.as_bytes())
	raw = raw.decode()
	return {'raw': raw}

Обратите внимание, что в конце мы используем функцию urlsafe_b64encode, которая установит сообщение в байтах. Это делается для упрощения передачи данных электронной почты в API Gmail.

Теперь пришло время отправить первое автоматическое письмо.

Отправка первого автоматического письма с помощью Python

После настройки учетных данных API Gmail мы можем отправить наше первое электронное письмо.

Мы будем использовать сервисную переменную, установленную с API Gmail и учетными данными.

Этот процесс показан ниже в функции send_message:

# b) Отправка сообщения
def send_message(service, user_id, message):
	try:
		message = service.users().messages(). \
		send(userId=user_id, body=message).execute()
		print('Message Id: %s' % message['id'])
		return message
	except Exception as e:
		print('An error occurred: %s' % e)
		return None

Теперь все, что нам нужно сделать, — передать сообщение и выполнить код.

Весь скрипт приведен ниже:

# 1. Получение данных из URL-адреса каждый раз, когда появляется новый набор данных.
# 2. Отправка электронного письма с помощью API Google.

# Подготовка:
# 1) Создайте проект в консоли API Google в верхнем левом углу GDC
# 2) Включите API Gmail, нажав 'Library' на левой боковой панели
# 3) Создайте учетные данные ID клиента OAuth на верхней боковой панели
# 4) Краткое руководство по Python в URL (см. ниже)
# 5) pip install --upgrade google-api-python-client \
# google-auth-httplib2 google-auth-oauthlib
# 6) Доступ к Gmail
# 7) Создание электронного письма

# Загрузка текстового файла с последней обновленной датой или даже данных!
# Отправка электронного письма, если дата обновлена.

# Требуемые модули для API Google 
from __future__ import print_function
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request

# Возможность загрузить страницу
# Возможность чтения файлов без их загрузки,
# что позволяет проверять наличие обновлений в режиме реального времени.
import requests

# Возможность запуска цикла с использованием модуля time
import time

# Возможность конвертировать значение времени в объект datetime
from datetime import datetime, timedelta
import math

# Возможность создать электронное письмо
from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import base64
import os

# При изменении этих областей файл token.pickle удаляется.
# Эта область нужна для полного доступа к API Google Mail.
SCOPES = 'https://mail.google.com/'

# Функция, необходимая для части 2: отправка электронной почты с помощью API Google.
# a) Создание сообщения
def create_message(sender, to, subject, csv):
	#message = MIMEMultipart()
	message = MIMEMultipart()
	message['from'] = sender
	message['to'] = to
	message['subject'] = subject

	# Отправка времени его обновления в качестве тела письма
	dt_object = datetime.utcnow() - timedelta(hours = 7)
	msg = MIMEText('Hi! Your file was updated.' \
		'\nTime of update: ' + dt_object.strftime('%m/%d/%Y, %I:%M:%S %p') \
		+ ' (Los Angeles Time)')

	message.attach(msg)

	# Прикрепление .csv-файла
	record = MIMEBase('application', 'octet-stream')
	# print(csv)
	record.set_payload(csv)
	encoders.encode_base64(record)
	record.add_header('Content-Disposition', 'attachment', filename='medicare.csv')
	message.attach(record)

	# Возврат сообщения
	raw = base64.urlsafe_b64encode(message.as_bytes())
	raw = raw.decode()
	return {'raw': raw}

# b) Отправка сообщения
def send_message(service, user_id, message):
	try:
		message = service.users().messages(). \
		send(userId=user_id, body=message).execute()
		print('Message Id: %s' % message['id'])
		return message
	except Exception as e:
		print('An error occurred: %s' % e)
		return None


# Часть 1: извлекаем данные.

# Получаем веб-страницу, сохраняем ее в объекте Response и присваиваем текст.
# Дополнительная информация: https://requests.readthedocs.io/en/master/api/#requests.Response.

# Этот URL содержит .csv-файл загрузки для
# 'https://catalog.data.gov/dataset/' \
#	'share-of-medicaid-enrollees-in-managed-care',
# который используется для отправки на адрес электронной почты.
csvFileURL = 'https://data.medicaid.gov/api/' \
	'views/u72p-j37s/rows.csv?accessType=DOWNLOAD'
csvFileRequest = requests.get(csvFileURL)
csvFile = csvFileRequest.content

# ПРИМЕЧАНИЕ: часть ниже необходима, если файл не является .csv.
# Теперь мы добавим важную команду метаданных SEP.
# Она говорит Excel о том, что нужно использовать разделитель.
#decoded = csvFile.decode('utf-8')
#decoded = 'SEP=,\n' + decoded
#csvFile = decoded.encode('utf-8')

# Этот URL-адрес содержит загрузку .json для
# 'https://catalog.data.gov/dataset/' \
#	'share-of-medicaid-enrollees-in-managed-care'
# которая используется для сравнения файлов.
jsonOfFile = 'https://data.medicaid.gov/api/views/u72p-j37s/' \
	'rows.json?accessType=DOWNLOAD'

r = requests.get(jsonOfFile)
firstJSON = r.text

# Часть 2: использование API Google для отправки электронного письма с обновленными метаданными.

# Выясните, был ли изменен файл.
# Изначально проверялась последняя дата обновления,
# но потом мне пришло в голову, что в один день
# может быть сделано несколько изменений.
# Старый код: print(BeautifulSoup(URL, 'html.parser). \
# find_all('td')[0].get_text())
# возвращает дату последнего обновления на основе текущего макета сайта.

# Теперь новый код сравнивает версию файлов .json каждую минуту.

# Этот фрагмент будет запускать код каждые 60 секунд (за исключением первой итерации)
# вместо time.sleep(60), который запускает цикл каждые 60 секунд.
# В последнем случае коду может потребоваться некоторое время "x" для завершения выполнения,
# поэтому общее время было бы 60 + х секунд (что плохо).

r = requests.get(jsonOfFile)
secondJSON = r.text

# Если сайт был обновлен или скрипт только начался, произойдет отправка сообщения.
if firstJSON != secondJSON:

	# Создание сообщения
	sender = '[email protected]'
	to = '[email protected]'
	subject = 'The Medicare metadata has been updated'
	message = create_message(sender, to, subject, csvFile)

	# Отправка сообщения с помощью API Google 
	creds = None
	# Файл token.pickle хранит токены доступа и обновления пользователя и
	# создается автоматически при первом завершении потока авторизации.
	if os.path.exists('token.pickle'):
		with open('token.pickle', 'rb') as token:
			creds = pickle.load(token)
	# Если нет доступных (действительных) учетных данных, предоставьте пользователю возможность войти в систему.
	if not creds or not creds.valid:
		if creds and creds.expired and creds.refresh_token:
			creds.refresh(Request())
		else:
			flow = InstalledAppFlow.from_client_secrets_file(
				'credentials.json', SCOPES)
			creds = flow.run_local_server(port=0)
		# Сохранение учетных данных для следующего запуска
		with open('token.pickle', 'wb') as token:
			pickle.dump(creds, token)

	service = build('gmail', 'v1', credentials=creds)
	send_message(service, sender, message)

	# Обновление переменной
	firstJSON = secondJSON

	print('Message sent')

Настало время приступить к автоматизации

Мы надеемся, что этот скрипт поможет вам автоматизировать электронные письма с помощью Python.

В приведенных выше примерах скрипт был составлен вручную. Однако, если вы хотите более эффективной автоматизации, вы можете использовать такую среду, как Airflow. Мы не рекомендуем пытаться создать собственный фреймворк автоматизации, особенно когда есть так много отличных вариантов.

Вот и все, удачной автоматизации!

Читайте также:

Читайте нас в TelegramVK и Яндекс.Дзен


Перевод статьи SeattleDataGuy: How to Automate Your Emails With Python

Предыдущая статьяСтатистические типы данных, используемые в машинном обучении
Следующая статьяСоветы по Docker: очистка локального компьютера