2013년 4월 12일 금요일

[python] SMTP email 전송 예제

python 공식 홈페이지에는 상당히 흥미로운 예제들이 많이 있다.

이것저것 구경하던 중에 smtplib 모듈을 활용해 이메일을 보내는 예제들이 몇개 있어

코드를 분석해 보고자 한다.

1. 간단한 이메일 전송

# 이메일을 보내기 위한 smtplib 모듈을 import 한다
import smtplib

# 이메일을 보내기 위한 email 모듈을 import 한다
# MIME (Multipurpose Internet Mail Extensions) 는 전자우편을 위한 인터넷 표준이라고 한다.
from email.mime.text import MIMEText

# 텍스트 문서로 구성되어 있는 파일을 읽는다.
# 여기서는 텍스트 파일이 ASCII  문자로만 구성되어 있다고 가정한다.
fp = open(textfile, 'rb')

# utf-8로 인코딩 된 파일을 읽고자 하는 경우
# import codecs
# fp = codecs.open(textfile, 'rb', 'utf-8')
# 로 읽어들이면 될 듯 하다.

# 읽어들인 파일의 텍스트를 MIME 형식으로 바꾼다.
msg = MIMEText(fp.read())
fp.close()

# me == 보내는 사람의 이메일 주소
# you == 받는 사람의 이메일 주소
msg['Subject'] = 'The contents of %s' % textfile # 이메일 제목
msg['From'] = me
msg['To'] = you

# 로컬 SMTP 서버를 사용해서 메세지를 보낸다.
# 이 예제에서는 Header 는 추가하지 않는다.
s = smtplib.SMTP('localhost')
s.sendmail(me, [you], msg.as_string())
s.quit()

# 로컬 SMTP 서버가 없을 경우 계정이 있는 다른 서버를 사용하면 된다.
s = smtplib.SMTP_SSL('smtp.gmail.com',465)
s.login("아이디", "비밀번호")
s.sendmail(me, you, msg.as_string())
s.quit()

2. 디렉토리에 존재하는 이미지 파일을 이메일에 첨부하는 방법
# 이메일을 보내기 위한 smtplib 모듈을 import 한다
import smtplib

# 이메일에 이미지를 첨부하기 위한 모듈들을 import 한다
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart

COMMASPACE = ', '

# 이메일 메세지 컨테이너를 만든다
msg = MIMEMultipart()
msg['Subject'] = 'Our family reunion'

# me == 보내는 사람의 주소
# family = 받는 사람들의 모든 주소
msg['From'] = me
msg['To'] = COMMASPACE.join(family) # join 함수로 받는 사람들의 주소를 합친다
msg.preamble = 'Our family reunion'

# 전송하고자 하는 이미지 파일들이 모두 PNG 파일이라고 가정하자
for file in pngfiles:
    # 바이너리 모드로 전송할 파일들을 연다.
    # MIMEImage 클래스가 자동으로 이미지의 타입을 알아낼 것이다.
    fp = open(file, 'rb')
    img = MIMEImage(fp.read())
    fp.close()
    msg.attach(img)

# 로컬 서버를 통해 메일을 보낸다.
s = smtplib.SMTP('localhost')
s.sendmail(me, family, msg.as_string())
s.quit()

# 로컬 SMTP 서버가 없을 경우 계정이 있는 다른 서버를 사용하면 된다.
s = smtplib.SMTP_SSL('smtp.gmail.com',465)
s.login("아이디", "비밀번호")
s.sendmail(me, you, msg.as_string())
s.quit()

3. 디렉토리내의 모든 파일들을 이메일로 전송하는 방법
#!/usr/bin/env python

import os
import sys
import smtplib
# 파일 확장자를 통해서 MIME 형식을 guessing 하기 위해 mimetypes 모듈을 임포트 한다.
import mimetypes

from optparse import OptionParser

from email import encoders
from email.message import Message
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

COMMASPACE = ', '

def main():
    parser = OptionParser(usage="""\
디렉토리 내의 파일들을 MIME 메세지로 전송함.

Usage: %prog [options]

-o 옵션을 주지 않는다면, 로컬 SMTP 서버를 통해 메일을 전송한다.
이 경우, 로컬 머신에서는 SMTP 서버를 꼭 사용해야 한다.
""")
    parser.add_option('-d', '--directory',
                      type='string', action='store',
                      help="""전송할 파일들이 있는 디렉토리 지정.
                      서브 디렉토리의 파일은 전송되지 않음.""")
    parser.add_option('-o', '--output',
                      type='string', action='store', metavar='FILE',
                      help="""압축될 파일들을 메일로 전송하지 않고 파일로 저장함""")
    parser.add_option('-s', '--sender',
                      type='string', action='store', metavar='SENDER',
                      help='보내는 사람의 메일 주소 (필요)')
    parser.add_option('-r', '--recipient',
                      type='string', action='append', metavar='RECIPIENT',
                      default=[], dest='recipients',
                      help='받는 사람의 메일 주소 (하나 이상 필요)')

    opts, args = parser.parse_args()

    # argument 가 설정되어 있지 않다면 help 메세지를 print 하고 종료
    if not opts.sender or not opts.recipients:
        parser.print_help()
        sys.exit(1)

    # argument 를 바탕으로 디렉토리 설정
    directory = opts.directory
    if not directory:
        directory = '.'

    # MIMEMultipart 클래스를 생성
    outer = MIMEMultipart()
    outer['Subject'] = '%s 디렉토리의 파일' % os.path.abspath(directory)
    outer['To'] = COMMASPACE.join(opts.recipients)
    outer['From'] = opts.sender
    outer.preamble = 'You will not see this in a MIME-aware mail reader.\n'

    # 디렉토리에 존재하는 파일 이름의 리스트에서 for
    for filename in os.listdir(directory):
        path = os.path.join(directory, filename) # 디렉토리와 파일 이름을 합치고, path 에 저장
        if not os.path.isfile(path):
            continue

        # 파일의 확장자를 바탕으로 파일 내용을 guess 한다.
        # 인코딩은 무시되지만, gzip 과 같이 간단한 파일들은 체크해야 한다.
        ctype, encoding = mimetypes.guess_type(path)

        if ctype is None or encoding is not None:
            # 파일 타입을 확인할 수 없거나 인코딩 혹은 압축 되어 있을때는
            # 'octet-stream' 로 정의한다.
            ctype = 'application/octet-stream'

        # ctype 을 '/' 로 나눈다. split 의 두번째 parameter 는 나눌 최대 횟수를 의미한다
        maintype, subtype = ctype.split('/', 1)

        if maintype == 'text': # 텍스트 타입 처리
            fp = open(path)
            msg = MIMEText(fp.read(), _subtype=subtype)
            fp.close()
        elif maintype == 'image': # 이미지 타입 처리
            fp = open(path, 'rb')
            msg = MIMEImage(fp.read(), _subtype=subtype)
            fp.close()
        elif maintype == 'audio': # 오디오 타입 처리
            fp = open(path, 'rb')
            msg = MIMEAudio(fp.read(), _subtype=subtype)
            fp.close()
        else:
            fp = open(path, 'rb')
            msg = MIMEBase(maintype, subtype)
            msg.set_payload(fp.read())
            fp.close()
            # Encode the payload using Base64
            encoders.encode_base64(msg)

        # 파일 이름을 msg 헤더로 추가한다.
        msg.add_header('Content-Disposition', 'attachment', filename=filename)
        outer.attach(msg)

    # 파일로 저장하거나, 메일로 전송한다.
    composed = outer.as_string()
    if opts.output:
        fp = open(opts.output, 'w')
        fp.write(composed)
        fp.close()
    else:
        s = smtplib.SMTP('localhost')
        s.sendmail(opts.sender, opts.recipients, composed)
        s.quit()


if __name__ == '__main__':
    main()
reference : http://docs.python.org/2/library/email-examples.html

2013년 4월 3일 수요일

[Python] BeautifulSoup를 이용한 웹 페이지 파징


웹 페이지 파징을 쉽고 빠르게 하는 방법 중 하나는

파이썬의 BeautifulSoup 모듈을 사용하는 것 입니다.

링크 : http://www.crummy.com/software/BeautifulSoup/
Documentation : http://www.crummy.com/software/BeautifulSoup/bs4/doc/

soup.findAll('a',{'class':'class_name'}) 이 함수만 보더라도

충분히 BeautifulSoup 의 강점을 느끼실 수 있을겁니다.

아래는 urllib2 모듈과 BeautifulSoup 모듈을 이용한 간단한 파징 예제 입니다.

from BeautifulSoup import BeautifulSoup
import urllib2

url="http://웹페이지 주소"

page=urllib2.urlopen(url)
soup = BeautifulSoup(page.read())

# 'a' 태그 중에서 class 이름이 class_name 인 element 를 저장
elements=soup.findAll('a',{'class':'class_name'})

for element in elements:
    print element['href']+","+ element.string

[Python] 윈도우 gui 자동화 pywinauto


윈도우 Gui 프로그램을 자동화 할 수 있는 파이썬 모듈이 있었네요.



링크 : http://code.google.com/p/pywinauto/
Documentation : http://pywinauto.googlecode.com/hg/pywinauto/docs/getting_started.html

공식 페이지에 있는 예제 소스를 분석해 봤습니다.
재미있는 프로그램을 만들 수 있을거 같네요 :)

# pywinauto.application 모듈 임포트
from pywinauto import application

# 어플리케이션 인스턴스 생성
app = application.Application()

# 어플리케이션_인스턴스.Start("실행하고자 하는 프로그램 이름"")
app.Start_("Notepad.exe")


app.Notepad.DrawOutline()

# 메모장 Edit 메뉴에서 Replace 메뉴 클릭
app.Notepad.MenuSelect("Edit -> Replace")

# 열린 Replace dialog 에서 사용할 수 있는 버튼 혹은 메뉴 출력
app.Replace.PrintControlIdentifiers()

# 결과 
# app.Replace.Edit
# app.Replace.Edit0
# app.Replace.Edit1
# app.FindwhatEdit

# Replace 다이얼로그 닫기
app.Replace.Cancel.Click()

# 메모장에 입력
app.Notepad.Edit.TypeKeys("Hi from Python interactive prompt %s" % str(dir()), with_spaces = True)

# File 메뉴에 Exit 버튼 클릭
app.Notepad.MenuSelect("File -> Exit")

# 저장 할 것인지에 대한 질문에 No 클릭
app.Notepad.No.Click()

[HTML5, jQuery] 비디오 및 자막 제어


HTML5 에서 새로 추가된 video 태그를 사용하면,

동영상을 웹에서 쉽게 보여줄 수 있고, 또한 자막도 함께 보여줄 수 있습니다.

아래 소스는 로컬에 저장된 자막파일을 읽어, 단어를 검색하고

또 video element 의 currentTime 속성을 통해 재생되고 있는 동영상의 재생 위치를

바꾸는 소스 코드입니다 :)



<!-- jQuery 사용 선언 



function setTime(){
 myVid=document.getElementById('video1');
 myVid.currentTime=$('#searchTime').val();
}

$(document).ready(function() {

 $('#searchText').val("이론");
 $('#searchTime').val("10");

        
 $('#btn').click(function(){
  var msg = $('#searchText').val();

                
  jQuery.get('sub.vtt', function(data) {
   var lines = data.split("\n");

   $.each(lines, function(n, elem) {
    if(elem.indexOf(msg) != -1) {
     $('#end').append('
' + elem + '
'); } }); }); }); } ); 검색 : 시간 이동 :

[php] 소켓 전송 및 파징 예제


php 에서 소켓을 생성하고, 호스트(google.com) 에 http request를 보낸 후

받은 response 를 출력하는 예제 입니다.

response 를 DOMDocument 인스턴스에 로드를 하면

getElementById 와 같은 함수를 통해 웹페이지를 쉽게 파징하실 수 있습니다.

$c = fsockopen('www.google.com', 80, $errno, $errstr, 30);

if (!$c)
 die ("Couldn't connect to google.com");

$header .= "Host: www.google.com\r\n";
$header .= "User-Agent: Mozilla/5.0 (Linux; U; Android 4.0.4; en-gb; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30\r\n\r\n";
fwrite($c, $header);

while (!feof($c))
 $file.=fgets($c,128);

$dom = new DOMDocument();
$dom->loadHTML(''.$file);

// echo $file;
// echo "".substr($file, strpos($file,"";

// 활용 예제
foreach ($dom->getElementById('ires')->getElementsByTagName('li') as $child) {
 $item = new SearchItem();

 $element = $child->getElementsByTagName('h3')->item(0);
 $title = $element->nodeValue;
 $item->title = $title;
}

[C#] xml 파징 예제


기상청에서 제공하는 날씨 api를 통해서

C# 에서 xml 문서를 파징하는 예제입니다.

단순하게 XmlDocument 객체를 생성하고, 로드할 xml 문서 주소를 넘겨주고

GetElementsByTagName 과 같은 함수를 이용해서 노드리스트를 얻으실 수 있습니다 :)

class Weather
{
    // http://www.kma.go.kr/wid/queryDFS.jsp?gridx=98&gridy=84  기상청 날씨 xml 이용
    public string weather = "";

    public Weather()
    {
        getWeather();
    }

    public void getWeather() {
        XmlDocument docX = new XmlDocument(); // XmlDocument 생성

        try
        {
            docX.Load("http://www.kma.go.kr/wid/queryDFS.jsp?gridx=98&gridy=84"); // url로 xml 파일 로드

        }
        catch
        {
            return;
        }

        XmlNodeList hourList = docX.GetElementsByTagName("hour"); // 태그 이름으로 노드 리스트 저장
        XmlNodeList tempList = docX.GetElementsByTagName("temp");
        XmlNodeList weatherList = docX.GetElementsByTagName("wfKor");

        // 활용 예제
        weather = "   = 울산 날씨 =\n";
        weather += hourList[0].InnerText + "시 : " + weatherList[0].InnerText + " (" + tempList[0].InnerText + "℃)\n";
        weather += hourList[1].InnerText + "시 : " + weatherList[1].InnerText + " (" + tempList[1].InnerText + "℃)\n";
        weather += hourList[2].InnerText + "시 : " + weatherList[2].InnerText + " (" + tempList[2].InnerText + "℃)\n";
        weather += hourList[3].InnerText + "시 : " + weatherList[3].InnerText + " (" + tempList[3].InnerText + "℃)\n";
        // weather += hourList[4].InnerText + "시 : " + weatherList[4].InnerText + "(" + tempList[4].InnerText + "℃)\n";
        // weather += hourList[5].InnerText + "시 : " + weatherList[5].InnerText + "(" + tempList[5].InnerText + "℃)\n";
    }
}

[Python] IRC 봇 예제


요즘은 거의 사용되지 않는 IRC 채팅 서버 봇 예제입니다.

python 의 socket 을 통해 쉽게 구현하실 수 있습니다.

IRC 프로토콜이 그다지 어렵지 않기 때문에 IRC 서버의 채널에 접속하신 후에는

prefix 구분을 통해 여러가지 행동을 하는 봇을 쉽게 만드실 수 있을겁니다 :)

import socket
import requests
import time

network = '호스트 주소'
port = 6667
prefix = '~'

# 소켓 생성
irc = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
# 소켓 호스트와 연결
irc.connect ((network, port))
# 리턴값 출력
print irc.recv (4096)

# irc에서 사용할 닉네임 설정
irc.send ('NICK pythonbot'+str(j)+'\n')
irc.send ('USER python'+str(j)+' python python :python bot\n')

# 채널 접속
irc.send ( 'JOIN #접속하고자_하는_채널_이름\r\n' )

# 접속 후 irc 채널에서 메세지 입
irc.send ('PRIVMSG #hexa :Hi, hexa\n')

while True:
 # 채널로부터 데이터를 받은 경우
 data = irc.recv (4096)

 # 받은 데이터가 메세지일 경우, 데이터를 split 해서 출력
 data.find ( 'PRIVMSG' ) != -1:
      nick = data.split ( '!' ) [ 0 ].replace ( ':', '' )
      message = ':'.join ( data.split ( ':' ) [ 2: ] )
      print nick + ':', message 

[Python] 웹브라우징 모듈 twill 예제


twill 다운로드 : http://twill.idyll.org

twill 은 로그인과 같은 행동이 필요한 웹사이트에 접속해서,

formvalue(~) 와 같은 함수로  input 을 쉽게 넣을 수 있는 모듈입니다.

파이썬의 다른 모듈과 같이 사용하면

재미있는 웹 사이트 자동화 프로그램을 쉽게 만드실 수 있을거 같네요.

# twill 임포트

from twill.commands import *
import time
import string

while True:
        for i in range(3):
                date=str(now.tm_year)+string.zfill(now.tm_mon, 2)+str(now.tm_mday+i)
                url='http://브라우징 할 웹사이트 주소'
                go(url) # 사이트로 이동

                # python 에서 showforms() 함수를 통해 폼의 형태, 인풋, 클릭할 수 있는 오브젝트의 임의 번호를 알 수 있음

                # fv("폼 번호", 입력할 element 번호, 입력할 값)

                fv("2", 2, "123") # twill 이 정해준 번호의 element에 입력
               # formvalue('loginform', userid, "id") 와 같이 elemnt id로 입력할 수 도 있음
                fv("2", 3, "456")

                submit('4') # twill 이 정해준 번호의 element 클릭

                go(url)


[C#] HttpWebRequest 로 http GET, POST 전송 및 처리


C# 에서 GET 혹은 POST 방식으로 http 리퀘스트를 보내는 예제입니다.

HttpWebRequest 인스턴스의 CookieContainer 를 이용하면 쿠키도 설정할 수 있습니다.

한글 값을 POST 방식으로 전송할 경우 UTF8 인코딩을 해서

바이트 데이터로 바꾼 후 HttpWebRequest 에 추가해 주시면 됩니다.

HttpWebRequest wReq;
HttpWebResponse wRes;

private bool getResponse(String url, string cookie, string data)
{
    string cookie;

    try
    {
        uri = new Uri(url); // string 을 Uri 로 형변환
        wReq = (HttpWebRequest)WebRequest.Create(uri); // WebRequest 객체 형성 및 HttpWebRequest 로 형변환
        wReq.Method = "GET"; // 전송 방법 "GET" or "POST"
        wReq.ServicePoint.Expect100Continue = false;
        wReq.CookieContainer = new CookieContainer(); 
        wReq.CookieContainer.SetCookies(uri, cookie); // 넘겨줄 쿠키가 있을때 CookiContainer 에 저장

        /* POST 전송일 경우
        byte[] byteArray = Encoding.UTF8.GetBytes(data);

        Stream dataStream = wReq.GetRequestStream();
        dataStream.Write(byteArray, 0, byteArray.Length);
        dataStream.Close();
        */

        using (wRes = (HttpWebResponse)wReq.GetResponse())
        {
            Stream respPostStream = wRes.GetResponseStream();
            StreamReader readerPost = new StreamReader(respPostStream,  Encoding.GetEncoding("EUC-KR"), true);

            resResult = readerPost.ReadToEnd();
        }

        return true;
    }
    catch (WebException ex)
    {
        if (ex.Status == WebExceptionStatus.ProtocolError && ex.Response != null)
        {
            var resp = (HttpWebResponse)ex.Response;
            if (resp.StatusCode == HttpStatusCode.NotFound)
            {
                // 예외 처리
            }
            else
            {
                // 예외 처리
            }
        }
        else
        {
            // 예외 처리
        }

        return false;
    }
}


// 활용 예제 
private void searchBoard(PortalBoard[] board, string boardId, int sPage, int ePage, string query)
{
    MainForm.gridView.Columns[4].HeaderText = "조회수";

    for (int i = 0; i < 10 * 10; i++)
    {
        board[i] = new PortalBoard();
    }

    for (int pageNum = sPage; pageNum <= ePage; pageNum++)
    {
        byte[] b = System.Text.Encoding.GetEncoding(51949).GetBytes(query);
        string result = "";

        foreach (byte ch in b)
        {
            result += ("%" + string.Format("{0:x2} ", ch)); // EUC-KR 인코딩
        }

        string url = "http://portal.unist.ac.kr/EP/web/collaboration/bbs/jsp/BB_BoardLst.jsp?boardid="
            + boardId + "&nfirst=" + pageNum 
            + "&searchcondition=BULLTITLE&searchname=" + result.Replace(" ","");

        if (!getResponse(url)) // HttpWebRequest 전송
            return;

        doc = (IHTMLDocument2)new HTMLDocument(); // 파징을 위해 IHTMLDocument2 객체 생성
        // IHTMLDocument1, IHTMLDocument2, IHTMLDocument3, IHTMLDocument4
        // 데이터형 마다 사용할 수 있는 함수 종류 다름. msdn 참고

        doc.clear();
        doc.write(resResult);
        doc.close();

        IEnumerable titles = ElementsByClass(doc, "ltb_left");    
        IEnumerable elements = ElementsByClass(doc, "ltb_center");

        // 파징 후 각자 활용
    }
}

[C#] WebBrowser 컨트롤 파징 예제


C# 에서 간단한 웹 브라우징 어플을 만들때 사용하는 WebBrowser 컨트롤 이용해서

소켓을 보내는 것 보다 간단한 파징을 하실 수 있습니다.

WebBrowserReadyState.Complete 를 이용해 브라우징이 완료되길 기다리고

웹브라우저 객체의 Document 속성을 HtmlDocument 로 형 변환하시면 됩니다.

public void getCourceMenu()
{
    HtmlDocument doc = null; // HtmlDocument 오브젝트

    for (int i = 0; i < board.Count(); i++)
    {
        string url = "http://bb.unist.ac.kr/webapps/blackboard/content/courseMenu.jsp?course_id=" + board[i].url;

        browser.Navigate(url); // 이동

        while (browser.ReadyState != WebBrowserReadyState.Complete)
        {
            Application.DoEvents(); // 웹페이지 로딩이 완료될 때 까지 대기
        }

        doc = browser.Document as HtmlDocument; 
        HtmlElementCollection options = doc.GetElementsByTagName("Option");

        HtmlElement elements = ElementsByClass(doc, "courseMenu").ElementAt(0); // 아래에 정의된 함수 이용
        HtmlElementCollection lists = elements.GetElementsByTagName("li"); // 태그 이름으로 파징
        HtmlElementCollection a = elements.GetElementsByTagName("a");

        // 파징한 데이터 활용 예
        board[i].menu = new List();
        board[i].menuUrl = new List();

        for (int j = 0; j < lists.Count; j++)
        {
            board[i].menu.Add(lists[j].InnerText);
            board[i].menuUrl.Add(a[j].GetAttribute("href"));
        }
    }

    addCourseMenu();
}

static IEnumerable ElementsByClass(HtmlDocument doc, string className)
{
    foreach (HtmlElement e in doc.All)
        // GetAttribute 함수를 통해 클래스 이름으로 IEnumerable 오브젝트를 얻음
        if (e.GetAttribute("className") == className) 
            yield return e; // 찾은 e 를 리턴할 IEnumerable 에 추가
}

[C#] 레지스트리 등록 예제 (자동 로그인)


C# 에서 윈도우 레지스트리를 추가하는 예제입니다.

보통 아이디와 비밀번호 입력할 때 자주 사용될 거 같은데,

클래스를 그대로 복사해서 사용하시면 될 거 같습니다.

RegistryKey 인스턴스를 만들고, CurrentUser\Software\ 경로에 키를 등록하면 됩니다.

using Microsoft.Win32;

class AutoLogin
{
    public AutoLogin()
    {

    }

    public bool ReadRegistry(ref string id, ref string pw)
    {
        RegistryKey reg = Registry.CurrentUser.CreateSubKey("SoftWare").CreateSubKey("robot_carpedm20");

        id = reg.GetValue("ID", "").ToString();
        pw = reg.GetValue("PW", "").ToString();

        if (id == "" || id == "" && pw == "")
            return false;
        else
            return true;
    }

    public void WriteRegistry(string id, string pw)
    {
        RegistryKey reg = Registry.CurrentUser.CreateSubKey("SoftWare").CreateSubKey("robot_carpedm20");
        // RegistryKey reg = Registry.LocalMachine.CreateSubKey("SoftWare").CreateSubKey("robot_carpedm20");
        // LocalMachine 에 등록할 경우 프로그램 실행 시 관리자 권한 필요

        reg.SetValue("ID", id);
        reg.SetValue("PW", pw);
    }
}