python으로 메일 보내기 - smtplib, email

|

파이썬에서 메일을 보내기 위해서는 외부 라이브러리가 별도로 필요하지 않으며, smtplib이라는 기본 라이브러리를 제공한다.

SMTP(Simple Mail Transfer Protocol) : 인터넷 상의 유효한 이메일 아이디로 이메일을 보내는 데에 사용되는 메세지 전송용 프로토콜

smtplib

class smtplib.SMTP(host='', port=0, local_hostname=None, [timeout,]source_address=None)
  • host : 지메일과 같은 특정 메일을 사용하기 위한 SMTP 변수 지메일은 smtp.gmail.com
  • port : 포트번호. 지메일은 587

SMTP 인스턴스는 SMTP 연결을 캡슐화한다. 선택적 호스트와 포트 매개 변수가 제공되면, 초기화 중에 해당 매개 변수로 SMTP connect() 메서드가 호출된다.

connect() 호출이 성공 코드 이외의 것을 반환하면 SMTPConnectError 가 발생한다. 선택적 timeout 매개 변수는 연결 시도와 같은 블로킹 연산을 위한 시간제한을 초로 지정한다. (지정하지 않으면 전역 기본 시간제한 설정이 사용된다.) 시간제한이 만료되면 socket.timeout이 발생한다. 선택적 source_address 매개 변수를 통해 기계의 특정 소스 주소 또는 특정 소스 TCP 포트에 바인딩할 수 있도록 한다. 연결 전에 소켓이 sorecu address로 할 2-튜블(host, port)를 취한다. 생략되면 OS 기본 동작이 사용된다.

일반적으로

  1. 초기화/연결
  2. sendmail()
  3. SMTP.quit() 메서드만이 필요하다.

SMTP 클래스는 with문을 지원한다. with문이 종료될 때 SMTP quit 명령이 자동으로 발행된다.

from smtplib import SMTP
with SMTP("domain.org") as smtp:
  smtp.noop()
...
...
(250, b'Ok')

SMTP 객체

  • SMTP.set_debuglevel(level) : 디버그 출력 수준을 설정한다. level의 값이 1이면 연결 및 서버와 주고받는 모든 메시지에 대한 디버그 메시지가 생성된다 level의 값이 2이면 이러한 메시지에 타임 스탬프가 추가된다.
  • SMTP.connect(host='localhost', port=0) : 지정된 포트(port)의 호스트(host)에 연결한다 기본값은 표준 SMTP 포트(25)로 로콜 호스트에 연결하는 것이다. 연결 응답에서 서버가 보낸 응답 코드와 메시지의 2-튜플을 반환한다.
  • SMTP.starttls(keyfile=None, certfile=None, context=None) : SMTP 연결을 TLS(Transport Layer Security) 모드로 전환한다. 뒤따르는 모든 SMTP 명령은 암호화된다. 그런 다음 ehlo()를 다시 호출해야 한다. 이 세션에 앞선 EHLOHELO 명령이 없었으면, 이 메서드는 ESMTP EHLO를 먼저 시도한다.
  • SMTP.login(user, password, *, initial_response_ok=True) : 인증이 필요한 SMTP 서버에 로그인한다. 인자는 인증할 사용자 이름과 비밀번호이다.
  • SMTP.sendmail(from_addr, to_addrs, msg, mail_options=(), rcpt_options=()) : 메일을 보낸다.
  • SMTP.quit() : SMTP 세션을 종료하고 연결을 닫는다.

msg 부분에는 email패키지를 사용하여 MIME 메시지를 보낼 수 있다.

email

email 패키지는 전자 메일 메시지를 관리하기 위한 라이브러리이다.

email.mime : 처음부터 (from scratch) 이메일과 MIME 객체를 생성하는 모듈

공식 문서에서는 이 부분이 아직 번역되어 있기 때문에 미흡하나마 전문의 번역을 실었다.

이 모듈은 전통적인 (Compat32) email API의 일부분이다. 이것의 기능은 새로운 API에서 contentmanager에 의해 부분적으로 대체되었다. 하지만 어떤 어플리케이션에서는 이 클래스들이 유용할 것이다. (전통적이지 않은 code에서도)

