
Центр непрерывного образования
факультет компьютерных наук НИУ ВШЭ
Уже много лет я преподаю машинное обучение, программирование и анализ данных. Подготовка материалов лекций и общение со студентами доставляют огромное удовольствие, а вот рассылки, оформление ведомостей занимают большое количество времени и вызывают лишь скуку. Поэтому я решила автоматизировать эту часть работы с помощью Python. Также наши сотрудники из учебного офиса тратят значительное количество времени, вручную создавая документы и рассылки. Приведенные ниже скрипты могут быть полезны не только в преподавательской, но и в разнообразной менеджерской работе.
По сути, передо мной и коллегами обычно стоят две типовыезадачи, которые я бы и хотела автоматизировать:
-
персонализированные рассылки
-
генерация документов (служебные записки, заявления)
И в рассылках, как и при генерации документов, хотелось бы автоматически подтягивать данные из большого количества excel‑таблиц, в которые собираются данные о студентах от преподавателей, ассистентов и учебного офиса.

Маргарита Бурова
Преподаватель и эксперт Центра непрерывного образования ФКН НИУ ВШЭ, руководитель Edtech-программ по DS и DA Wildberries&Russ
Автоматизированные рассылки
Здесь основной фокус стоит на том, чтобы большое количество людей получили персонализированные рассылки (в которых, к примеру, будет содержаться уникальный код доступа или еще какая‑то личная информация)
Для начала необходимо подключиться к почтовому серверу под логином и паролем отправителя. Для этого я использую следующий код:
HOST = "smtp.hse.ru" PASSWORD = "ВашПароль" # тут записывается пароль к почте PORT = 587 SENDER_EMAIL = # тут записывается адрес отправителя smtpserver = smtplib.SMTP(HOST, PORT) smtpserver.starttls() smtpserver.login("логин отправителя", PASSWORD)
Поясню основные моменты:
HOST = "smtp.hse.ru"
Сначала мы задаем адрес SMTP‑сервера нашего вуза, Высшей школы экономики (hse.ru). Этот сервер будет использоваться для отправки электронной почты.
PORT = 587
Для порта устанавливается значение 587. Это стандартный порт для SMTP‑сервера с использованием TLS (Transport Layer Security). Он обеспечивает безопасную передачу данных.
Далее следует установка соединения с SMTP‑сервером:
smtpserver = smtplib.SMTP(HOST, PORT)
Мы создаем объект, который устанавливает соединение с указанным SMTP‑сервером по заданному хосту и порту.
И после этого инициализируется безопасное соединение:
smtpserver.starttls()
Осталось только авторизоваться:
smtpserver.login("логин", PASSWORD)
Таким образом, мы выполнили подготовительную часть, и если авторизация прошла успешно — можно переходить к рассылке. Рассылку сразу будем делать по списку почт.
Допустим, у меня есть три почтовых адреса:
mails = [‘почта1@gmail.com’, ‘почта2@yandex.ru’, ‘почта3@gmail.com‘]
В реальности, конечно, их может быть сто или больше — и я подгружаю их из таблички.
Составляем непосредственно тело письма:
for mail in mails: msg = MIMEMultipart() # Создаем сообщение msg["From"] = SENDER_EMAIL # Добавляем адрес отправителя msg['To'] = mail # Добавляем адрес получателя msg["Subject"] = Header('Орг.инфо про курс', 'utf-8') # Пишем тему сообщения text = ‘текст’ msg.attach(MIMEText(text, 'html', 'utf-8')) # Добавляем форматированный текст сообщения smtpserver.send_message(msg) # отправляем сообщение
В начале создается новый объект MIMEMultipart, который представляет собой многочастное сообщение электронной почты. Это позволит нам добавить различные части к нашему сообщению, такие как текст, изображения и вложения. Или вообще отформатировать сообщение с помощью HTML‑разметки.
Далее добавляем основные характеристики письма: адрес отправителя (у меня он заранее определен в переменную), адрес получателя (будет каждый раз разный, мы в цикле как раз перебираем адреса) и тему сообщения.
В переменную text я записала здесь просто строку, но в реальности письма, как правило, носят персонализированный характер, поэтому сюда может добавиться информация для конкретного человека.
После добавления текста письма — оно отправляется! Таким образом, я за один раз делаю рассылку по всем студентам.
Для того, чтобы добавлять персонализированную информацию, я подтягиваю данные из таблицы.
К примеру, у меня есть табличка с данными о студентах:

В таком случае я могу запускать цикл при генерации писем, который пройдется по всем строкам и из каждой строки извлечет адрес для отправки и, к примеру, имя студента.
Можно использовать для этого pandas:
for i in range(df.shape[0]): msg = MIMEMultipart() msg['To'] = df['E-mail'].iloc[i] # добавляю адрес из i-ой строки msg["Subject"] = Header('Орг.инфо про курс', 'utf-8') text = f'Добрый день, {df["Имя"].iloc[i]}' # добавляю имя из i-ой строки msg.attach(MIMEText(text, 'html', 'utf-8')) сообщения smtpserver.send_message(msg)
В приведенном выше коде из персонализации я просто добавляю обращение по имени, но, разумеется, можно добавлять и какую‑то еще информацию, записанную в таблицу.
Рассылка сделана, время сэкономлено!
Заполнение документов по шаблонам
Кроме рассылки писем есть вторая задача — генерация документов по шаблонам.
Достаточно часто нужно сгенерировать какие‑либо документы или заявления по шаблону. Причемданные, как правило, подтягиваются из экселевских таблиц.
Этот код у нас используют и сотрудники учебных офисов, поэтому он максимально приближен к обычной работе с таблицами и документами.
По сути, задача такая:
-
Снова есть уже знакомая нам таблица с информацией о студентах:

-
И заявление, в которое необходимо подставить эти данные:

В этом заявлении в двойных фигурных скобках записаны переменные, которые необходимо заменять данными из таблицы.
Для решения задачи будем использовать два модуля: openpyxl и DocxTemplate.
Модуль openpyxl полезен для работы с файлами Microsoft Excel формата.xlsx и.xlsm непосредственно из Python. С его помощью вы можете программно создавать, читать, изменять и сохранять Excel‑файлы без необходимости использования самого приложения Excel. Его преимущество заключается в том, что им легко пользоваться людям, не очень глубоко знакомым с python, но при этом уверенно работающим в Excel. По сути, этот модуль позволяет работать с экселевскими файлами почти так же, как и в самом эксель. Именно поэтому я хотела бы показать его, а не pandas (в том числе, чтобы добавить разнообразия между двумя примерами)
Первоначально импортируем модуль openpyxl и открываем таблицу с данными, выбираем оттуда нужный лист:
import openpyxl wb = openpyxl.load_workbook(filename='table_final.xlsx') sheet = wb['Лист1']
Далее импортируем класс DocxTemplate и загружаем ранее созданный шаблон.
from docxtpl import DocxTemplate doc = DocxTemplate("temp_final.docx")
DocxTemplate нужен, чтобы автоматически создавать документы Word на основе шаблонов с пустыми полями. Он позволяет вставлять в эти шаблоны переменные из кода Python, что упрощает создание персонализированных документов, например, отчетов или счетов. По сути, нужно сделать «рыбу» документа, обозначив места для подстановки данных. И далее уже такой шаблон можно преобразовывать в экземпляр класса DocxTemplate и работать с ним.
Далее необходимо лишь прописать цикл, который пройдет по всем строкам и в каждой заберет нужные ячейки, а потом подставит в них контекст:
for num in range(2,7): name = sheet['A'+str(num)].value course = sheet['B'+str(num)].value group = sheet['C'+str(num)].value op = sheet['D'+str(num)].value mobile = sheet['E'+str(num)].value mail = sheet['F'+str(num)].value date = sheet['G'+str(num)].value.date() context = { 'name': name, 'course': course, 'group': group, 'op': op, 'mobile': mobile, 'mail': mail, 'date' : date, 'now_date': today } doc.render(context) doc.save(name+' заявление.docx')
Давайте разберем основные моменты:
name = sheet['A'+str(num)].value
В данном кусочке кода из объекта sheet (представляющего рабочий лист электронных таблиц) считывается значение из ячейки в столбце ‘A’ и строке num. Часть ‘A’+str(num). Далее аналогичным образом из столбцов B, C, D, E и F текущей строки (num) считываются значения и присваиваются соответствующим переменным::
-
course — курс
-
group — группа
-
op — образовательная программа
-
mobile — номер мобильного телефона
-
mail — адрес электронной почты
date = sheet['G'+str(num)].value.date()
Здесь из столбца G текущей строки читается значение, которое предполагается датой. Метод.date() используется, чтобы получить только дату без времени.
Также здесь в текущую дату записано значение переменной today, которая задается следующим образом:
today = str(date.today())
Это нужно для подписания документа сегодняшней датой.
В конце мы обрабатываем шаблон, заменяя в нем плейсхолдеры соответствующими значениями из словаря context. т. е., если в шаблоне есть плейсхолдер {{ name }}, он будет заменен на значение переменной name. И так происходит со всеми плейсхолдерами.
doc.render(context)
После этого лишь сохраняем документ, и все! Таким образом можно сгенерировать огромное количество документов за раз. Что упрощает и автоматизирует работу.
ссылка на оригинал статьи https://habr.com/ru/articles/900728/
Добавить комментарий