Skip to content

Instantly share code, notes, and snippets.

@qkdxorjs1002
Last active September 17, 2021 07:53
Show Gist options
  • Save qkdxorjs1002/89a866803cc758543dbedfc52cdb6d76 to your computer and use it in GitHub Desktop.
Save qkdxorjs1002/89a866803cc758543dbedfc52cdb6d76 to your computer and use it in GitHub Desktop.
Researching: How to implement processes of auth API and vaccine reservation API for Kakao 'no-show' Vaccine reservation system
import copy
import sys
from time import sleep
import js2py
import requests
import urllib3
from bs4 import BeautifulSoup
urllib3.disable_warnings()
USER_AGENT = "Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 KAKAOTALK 9.4.6"
with requests.Session() as session:
def getCookies(userEmail, userPw):
try:
print("\n== Request Token and Encryption Key ==")
loginPage = session.get(
"https://accounts.kakao.com/login?continue=https%3A%2F%2Faccounts.kakao.com%2Fweblogin%2Faccount%2Finfo",
headers = {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"User-Agent": USER_AGENT,
"Referer": "https://accounts.kakao.com/"
}
)
print(f" - LoginPage Status Code: {loginPage.status_code}")
loginSoup = BeautifulSoup(loginPage.text, "html.parser")
token = loginSoup.select("head > meta:nth-child(3)")[0]['content']
key = loginSoup.select("#login-form > fieldset > input[type=hidden]:nth-child(3)")[0]['value']
print(f" - Token: {token}")
print(f" - Encryption Key: {key}")
print("\n== Request Session Params ==")
tiaraTrack = session.get(
"https://stat.tiara.kakao.com/track?d=%7B%22sdk%22%3A%7B%22type%22%3A%22WEB%22%2C%22version%22%3A%221.1.15%22%7D%2C%22env%22%3A%7B%22screen%22%3A%221441X2561%22%2C%22tz%22%3A%22%2B9%22%2C%22cke%22%3A%22Y%22%7D%2C%22common%22%3A%7B%22svcdomain%22%3A%22accounts.kakao.com%22%2C%22deployment%22%3A%22production%22%2C%22url%22%3A%22https%3A%2F%2Faccounts.kakao.com%2Flogin%22%2C%22referrer%22%3A%22https%3A%2F%2Faccounts.kakao.com%2F%22%2C%22title%22%3A%22%EC%B9%B4%EC%B9%B4%EC%98%A4%EA%B3%84%EC%A0%95%22%2C%22section%22%3A%22login%22%2C%22page%22%3A%22pageLogin%22%7D%2C%22action%22%3A%7B%22type%22%3A%22Pageview%22%2C%22name%22%3A%22pageLogin%22%2C%22kind%22%3A%22%22%7D%7D",
headers = {
"User-Agent": USER_AGENT,
"Referer": "https://accounts.kakao.com/"
}
)
print(f" - Tiara Status Code: {tiaraTrack.status_code}")
print("\n== Encrypt User Email & PW ==")
CryptoJS = js2py.require('crypto-js')
encryptEmail = CryptoJS.AES.encrypt(userEmail, key).toString()
encryptPw = CryptoJS.AES.encrypt(userPw, key).toString()
print(f" - Cipher Email: {encryptEmail}")
print(f" - Cipher PW: {encryptPw}")
print("\n== Request Auth ==")
authenticate = session.post(
"https://accounts.kakao.com/weblogin/authenticate.json",
headers = {
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "ko,en;q=0.9,en-US;q=0.8",
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
"Host": "accounts.kakao.com",
"User-Agent": USER_AGENT,
"Origin": "https://accounts.kakao.com",
"Referer": "https://accounts.kakao.com/login?continue=https%3A%2F%2Faccounts.kakao.com%2Fweblogin%2Faccount%2Finfo",
"X-Requested-With": "XMLHttpRequest"
},
data = {
"os": "web",
"webview_v": 2,
"continue": "https://accounts.kakao.com/weblogin/account/info",
"email": encryptEmail,
"password": encryptPw,
"third": "false",
"k": "true",
"authenticity_token": token
}
)
print(f" - Auth Status Code: {authenticate.status_code}")
if (authenticate.json()["status"] != 0):
sys.exit(authenticate.text)
print("\n== Request Auth Token==")
ssoInit = session.get(
"https://accounts.kakao.com/weblogin/sso_initialize?continue=https%3A%2F%2Fmap.kakao.com",
headers = {
"User-Agent": USER_AGENT
}
)
print(f" - SSO Status Code: {ssoInit.status_code}")
print("\n== Request Token Login ==")
tokenLogin = session.get(
f"https://logins.daum.net/accounts/kakaossotokenlogin.do?redirect=false&ssotoken={ssoInit.json()['tokens'][0]['token']}",
headers = {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "ko,en;q=0.9,en-US;q=0.8",
"Referer": "https://accounts.kakao.com/",
"User-Agent": USER_AGENT
}
)
print(f" - Token Login Status Code : {tokenLogin.status_code}")
if (tokenLogin.status_code == 200):
print(f"\n!!!! Login Success !!!!")
else:
sys.exit("\n!!!! Failed to Login !!!!")
except Exception as e:
sys.exit(e)
def testCookie():
try:
userInfo = session.get(
"https://vaccine.kakao.com/api/v1/user",
headers = {
"User-Agent": USER_AGENT
},
verify = False
)
userJson = userInfo.json()
if (userInfo.status_code == 200) :
print(f" - Name: {userJson['user']['name']}")
print(f" - Status: {userJson['user']['status']}")
print(f" - Reservations: {len(userJson['reservations'])}")
return True
except Exception as e:
print(e)
return False
def requestOrgs(coords, onlyLeft = True):
temp = copy.deepcopy(coords)
temp["onlyLeft"] = onlyLeft
return session.post(
"https://vaccine.kakao.com/api/v3/vaccine/left_count_by_coords",
json = temp,
verify = False
)
def requestResrv(org, type):
orgResp = session.get(
f"https://vaccine.kakao.com/api/v3/org/org_code/{org['orgCode']}",
headers = {
"Accept": "application/json, text/plain, */*",
"Content-Type": "application/json;charset=utf-8",
"Origin": "https://vaccine.kakao.com",
"Accept-Language": "en-us",
"User-Agent": USER_AGENT,
"Referer": f"https://vaccine.kakao.com/",
"Accept-Encoding": "gzip, deflate",
"Connection": "close"
},
verify = False
)
orgResp_json = orgResp.json()
leftCnt = 0;
for x in orgResp_json['lefts']:
if (x['vaccineCode'] == type):
leftCnt += x['leftCount']
if (leftCnt == 0):
return False
for x in range(leftCnt):
req_x = session.post(
"https://vaccine.kakao.com/api/v2/reservation",
json = {
"from": "Map",
"vaccineCode": type,
"orgCode": int(org["orgCode"]),
"distance": None
},
headers = {
"Accept": "application/json, text/plain, */*",
"Content-Type": "application/json;charset=utf-8",
"Origin": "https://vaccine.kakao.com",
"Content-Length": "74",
"Accept-Language": "en-us",
"User-Agent": USER_AGENT,
"Referer": f"https://vaccine.kakao.com/reservation/{org['orgCode']}?from=Map&code={type}",
"Accept-Encoding": "gzip, deflate",
"Connection": "close"
},
verify = False
)
print(req_x.text)
if (req_x.status_code == 200) :
return True
return False
def reserveLoop(coords, type, interval):
isReserved = False;
while True:
try:
resp = requestOrgs(coords)
resp_json = resp.json()
for a in resp_json["organizations"]:
if (requestResrv(a, type)) :
isReserved = True
if (isReserved) :
print("!!!! Reserved !!!!\n\n")
if (resp.status_code != 200):
print(resp.status_code)
sleep(interval)
except Exception as e:
print(e)
pass
def parsePosition(leftTop, rightBottom):
lt = leftTop.split(", ")
rb = rightBottom.split(", ")
return {
"bottomRight": {
"x": lt[1],
"y": lt[0]
},
"onlyLeft": True,
"order": "count",
"topLeft": {
"x": rb[1],
"y": rb[0]
}
}
######################################################
## 무조건 하단 절차 확인해야 오류 안남
######################################################
## 필수! pip install wheel js2py numpy requests bs4
##
## 0. 카카오톡에서 지갑 인증서 발급 되어 있는지 확인하기
## 0. 카카오톡에서 #탭 - 잔여백신에서 아무 기관에 알람 신청하기 (개인정보 수집 동의)
## 1. "python vaccine.py" 혹은 "python3 vaccine.py" 로 스크립트 실행
##
## !. 오류 401 "{"error": "error occurred"}"가 뜰 경우 로그인 정보 올바른지, 알림 신청시 개인정보 수집 동의를 했는지 확인
## !. 오류 401 "{"error": "User not found: accountId=<숫자>"}"가 뜰 경우 카카오톡에서 지갑 인증서 발급 및 인증되어 있는지 확인
if (__name__ == "__main__"):
TYPE_ALL = "ANY"
TYPE_PFIZER = "VEN00013"
TYPE_MODERNA = "VEN00014"
TYPE_AZ = "VEN00015"
TYPE_JANSSEN = "VEN00016"
####################################################################
####################################################################
## 카카오 계정 이메일
userEmail = "value"
## 카카오 계정 패스워드
userPw = "value"
## 구글 지도 통해서 사각형 범위를 지정해주세요
## 왼쪽 상단 좌표
posLeftTop = "value"
## 오른쪽 하단 좌표
posRightBottom = "value"
## 백신 종류입니다.
## 전부: TYPE_ALL / 화이자: TYPE_PFIZER / 모더나: TYPE_MODERNA
## 아스트라제네카: TYPE_AZ / 얀센: TYPE_JANSSEN
type = TYPE_PFIZER;
####################################################################
####################################################################
print("\n== Check Coords ==")
coords = parsePosition(posLeftTop, posRightBottom)
print(f" - Org count: {len(requestOrgs(coords, onlyLeft = False).json()['organizations'])}")
print("\n== Login ==")
getCookies(userEmail, userPw)
print("\n== Check Cookies ==")
if(testCookie()):
print("\n!!!! Valid Cookie !!!!")
else:
sys.exit("\n!!!! Invalid Cookie !!!!")
print("\n== Reservation Loop ==")
reserveLoop(
## 지역 범위
coords = coords,
## 백신 종류
type = type,
## 요청 간격 낮을 수록 예약 확률이 높아지지만, 서버에서 요청을 차단할 가능성 있음 (기본값: 0.35초)
interval = 0.35
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment