페이스북 억세스 토큰을 얻는 것은 트위터에 비해 조금 귀찮습니다.
브라우저를 사용해야 하기 때문입니다~
 

페이스북 억세스 토큰을 얻어내는 코드에 파이썬 브라우저 모듈인 mechanize 를 붙여보았습니다



# vi: set sw=4 sts=4 expandtab:
import os
import os.path
import json
import urllib2
import urllib
import urlparse
import BaseHTTPServer
import webbrowser
import mechanize
CODE_BEGIN = 'code='
CODE_END = '"'

APP_ID = '페이스북_앱_아이디'
APP_SECRET = '페이스북_앱_시크릿'
ENDPOINT = 'graph.facebook.com'
REDIRECT_URI = '콜백처리_URL'
ACCESS_TOKEN = None
LOCAL_FILE = '.fb_access_token'

STATUS_TEMPLATE = u"{name}\033[0m: {message}"

def get_url(path, args=None):
args = args or {}
if ACCESS_TOKEN:
args['access_token'] = ACCESS_TOKEN
if 'access_token' in args or 'client_secret' in args:
endpoint = "https://"+ENDPOINT
else:
endpoint = "http://"+ENDPOINT
return endpoint+path+'?'+urllib.urlencode(args)

def get(path, args=None):
return urllib2.urlopen(get_url(path, args=args)).read()


class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):

def do_GET(self):
global ACCESS_TOKEN
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()

code = urlparse.parse_qs(urlparse.urlparse(self.path).query).get('code')
code = code[0] if code else None
if code is None:
self.wfile.write("Sorry, authentication failed.")
sys.exit(1)
response = get('/oauth/access_token', {'client_id':APP_ID,
'redirect_uri':REDIRECT_URI,
'client_secret':APP_SECRET,
'code':code})
ACCESS_TOKEN = urlparse.parse_qs(response)['access_token'][0]
open(LOCAL_FILE,'w').write(ACCESS_TOKEN)
self.wfile.write("You have successfully logged in to facebook. "
"You can close this window now.")

def print_status(item, color=u'\033[1;35m'):
print color+STATUS_TEMPLATE.format(name=item['from']['name'],
message=item['message'].strip())


def main(email, password):
url = get_url('/oauth/authorize',
{'client_id':APP_ID,
'redirect_uri':REDIRECT_URI,
'scope':'read_stream'})

browser = mechanize.Browser()
browser.set_handle_refresh(False)
browser.set_handle_robots(False)
cookies = mechanize.CookieJar()
browser.set_cookiejar(cookies)
response = browser.open(url)
open("response.html", "wb").write(response.get_data())
browser.addheaders = [('User-agent', 'Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.517.41 Safari/534.7')]


try:
browser.select_form(predicate=lambda f:"login.php" in f.action and f.method.lower() == "post")
except mechanize.FormNotFoundError:
print "WRONG_LOGIN_PAGE"
return -1

browser.form["email"] = email
browser.form["pass"] = password

response = browser.submit()
responseData = response.read()
relocationPos = responseData.find("window.location.replace")
open("response2.html", "wb").write(responseData)

if relocationPos < 0:
try:
browser.select_form(predicate=lambda f:f.action.endswith("uiserver.php") and f.method.lower() == "post")
except mechanize.FormNotFoundError:
print "RETRY_LOGIN_LATER"
return -2

response = browser.submit(name="grant_clicked")
responseData = response.read()
relocationPos = responseData.find("window.location.replace")
open("response3.html", "wb").write(responseData)


if relocationPos > 0:
tagBeginPos = responseData.find(CODE_BEGIN, relocationPos)
if tagBeginPos > 0:
tagEndPos = responseData.find(CODE_END, tagBeginPos)

code = responseData[tagBeginPos+len(CODE_BEGIN):tagEndPos].decode("unicode-escape")

response = get('/oauth/access_token', {'client_id':APP_ID,
'redirect_uri':REDIRECT_URI,
'client_secret':APP_SECRET,
'code':code})
ACCESS_TOKEN = urlparse.parse_qs(response)['access_token'][0]
open(LOCAL_FILE,'w').write(ACCESS_TOKEN)
print "LOGIN_SUCCESS"
return 0
else:
print "UNKNOWN_REDIRECT"
return -4
else:
print "INVALID_CODE"
return -3

if __name__ == '__main__':
import sys
if len(sys.argv) >= 3:
sys.exit(main(sys.argv[1], sys.argv[2]))
else:
print "USAGE:"
print "\t%s [email] [password]" % os.path.split(sys.argv[0])[1]


UNKNOWN_REDIRECT 는 뭔가 페이스 북이 특수 기능 - 이메일 친구 스캔 하기 - 을 처리하기 위해

로그인 절차와는 상관없는 페이지를 보여주는 경우가 있습니다. 이 때는 진짜 웹 브라우저를 띄워서 처리해주는 수밖에 없을 듯 합니다.

끝 ~(-_-)~

이올린에 북마크하기(0) 이올린에 추천하기(0)
2011/03/31 17:26 2011/03/31 17:26

파이썬에서 트위터 날리기 tweepy

Posted at 2011/03/31 17:11// Posted in python/py-sns

tweepy 를 사용해서 트위터 OAuth 로그인을 한 다음 타임 라인을 보는 예제입니다~


USERNAME = "아이디"
PASSWORD = "비밀번호"

import tweepy

import re
roToken = re.compile("<\w+|>|/>|</\w+>|\w+|=|\"[^\"]+\"")

def GetTagAttrd(tokens, beginTag, endTag, curIndex, endIndex):
itokens = []
try:
curIndex = tokens.index(beginTag, curIndex)
except ValueError:
return curIndex, None

if curIndex >= endIndex:
return endIndex, None

while tokens[curIndex] != endTag:
itokens.append(tokens[curIndex])
curIndex += 1

return curIndex, dict(zip(itokens[1::3], [val[1:-1] for val in itokens[3::3]]))

from urllib import *
from urllib2 import *

def Main():
CONSUMER_KEY = '트위터_앱_키'
CONSUMER_SECRET = '트위터_앱_시크릿'

auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
aurl = auth.get_authorization_url()
data = urlopen(aurl).read()
lines = data.splitlines()
tokens = []
for line in lines:
tokens += roToken.findall(line)

curIndex, formd = GetTagAttrd(tokens, "<form", ">", 0, len(tokens))
endIndex = tokens.index("</form>", curIndex)
actionURL = formd["action"]

valued = {}
while True:
curIndex, inputd = GetTagAttrd(tokens, "<input", "/>", curIndex, endIndex)
if inputd:
if inputd["type"] == "hidden":
valued[inputd["name"]] = inputd["value"]
elif inputd["type"] == "text":
valued[inputd["name"]] = USERNAME
elif inputd["type"] == "password":
valued[inputd["name"]] = PASSWORD
elif inputd["type"] == "submit":
valued["submit"] = inputd["value"]
else:
print inputd
else:
break

params = urlencode(valued)
req = Request(actionURL, params)
res = urlopen(req)
data = res.read()
lines = data.splitlines()
tokens = []
for index, line in enumerate(lines):
if "div" in line and "oauth_pin" in line:
index += 1
break

verifier = lines[index].strip()
auth.get_access_token(verifier)

api = tweepy.API(auth)

if 0:
api.update_status(u"~tweepy test")
else:
for s in api.user_timeline():
print s.text.encode("utf8")

Main()
이올린에 북마크하기(0) 이올린에 추천하기(0)
2011/03/31 17:11 2011/03/31 17:11

mac python 32bit 실행

Posted at 2010/11/29 20:49// Posted in python
$ vim .vimrc

export VERSIONER_PYTHON_PREFER_32_BIT=yes


위의 내용을 추가한다
이올린에 북마크하기
2010/11/29 20:49 2010/11/29 20:49

파이썬 설치 경로 알아내기

Posted at 2010/05/31 15:50// Posted in python/pyutils
작년부터 자동화된 빌드를 위해 파이썬 대신 파이널 빌더를 사용하고 있습니다.

cpascal 님 추천으로 사용하기 시작했는데, 스케쥴러를 사용해서 데일리 빌드도 할 수 있고

트레이에서 명령을 내릴 수도 있어서 꽤나 유용하게 사용하고 있습니다.


그런데 파이썬으로 만든 도구들을 실행할때가 문제더군요.

파이널 빌더에서도 파이썬 스크립트를 지원하긴 하는데,

임베딩 느낌인데다 사용 방법이 불편해서

지금까지는 그냥  "c:\python26 에 무조건 설치해라!" 라고 강요했었는데...;

요즘들어 cpascal 님을 비롯해서 여러 분들이 지나친 강요는 반발을 불러일으킨다라는 조언을 해주셔서

왼손잡이를 위한 방법을 찾아보기로 했습니다.


Modifying the module search path can also be done through the Windows registry under the keyHKLM\SOFTWARE\Python\PythonCore\version\PythonPath. Subkeys which have semicolon-delimited path strings as their default value will cause each path to be searched. Multiple subkeys can be created and are appended to the path in alphabetical order. A convenient registry editor is regedit (start it by typing “regedit” into Start ‣ Run).

메뉴얼 상에는 HKLM\SOFTWARE\Python\PythonCore\version\PythonPath 로 나와있지만, 이곳에는 다양한 모듈 경로가 들어있기 때문에

C:\Python\Python26\Lib;C:\Python\Python26\DLLs;C:\Python\Python26\Lib\lib-tk

HKLM\SOFTWARE\Python\PythonCore\version\InstallPath 를 사용하는 편이 더 좋아 보입니다~

C:\Python\Python26\

파이썬은 여러개 설치될 수 있기 때문에 어느 것이 디폴트 버전인가도 중요한 이슈입니다만


윈도우 버전은 확장자-아이콘 연결을 통해 알아내는 방법밖에 안 나오는군요.


일단 파이널 빌더에서는 지정한 버전을 실행하면 되니까 윈도우에서

정확한 파이썬 디폴트 버전을 알아내는 방법은 어물쩡 넘어가도록 하겠습니다 ㅋㅋ ~(-_-)~
이올린에 북마크하기(0) 이올린에 추천하기(0)
2010/05/31 15:50 2010/05/31 15:50
사내에서 urlimport 를 perforce 와 연계해서 사용하고 있는 중 입니다.

문제는 프로그래머가 아닌 사용자가 urlimport 랑 p4python 을 설치하는 것은 꽤나 귀찮은 일이더군요.

urlimport 의 경우는 python site-packages 디렉토리를 찾아 복사해줘야 하고
p4python 은  커맨드라인에서 setup.py install 을 실행해야하는데...;
... orz;;

가만히 생각해보니 파이썬 모듈들은 전부 msi 로 배포된다는 사실이 떠오르더군요.

게다가 촌스러운 모양도 거의 동일하기 때문에;
아무래도 파이썬에서 제공해주는 기능이 아닐까 생각되더라구요.

열심히 검색해 본 결과!

setup.py bdist_wininst


이렇게 실행하면 윈도우즈용 인스톨러가 만들어지더군요 -ㅁ-; 오오오;




이올린에 북마크하기(0) 이올린에 추천하기(0)
2010/04/14 17:58 2010/04/14 17:58
http://dev.naver.com/projects/urlimport/

SVN
  • svn checkout --username anonsvn https://dev.naver.com/svn/urlimport
  • The password is 'anonsvn'

이올린에 북마크하기(0) 이올린에 추천하기(0)
2010/03/26 17:48 2010/03/26 17:48

python urlimport 0.55m

Posted at 2010/03/21 19:00// Posted in python/py-urlimport
회사 업무에 쫓겨서 퍼포스 테스트만 했더니 T_T)~

수정한 사항에 버그가 있었네요

다행히 방명록에 split_url 버그를 알려주신 분이 있어서 바로 수정본 올립니다.

http://www.imp17.com/python/urlimport-0.55m.py (myevan version)

테스트 스크립트를 하나 만들어 봐야겠습니다~


오늘 답장이~

"\\컴퓨터\경로" 로 사용하는윈도우 파일 공유 프로토콜이
일부 구현에서 "//컴퓨터/경로" 식으로 접근하기 때문에 perforce 주소 형식과 충돌이 생길 수 있다는 군요.

아래는 alex 님이 퍼포스 주소 사용시에는 "p4://depot/path" 식으로 수정한 버전입니다.

http://www.imp17.com/python/urlimport.py (alex version)
http://www.imp17.com/python/urlimport-0.53n.py (alex version)

심심할때 svn+ssh 연동도 시켜봐야겠어요~

이올린에 북마크하기(0) 이올린에 추천하기(0)
2010/03/21 19:00 2010/03/21 19:00

python urlimport 0.54m

Posted at 2010/03/19 13:36// Posted in python/py-urlimport
얼마전 방명록을 통해 만난 alex 님으로부터 urlimport 향상 버전을 받았습니다.

아무 생각 없이 붙였던 캐슁 기능과 퍼포스 연동 기능을 깔끔하게 정리해주셨더라구요 -_-)b

http://www.imp17.com/python/urlimport-0.53n.py (from alex)

alex 님 요청에 따라 퍼포스 관련 테스트 결과 생긴 자잘한 버그만 수정해봤습니다.

http://www.imp17.com/python/urlimport-0.54m.py (from myevan)

아... 몇천 km 떨어진 곳에 계신 분과 작업하니까 재밌네요 >ㅁ<)~

정말 바램대로 기본 모듈로 들어가면 좋겠어요. 햐햐



이올린에 북마크하기(0) 이올린에 추천하기(0)
2010/03/19 13:36 2010/03/19 13:36
파이썬은 스크립트 언어중에서 가장 훌륭한 문자열 인코딩/디코딩 시스템을 갖고 있음에도 불구하고;
XML 은 ascii 나 utf8 밖에 지원되지 않습니다 (-_-) 대체 왜?


<?xml version="1.0" encoding="ks_c_5601-1987"?>


이로 인해 위와 같은 xml 문서는 encoding 부분을 읽어서 어떤 인코딩으로 되어있는지 파악한 다음
utf8 문서로 변환해주어야 합니다.

encoding="코덱" 을 찾아내는 방법은 여러가지가 있겠지만;
코드 분량 문제라던지 처리 속도 문제등으로 인해 가능하면 정규 표현식을 사용하는 것이 좋습니다.


import re
RE_XML_ENCODING = re.compile("encoding[ \t]*=[ \t]*\"([^\"]+)\"")


encoding까지 매치하고
스페이스나 탭과 같은 공백 문자가 있는지 체크하고
= 매치하고
다시 공백 문자 체크한 다음
"에서 ~ "까지를 그룹으로 설정하는 정규 표현식입니다.

이걸 이용해서 어떤 encoding 을 알아내는 함수나


def getXMLEncoding(data):
head = data[:100]
headline = head.splitlines()[0]
mo = RE_XML_ENCODING.search(headline)
return mo.group(1) if mo else None


아예 전체를 유니코드로 변환시켜 버리는 함수를 만들 수 있습니다.


def decodeXML(data):
head = data[:100]
headline = head.splitlines()[0]
mo = RE_XML_ENCODING.search(headline)
if mo:
encoding = mo.group(1)
return data[len(headline):].decode(encoding)
else:
return data



이올린에 북마크하기(0) 이올린에 추천하기(0)
2010/02/07 15:11 2010/02/07 15:11

python/xmldiff

Posted at 2010/02/07 14:33// Posted in python/pyutils
visual studio 의 vsproj 자동 변환기를 만들다가 두개의 vcproj 파일을 비교할 일이 있어서 검색해보았습니다.

http://www.logilab.org/859

두개의 XML 파일을 비교해서 어떤게 다른지 알려주는 파이썬 모듈입니다

설치할때 알아두셔야 할 것은

python setup.py install 로 안되고

python setup.py install_lib 으로 해야 한다는 점입니다.

제작자가 배치파일 하나를 빼먹은 것 같더군요.


ascii 나 utf8 이 아닌 xml 문서는 에러가 나기 때문에 미리 변환을 해줘야 하는데...
입력인자가 파일명이기 때문에 약간 손을 봐줘야 합니다.

수정보기..


사용예는 다음과 같습니다. 엄청난 개수의 파라미터의 압박에 대비하시는 게 좋습니다.

예제보기..


비교 결과는 아래처럼 나옵니다.
[append-first, /VisualStudioProject[1]/Files[1],
<File RelativePath=".\_corest.ini"/>
]
[insert-after, /VisualStudioProject[1]/Files[1]/File[3],
<File RelativePath=".\basepch.h"/>
]
[rename, /VisualStudioProject[1]/Configurations[1]/Configuration[1]/@OutputDirectory, LogilabXmldiffTmpAttrIntermediateDirectory]
[append, /VisualStudioProject[1]/Configurations[1]/Configuration[1],
<@OutputDirectory>
../lib/x86/vc90
</OutputDirectory>
]
[rename, /VisualStudioProject[1]/Configurations[1]/Configuration[2]/@OutputDirectory, LogilabXmldiffTmpAttrIntermediateDirectory]
[append, /VisualStudioProject[1]/Configurations[1]/Configuration[2],
<@OutputDirectory>
../lib/x86/vc90
</OutputDirectory>
]
[update, /VisualStudioProject[1]/Configurations[1]/Configuration[1]/@LogilabXmldiffTmpAttrIntermediateDirectory, ../obj/$(ProjectName)_x86_vc90_$(Configuration)
]
[update, /VisualStudioProject[1]/Configurations[1]/Configuration[1]/@Name, mtd|Win32]
[append, /VisualStudioProject[1]/Configurations[1]/Configuration[1]/Tool[6],
<@PrecompiledHeaderThrough>
$(ProjectName)pch.h
</PrecompiledHeaderThrough>
]
[append, /VisualStudioProject[1]/Configurations[1]/Configuration[1]/Tool[10],
<@OutputFile>
$(OutDir)\$(ProjectName)_x86_vc90_$(ConfigurationName).lib
</OutputFile>
]
[update, /VisualStudioProject[1]/Configurations[1]/Configuration[2]/@LogilabXmldiffTmpAttrIntermediateDirectory,
../obj/$(ProjectName)_x86_vc90_$(Configuration)
]
[update, /VisualStudioProject[1]/Configurations[1]/Configuration[2]/@Name, mt|Win32]
[append, /VisualStudioProject[1]/Configurations[1]/Configuration[2]/Tool[6],
<@PrecompiledHeaderThrough>
$(ProjectName)pch.h
</PrecompiledHeaderThrough>
]
[update, /VisualStudioProject[1]/Files[1]/File[3]/@RelativePath, .\basepch.cpp]
[update, /VisualStudioProject[1]/Files[1]/File[5]/@RelativePath, .\ReadMe.txt]
[update, /VisualStudioProject[1]/Files[1]/File[3]/FileConfiguration[1]/@Name, mtd|Win32]
[update, /VisualStudioProject[1]/Files[1]/File[3]/FileConfiguration[2]/@Name, mt|Win32]
[remove, /VisualStudioProject[1]/Configurations[1]/Configuration[1]/@IntermediateDirectory]
[remove, /VisualStudioProject[1]/Configurations[1]/Configuration[2]/@IntermediateDirectory]
[remove, /VisualStudioProject[1]/Files[1]/File[2]]
[rename, //LogilabXmldiffTmpAttrIntermediateDirectory, IntermediateDirectory]
꽤 유용하다는~ : )
이올린에 북마크하기(0) 이올린에 추천하기(0)
2010/02/07 14:33 2010/02/07 14:33
1 2 3 4 5 ... 9