{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Основы программирования в Python\n", "\n", "*Алла Тамбовцева, НИУ ВШЭ*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Как создать рассылку email в Python\n", "\n", "*Частично основано на следующих источниках:*\n", " \n", "* [gist](https://gist.github.com/nickoala/569a9d191d088d82a5ef5c03c0690a02) от nickoala\n", "* [gist](https://gist.github.com/robulouski/7441883) от robulouski" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Как отправить email" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Сегодня мы посмотрим, как можно рассылать электронные письма средствами Python. \n", "Импортируем библиотеку `smtplib` для доступа к `gmail.com` и библиотеку `email` для создания текста письма (а именно, функцию `MIMEText`)." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import smtplib\n", "from email.mime.text import MIMEText" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Укажем название хоста и порт для безопасного доступа к gmail. " ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "host = 'smtp.gmail.com'\n", "port = 465" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь можем ввести свои логин и пароль:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "user = 'allatambov@gmail.com' \n", "password = '1234' # your password here" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "При работе с ящиком *gmail* в связи с настройками безопасности можно столкнуться с проблемой: Gmail не позволит подключиться к ящику через Python, поскольку посчитает его сторонним подозрительным приложением. Чтобы избежать этого, давайте заранее изменим настройки безопасности (после работы в Python очень рекомендуется вернуть всё на место). Зайдем на почту, кликнем на фотографию, выберем *Аккаунт Google*, затем *Безопасность*, и включим доступ в разделе *Ненадежные приложения, у которых есть доступ к аккаунту*.\n", "\n", "Теперь сообщим Python, с какого адреса и кому мы будем отправлять сообщение. Пока я отправлю письмо самой себе, но создам при этом список адресатов `to`, который можно расширять:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "from_user = user\n", "to = ['allatambov@gmail.com'] " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Сохраним строку с текстом письма в `text`, а затем превратим его в объект *сообщение*:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "text = 'I did it!'\n", "msg = MIMEText(text)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Посмотрим, что внутри:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Content-Type: text/plain; charset=\"us-ascii\"\n", "MIME-Version: 1.0\n", "Content-Transfer-Encoding: 7bit\n", "\n", "I did it!\n" ] } ], "source": [ "print(msg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь добавим тему письма, укажем отправителя и получателя:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "msg['Subject'] = 'Python'\n", "msg['From'] = from_user\n", "msg['To'] = ', '.join(to) # склеим всех адресатов в списке (подойдет для рассылки)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Снова посмотрим на сообщение:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Content-Type: text/plain; charset=\"us-ascii\"\n", "MIME-Version: 1.0\n", "Content-Transfer-Encoding: 7bit\n", "Subject: Python\n", "From: allatambov@gmail.com\n", "To: allatambov@gmail.com\n", "\n", "I did it!\n" ] } ], "source": [ "print(msg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Письмо готово! Осталось его отправить. Подключимся к серверу, введём логин и пароль:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(235, b'2.7.0 Accepted')" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "server = smtplib.SMTP_SSL(host, port)\n", "server.login(user, password)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Если вы получили ошибку `SMTPAuthenticationError`, а адрес почты и пароль указаны верно, проверьте, что вы изменили настройки безопасности." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Отправляем письмо:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{}" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "server.sendmail(from_user, to, msg.as_string())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Если после исполнения кода был возвращён пустой словарь, то всё хорошо, письмо отправилось (проверьте почту)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь давайте сделаем что-нибудь поинтереснее: прикрепим к письму приложение-картинку ([ссылка](https://www.clipartmax.com/middle/m2i8i8H7G6b1Z5d3_funny-cartoon-snake-images-python-guide-for-complete-beginners/)). " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![python](python.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Для этого нам понадобится функция `MIMEMultipart()` для создания письма из нескольким частей и функция `MIMEImage()` для добавления изображения." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "from email.mime.image import MIMEImage\n", "from email.mime.multipart import MIMEMultipart" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Так как изображение будем брать с компьютера, импортируем ещё модуль `os` (от *operation system*)." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "import os" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Создадим сообщение из нескольких частей (типа `Multipart`), добавим тему письма, отправителя и получателя:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "msg2 = MIMEMultipart()\n", "msg2['Subject'] = 'Hello from Python'\n", "msg2['From'] = from_user\n", "msg2['To'] = ', '.join(to)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Прикрепим текст письма:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "text = \"Python is missing you.\"\n", "text_part = MIMEText(text)\n", "msg2.attach(text_part)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Выберем изображение, которое мы хотим прикрепить и откроем его:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "filepath = '/Users/allat/Desktop/python.png'\n", "with open(filepath, 'rb') as f:\n", " img = MIMEImage(f.read())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Прикрепим изображение к сообщению `msg2`, предварительно добавив ему название (совпадает с названием файла, как обычно):" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "img.add_header('Content-Disposition',\n", " 'attachment',\n", " filename=os.path.basename(filepath))\n", "msg2.attach(img)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Осталось отправить письмо:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{}" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "server.sendmail(from_user, to, msg2.as_string())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Готово! Теперь посмотрим, как можно организовать рассылку на основе имён и адресов, сохранённых в таблице. И заодно закидаем питонами тех, кто не пришёл на пары :)\n", "\n", "Импортируем библиотеку `pandas` и функцию `sleep` для добавления задержки по времени после каждого письма:" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "from time import sleep" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Создадим датафрейм из вложенного списка:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "students = [['Alla', 'allatambov@gmail.com'],\n", " ['Not alla', 'matstat.polit@gmail.com']]" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
namemail
0Allaallatambov@gmail.com
1Not allamatstat.polit@gmail.com
\n", "
" ], "text/plain": [ " name mail\n", "0 Alla allatambov@gmail.com\n", "1 Not alla matstat.polit@gmail.com" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df = pd.DataFrame(students)\n", "df.columns = ['name', 'mail']\n", "df" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Загрузим картинку (ту же, что и в первый раз):" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [], "source": [ "filepath = '/Users/allat/Desktop/python.png'\n", "with open(filepath, 'rb') as f:\n", " img = MIMEImage(f.read())\n", "\n", "img.add_header('Content-Disposition',\n", " 'attachment',\n", " filename=os.path.basename(filepath))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Напишем цикл для пары *имя-адрес* в датафрейме `df` и включим в него шаблон текста письма, в который будет подставляться имя адресата:" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [], "source": [ "for name, mail in zip(df.name, df.mail):\n", " \n", " text = f\"\"\"Dear {name}, \\n \n", " Why are you absent? Python is missing you. \\n\n", " We are learning to send emails from Python now, never mind. \"\"\"\n", " \n", " msg = MIMEMultipart()\n", " text_part = MIMEText(text)\n", " msg.attach(text_part)\n", " msg['Subject'] = 'Hello from Python'\n", " msg['From'] = from_user\n", " msg['To'] = mail\n", " \n", " msg.attach(img)\n", " \n", " server.sendmail(from_user, mail, msg.as_string())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Done!" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.5" } }, "nbformat": 4, "nbformat_minor": 2 }