일반적으로 메세지 객체 구조는 file이나 어떤 text를 파서에 넘김으로써 획득될 수 있다. 파서는 text를 분석하고 메세지의 root 객체를 리턴한다. 하지만 우리는 완전한 메세지 구조를 무에서부터, 혹은 개인적인 Message 오브젝트로부터 직접 완성할 수도 있다. 사실 이미 존재하는 구조에 새로운 Message 객체를 붙이거나, 제거할 수도 있다. 이는 MIME 메세지를 자르고 이어 붙이는 데에 있어서 매우 편안한 인터페이스를 만들어준다.

또한, Message 인스턴스를 생성하고, 첨부물이나 모든 적절한 헤더를 직접 붙임으로써 새로운 객체 구조를 만들 수도 있다. email 패키지는 이를 더 쉽게 만들어줄 몇 개의 편리한 서브클래스들을 제공한다.

출처

200324TIL

|

오늘 한 것

  • 파이썬으로 이메일 보내는 법 공부하고 실습하기
  • 파이썬으로 크롤링 복습하기
  • 코딩테스트 문제 풀기
  • 지원서 작성 및 제출

내일 할 것

  • 파이썬과 firebase 연동
  • 크롤링 테스트

초보자를 위한 장고 뿌시기 8 Include로 HTML 소스 관리하기

|

goormedu의 초보자를 위한 장고 뿌시기를 보고 공부한 것을 정리합니다.

웹페이지에는 재사용되는 요소들이 많다. 대표적으로 navigation bar가 있다. bootstrap에서 nav 중 하나를 base.html의 block 상단에 붙여넣기하면 다음과 같은 화면을 볼 수 있다.

views
main 화면
views
novels 화면

이는 분명히 편리하지만, (하나의 html에만 코드를 추가해줘도 된다는 점에서) 어떤 페이지에는 navbar가 필요하고 어떤 페이지는 그렇지 않은 경우, base.html을 각각 만들어줘야 하는 문제가 생긴다.

여기에서는 공유하는 html 파일을 share 폴더 안에 넣어주고, 앞에 _를 붙여서 이름지어준다.

views
views
이제 특정 페이지에서만 navbar를 볼 수 있다.

이렇게 하면 코드 관리 효율성이 좋아진다. navbar를 변경할 필요성이 있을 때 _navbar 폴더만 변경하면 되기 때문이다. 만약 이 코드가 여러 html에 산발적으로 쓰여져 있다면 코드의 중복성 때문에 일일이 찾아서 수정변경해야 하는 번거로움이 생긴다.

TemplateSyntaxError

|

경위

프로젝트에 static 폴더를 생성해 테스트 중 에러 발생

error log


Environment:


Request Method: GET
Request URL: http://127.0.0.1:8000/

Django Version: 3.0.4
Python Version: 3.7.3
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'home',
 'novels']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']


Template error:
In template /Users/eunha/PycharmProjects/koreagojeon/koreagojeon/templates/base.html, error at line 3
   'staticfiles' is not a registered tag library. Must be one of:
admin_list
admin_modify
admin_urls
cache
i18n
l10n
log
static
tz
   1 : <!doctype html>
   2 :
   3 :  {% load staticfiles %}
   4 :
   5 : <html lang="ko">
   6 :   <head>
   7 :     <!-- Required meta tags -->
   8 :     <meta charset="utf-8">
   9 :     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
   10 :     <title>한국고전문학의 세계</title>
   11 :   </head>
   12 :   <body>
   13 :     {% block content %}


Traceback (most recent call last):
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/template/defaulttags.py", line 1021, in find_library
    return parser.libraries[name]

During handling of the above exception ('staticfiles'), another exception occurred:
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/eunha/PycharmProjects/koreagojeon/home/views.py", line 4, in main
    return render(request, 'home/main.html')
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/shortcuts.py", line 19, in render
    content = loader.render_to_string(template_name, context, request, using=using)
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/template/loader.py", line 62, in render_to_string
    return template.render(context, request)
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/template/backends/django.py", line 61, in render
    return self.template.render(context)
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/template/base.py", line 171, in render
    return self._render(context)
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/template/base.py", line 163, in _render
    return self.nodelist.render(context)
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/template/base.py", line 936, in render
    bit = node.render_annotated(context)
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/template/base.py", line 903, in render_annotated
    return self.render(context)
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/template/loader_tags.py", line 127, in render
    compiled_parent = self.get_parent(context)
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/template/loader_tags.py", line 124, in get_parent
    return self.find_template(parent, context)
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/template/loader_tags.py", line 104, in find_template
    template_name, skip=history,
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/template/engine.py", line 125, in find_template
    template = loader.get_template(name, skip=skip)
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/template/loaders/base.py", line 30, in get_template
    contents, origin, origin.template_name, self.engine,
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/template/base.py", line 156, in __init__
    self.nodelist = self.compile_nodelist()
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/template/base.py", line 194, in compile_nodelist
    return parser.parse()
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/template/base.py", line 477, in parse
    raise self.error(token, e)
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/template/base.py", line 475, in parse
    compiled_result = compile_func(self, token)
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/template/defaulttags.py", line 1078, in load
    lib = find_library(parser, name)
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/template/defaulttags.py", line 1025, in find_library
    name, "\n".join(sorted(parser.libraries)),

