레이블이 예제인 게시물을 표시합니다. 모든 게시물 표시
레이블이 예제인 게시물을 표시합니다. 모든 게시물 표시

2013년 7월 29일 월요일

[Python] openCV를 이용해 스도쿠 그림 인식 및 계산 예제




다음과 같은 스도쿠 이미지를


위와 같이 refactor 한 후,


점점 contour 를 조정해 가면서 숫자를 알아낸다


혹은 아래처럼 스도쿠판의 네 꼭지점을 먼저 찾아서 스도쿠 범위를 정하는 방법도 있다




그림 인식에는 openCV 가 사용된다.

코드를 한달 전 정도에 짜서 내용도 잘 기억 안나고, 코멘트도 영어로 단 상태인데,

조만간 수정할 계획이다 :)

# author = 'carpedm20'
import cv2
from cv import *
from cv2 import *
import numpy as np

img =  cv2.imread('sudoku.jpg')

# can remove lots of noises by blur effect
gray = cv2.GaussianBlur(gray,(5,5),0)
#cv2.imwrite('gaussian.jpg',gray)

# change color from RGB image to Gray image
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

mask = np.zeros((gray.shape),np.uint8)
kernel1 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(11,11))

close = cv2.morphologyEx(gray,cv2.MORPH_CLOSE,kernel1)
div = np.float32(gray)/(close)
res = np.uint8(cv2.normalize(div,div,0,255,cv2.NORM_MINMAX))
res2 = cv2.cvtColor(res,cv2.COLOR_GRAY2BGR)

# adaptive threshold : taking a best value for a local neighbourhood
# threshold : find treshold taking image as a whole
thresh = cv2.adaptiveThreshold(gray,255,1,1,11,2)
cv2.imwrite('img_thresh.jpg',thresh)

white = Closing[src, DiskMatrix[5]];
srcAdjusted = Image[ImageData[src]/ImageData[white]]

# # of contour by mode
# CV_RETR_EXTERNAL < CV_RETR_LIST < CV_RETR_CCOMP < CV_RETR_TREE
#contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)

biggest = None
#biggest = []
max_area = 0
for i in contours:
 # calculate area of contour
        area = cv2.contourArea(i)
        if area > 100:
  # calculates a contour perimeter or a curve lengt
  # closed : True - Flag indicating whether the curve is closed or not
                peri = cv2.arcLength(i,True)
                #peri = cv2.arcLength(i,False)
  # Approximates a polygonal curves with the specified precision.
                approx = cv2.approxPolyDP(i,0.02*peri,True)
                #approx = cv2.approxPolyDP(i,0.02*peri,False)

                if area > max_area and len(approx)==4:
                #if len(approx)>=4:
                        biggest = approx
                        #biggest.append(approx)
                        max_area = area

# -1 : indicating a contour to draw, negative means all contours
# (0,255,0) : RGB
# 3 : width of countour, negative means fill the countours
cv2.drawContours(img, biggest, -1, (0,255,0), 2)

cv2.imwrite('contour.jpg',img)

def rectify(h):
        h = h.reshape((4,2))
        hnew = np.zeros((4,2),dtype = np.float32)

        add = h.sum(1)
        hnew[0] = h[np.argmin(add)]
        hnew[2] = h[np.argmax(add)]

        diff = np.diff(h,axis = 1)
        hnew[1] = h[np.argmin(diff)]
        hnew[3] = h[np.argmax(diff)]

        return hnew

approx=rectify(biggest)
h = np.array([ [0,0],[449,0],[449,449],[0,449] ],np.float32)

retval = cv2.getPerspectiveTransform(approx,h)
warp = cv2.warpPerspective(gray,retval,(450,450))

cv2.imwrite('warp.jpg',warp)

reference : http://opencvpython.blogspot.in/2012/06/sudoku-solver-part-1.html

[Python] Flask 블로그 예제


python을 언어로 사용하는 웹 프레임워크에는 유명한 Django 가 있지만,

배우는데 시간이걸리고 복잡해서 저는 Flask라는 프레임 워크를 이용해 개인 블로그를 만들어 보았습니다.

아직 개인 서버와 도메인이 없어서 블로그를 옮기지는 않고 있지만, 준비가 되는데로 옮기려고 합니다 :)

혹시 Flask를 이용해 블로그나 웹 사이트를 구현하고 싶은데 참고하고 싶은 예제를 찾고 계시는 분은

https://github.com/carpedm20/personal-blog-powered-by-flask

여기서 소스를 참고하시면 될 것 같습니다

Flask에 대한 기본적인 튜토리얼은

http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world

여기를 참고하시면 됩니다

Framework를 사용해 아무것도 없는 상태에서 글쓰기, 글수정, 사진, markdown 기능, 소스코드 highlighter 등의 기능을 구현해 보면서

블로그라는걸 가볍게 생각하고 만들기 시작하면 안된다는걸 배웠습니다..

특히나 저에게 웹 디자인과 css는 정말 악몽이었습니다...

아래 사진은 블로그를 run한 사진입니다 :)

URL : http://carpedm20.us.to




2013년 5월 18일 토요일

[python] facebook bot 구현, access token 자동 갱신



트위터와는 달리 페이스북 봇에 대한 정보는 인터넷에 거의 없다.

트위터와 페이스북의 성격이 다르다는 점이 주된 이유겠지만

주변에 페이북 유저가 트위터 유저보다 훨씬 많아서 페이스 봇을 만들고자 했다.

먼저 페이스북에 글을 올리기 위해서는


1. Request를 직접 보내는 방법

2. Facebook API를 이용하는 방법


이렇게 두가지 방법이 가능하다.

페이스북은 플래시, 파이썬, 루비, 자바 등의 다양한 언어에 대해 API를 제공하고 있다.

파이썬에서는 Graph API 가 제공되며, 페이스북에서 데이터를 주고 받을때 주로 사용되는 API 이다.


python graph api 링크 : https://github.com/pythonforfacebook/facebook-sdk/

API 공식 reference : https://developers.facebook.com/docs/reference/api/


API를 이용하는 경우 데이터를 올리고 받을때 사용할 계정을 developer로 등록해야 하며

어플리케이션을 하나 만들어서 앱 ID를 가지고 있어야 한다

그리고 나선 어플리케이션이 할 수 있는 행동들의 범위를 지정해 주고 그에 대한 oauth_access_token를 받아야 한다

graph = facebook.GraphAPI(oauth_access_token)
profile = graph.get_object("me")
friends = graph.get_connections("me", "friends")
graph.put_object("me", "feed", message="I am writing on my wall!")

tags = json.dumps([{'x':50, 'y':50, 'tag_uid':12345}, {'x':10, 'y':60, 'tag_text':'a turtle'}])
graph.put_photo(open('img.jpg'), 'Look at this cool photo!', album_id_or_None, tags=tags)


파이썬 기본 예제에서 볼 수 있듯이 oauth_access_token 가 있어야 글을 얻거나 사진을 올릴 수 있다.

하지만 봇을 만들때 문제는 이 access_token이 시간이 지나면 expire 된다는 것이다.


나는 이 문제를 해결하기 위해 access_token을 자동으로 갱신받는 방법을 찾아 봤으며

브라우저를 이용하는 방법 post request를 사용하는 방법 등 여러가지를 시도해봤지만 실패했다.

하지만 방법을 결국 찾았는데 웹 사이트에서 페이스북 api를 사용하고자 할 때

그 웹 사이트에서 api를 사용할 수 있도록 만들어 주는 url을 만들 수 있으며

그 url 로 접속해서 접근하고자 하는 유저로 로그인을 하고 나면 redirect 되는 url에 oauth_access_token 이 주어진다.


아래의 소스는 위의 방법을 구현한 python 소스다.

분명 이 방법보다 쉬운 방법이 있을 수 있으나 내 검색 능력에서는 이게 최선이었다.

혹시라도 나와 같은 어려움을 겪고 있는 사람들을 위해 방법을 공유하고자 이렇게 글을 올린다.

이를 이용하면 재미있는 어플리케이션을 많이 만들 수 있을 것이다.

# author = 'carpedm20'

import mechanize
import facebook

while 1:
        br_mech = mechanize.Browser() # mechanize 를 이용해 브라우징을 한다
        br_mech.set_handle_robots(False) # 페이스북의 로봇 필터에 걸리지 않게 한다

        link='https://www.facebook.com/dialog/oauth?scope=사용하고자 하는 auth 범위
        &redirect_uri=리다이렉트 할 사이트 주소
        &response_type=token&client_id=어플리케이션 ID'

       # link='https://www.facebook.com/dialog/oauth?scope=publish_stream,publish_actions,
       # &redirect_uri=http://carpedm20.blogspot.kr
       # &response_type=token&client_id=어플리케이션 ID'

        #print '[1] open link'
        br_mech.open(link)

        #print '[2] current url : ' + br_mech.geturl()

        br_mech.form = list(br_mech.forms())[0]
        control = br_mech.form.find_control("email")
        control.value='로그인 할 계정 이메일'
        control = br_mech.form.find_control("pass")
        control.value='로그인 할 계정 비밀번호'

        #print '[3] submit'
        br_mech.submit() # 로그인

        #print '[4] current url : ' + br_mech.geturl()

        app_access = br_mech.geturl().split('token=')[1].split('&expires')[0]
        print '[5] access token : ' + app_access # access token 을 파징해서 출력한다
ps. 포탈봇 : https://www.facebook.com/hexa.portal
ps2. 컴공아 일하자 : https://www.facebook.com/comgong.job

2013년 5월 10일 금요일

[python] BeautifulSoup 웹 파징 예제



파징하고자 하는 사이트는 여기 며 궁극적으로 사이트에서 추천하는 샌드위치 상점들의 정보를 파징하는 예제입니다.


from bs4 import BeautifulSoup # 파징을 위한 BeautifulSoup 모듈 임포트
from urllib2 import urlopen # url 처리를 위한 BeautifulSoup 모듈 임포트
import csv # 추출한 정보를 저장하기 위한 csv 모듈 임포트

base_url = ("http://www.chicagomag.com/Chicago-Magazine/"
            "November-2012/Best-Sandwiches-Chicago/")

soup = BeautifulSoup(urlopen(base_url).read())

# div 중 class 가 sammy 인 element 찾기
sammies = soup.find_all("div", "sammy") 

#sammies 안에 있는 div 에 대해서 div 하위의 a 를 찾아 href 값을 저
sammy_urls = [div.a["href"] for div in sammies]



fieldnames = ("rank", "sandwich", "restaurant", "description", "price",
                "address", "phone", "website")

for url in sammy_urls:
    url = url.replace("http://www.chicagomag.com", "")  # inconsistent URL

    # 특별히 하는 이유는 모르겠으나 format 함수를 이용해 스트링 수정 가능
    # 위의 예제에서 파징했던 sammy_urls 링크로 이동
    page = urlopen("http://www.chicagomag.com{0}".format(url))  

    # div 중 id 가 sandwich 인 element 찾아 soup 객체에 저장
    soup = BeautifulSoup(page.read()).find("div", {"id": "sandwich"})

    # soup 객체 안의 div 중 id 가 sandRank 인 element 찾아 soup 객체에 저장
    # strip() : string 의 좌우 공백 제거
    rank = soup.find("div", {"id": "sandRank"}).encode_contents().strip()

    # soup 에서 h1 객체를 "br/" 로 split 한 후 인덱스가 0 인 리스트 저장
    sandwich = soup.h1.encode_contents().strip().split("br/")[0]

    restaurant = soup.h1.span.encode_contents()
    description = soup.p.encode_contents().strip()

    addy = soup.find("p", "addy").em.encode_contents().split(",")[0].strip()

    # partition(seperator) : seperator 로 string 을 
    # 3부분(seperator 전, sperator 자체, seperator 후)으로 나누고 리스트 리턴
    price = addy.partition(" ")[0].strip()
    address = addy.partition(" ")[2].strip()

    phone = soup.find("p", "addy").em.encode_contents().split(",")[1].strip()

    if soup.find("p", "addy").em.a:
        website = soup.find("p", "addy").em.a.encode_contents()
    else:
        website = ""


2013년 5월 9일 목요일

[python] BaseHTTPServer 웹 서버의 구현



웹 서버는 작동 원리를 이해하기 어렵지 않다:

  • 클라이언트(브라우저)가 웹서버와 접속을 하고 HTTP GET 혹은 POST 방식으로 request 를 전송한다 (요청하고자 하는 url(경로)와 쿠키 등 의 정보가 request에 포함된다)
  • 서버측에서는 전송된 request를 파징한다. (경로 (예. /some/file), 쿠키 등.) 그리고 HTTP 코드로 respond 를 전송하며 (404 코드 : 찾을 수 없음, 200 코드 : 완료) 웹 페이지의 내용도 전송한다 (html 소스, 이미지...)
브라우저
(HTTP 클라이언트)
GET /path/hello.html HTTP/1.1
Host: www.myserver.com
서버
(HTTP 서버)
--------->
HTTP/1.1 200 OK
Content-Type: text/html
<html><body>Hello, world !</body></html>
<---------

파이썬의 BaseHTTPServer 모듈을 사용하면 위의 프로세스를 쉽게 작성할 수 있다.
아래의 웹서버 소스는 http://localhost:8088/ 로 접속시 "Hello, world !" 를 출력하는 예제다.


#!/usr/bin/python
import BaseHTTPServer

class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)            # 200 코드 : request 를 잘 받았다는 의미        
        self.send_header('Content-type','text/html')   # respond 헤더 추가
        self.end_headers()                          # respond 헤더의 끝을 추가 ( "\n\r" )
        self.wfile.write('<html><body>Hello, world !</body></html>')
        return

print "Listening on port 8088..."
server = BaseHTTPServer.HTTPServer(('', 8088), MyHandler)
server.serve_forever()
  • MyHandler  가 우리가 만들고자 하는 웹서버 응답 핸들러이며, 정해진 포트로  request가 들어온 경우 그에 대한 응답을 돌려 준다.
  • GET 방식의 request 만 처리한다 (do_GET).
  • 코드 200의 의미는 request를 잘 받았다는 뜻이다. (self.send_response(200)).
  • 헤더를 추가해서 브라우저에 request 를 전송한다고 알려준다 (self.send_header('Content-type','text/html')).
  • 그리고 HTML 소스 자체를 출력한다 (self.wfile.write(...))

2013년 5월 3일 금요일

[python] 파이썬에서 bash 명령어 실행 subprocess, pexpect


subprocess 모듈은 파이썬에서 새로운 프로세스를 만들고 인풋, 아웃풋, 에러 파이프를 연결 시켜준다.

pexpect 모듈을 사용해서도 쉘과 상호작용할 수 있습니다.

# subprocess  모듈 사용
import subprocess

subprocess.call(['ls', '-l'])  # 명령어 실행
subprocess.check_call(['ls', '-l'])  # 명령어 실행 확인

subprocess.check_output(['ls', '-l'])  # 아웃풋 string 으로 출력

# pexpect 모듈 사용
import pexpect
child = pexpect.spawn ('ftp ftp.openbsd.org')

# ftp 실행 시 Name (ftp.openbsd.org:carpedm20): 로 입력 받음 -> 정규표현식 Name.* 사용
# 단순하게 child.expect(".*") 로도 사용 가능
child.expect ('Name .*: ')

# 인풋값 입력
child.sendline ('anonymous')

child.expect ('Password:')
child.sendline ('noah@example.com')
child.expect ('ftp> ')
child.sendline ('cd pub')
child.expect('ftp> ')
child.sendline ('get ls-lR.gz')
child.expect('ftp> ')
child.sendline ('bye')

print child.before   # 이전 명령어 실행으로 나온 아웃풋 출력

try:child.interact()    # 유저에게 컨트롤을 넘겨줌
except:pass

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);
    }
}