Exception Type: TemplateSyntaxError at /
Exception Value: 'staticfiles' is not a registered tag library. Must be one of:
admin_list
admin_modify
admin_urls
cache
i18n
l10n
log
static
tz

-> staticfiles가 등록된 tag library에 없다…즉 잘못된 문법 사용

해결 방법


{% load staticfiles %}

가 아니라


{% load static %}

라고 적어야 한다.

load staticfiles는 이전 버전의 Django에서 사용되었으나, 2016년 이후부터 동작하지 않는다.

이제는 사용할 수 없게 되긴 했지만, 둘의 차이는 이러했다.

static 태그는 STATIC_ROOT에 저장된 static 파일들을 link하라는 의미이고, staticfiles 태그는 주어진 path에 있는 모든 URL을 생성하기 위해 STATICFILES_STORAGE 저장소를 사용한다. 이것은 파일을 배치할 때 로컬 스토리지를 사용하지 않는 경우에 유용하다.

static 태그의 공식문서에는 만약 static 파일들을 배치하기 위해 클라우드 서비스를 사용하는 것과 같은 경우에 staticfiles가 유용하다고 쓰여 있다.

그러나 staticstaticfiles보다 좀더 명확하다.

참고

TemplateDoesNotExist 에러

|

경위

Django에서 templates 폴더 아래에 html을 생성하고 테스트하는 도중 에러가 발생하였다.

error log

Environment:


Request Method: GET
Request URL: http://127.0.0.1:8000/

Django Version: 3.0.4
Python Version: 3.7.3
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'novels']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']

Template loader postmortem
Django tried loading these templates, in this order:

Using engine django:
    * django.template.loaders.filesystem.Loader: /Users/eunha/PycharmProjects/koreagojeon/templates/home/main.html (Source does not exist)
    * django.template.loaders.app_directories.Loader: /Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/contrib/admin/templates/home/main.html (Source does not exist)
    * django.template.loaders.app_directories.Loader: /Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/contrib/auth/templates/home/main.html (Source does not exist)
    * django.template.loaders.app_directories.Loader: /Users/eunha/PycharmProjects/koreagojeon/novels/templates/home/main.html (Source does not exist)



Traceback (most recent call last):
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/eunha/PycharmProjects/koreagojeon/home/views.py", line 4, in main
    return render(request, 'home/main.html')
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/shortcuts.py", line 19, in render
    content = loader.render_to_string(template_name, context, request, using=using)
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/template/loader.py", line 61, in render_to_string
    template = get_template(template_name, using=using)
  File "/Users/eunha/PycharmProjects/koreagojeon/venv/lib/python3.7/site-packages/django/template/loader.py", line 19, in get_template
    raise TemplateDoesNotExist(template_name, chain=chain)

Exception Type: TemplateDoesNotExist at /
Exception Value: home/main.html

-> 지정한 경로에 해당 template 파일이 존재하지 않는다! 경로를 잘못 지정해 주었거나 하는 문제일 것이라고 생각했다.

문제 원인

settings.py 파일의 INSTALLED_APPS 부분에 app이 추가되어 있지 않아서 app을 인식하지 못해서, 당연히 app 하위에 있는 templates 폴더도 인식하지 못한 것이었다.

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'home',
    'novels',
]

추가해주니 정상적으로 작동하였다.

이 에러의 다른 가능한 원인으로는 폴더 이름을 잘못 생성했기 때문일 수도 있다. 가장 흔하게는 templatess를 빼고 template라고 쓰는 실수인 듯하다. (처음에는 나도 이것 때문인 줄 알았다.) 가장 상위 폴더>app폴더>templates>app이름>html파일이라는 경로를 반드시 지킬 것!

참고