네이버 웹툰 크롤러를 만들었는데 로그인 된 세션이 아니다보니 나이제한 걸린 웹툰 접근이 안됐다.

네이버 로그인 구현을 하려고 검색을 해보니 역시 선구자가 있었다.

 

원리는 잘 모르겠으나 일단 잘 읽어보고 시도. 옛날 소스라 특히 encode, decode를 좀 수정해줘야 한다.

파이썬 문법도 잘 모르는데 옛날 소스 포팅하려고 하니 이건 뭐 장님 코끼리 만지는 격이다.

 

다 수정해서 돌렸는데 로그인을 하니 status code는 200 OK가 나오는데 자꾸 캡차가 나온다.

여기서 requests 대신 selenium을 써볼까 했는데 속도가 너무 느려서 포기했다.

좀 더 찾아보니 또 역시나 선구자가 있었다.

 

요약하면 bvsd.js 파일에서 브라우저 정상/비정상 여부를 감지해 캡차를 띄운다는 것. 어짜피 클라이언트에서 감지하는 것이니 우회할 수 있는데 위 소스들이 vb.net, c#이라 베끼는 데 좀 애를 먹었다 ㅠ

그리고 보니까 public key 주소도 변경됐다.

이전
http://static.nid.naver.com/enclogin/keys.nhn

현재(2020. 3. 16 기준)
https://nid.naver.com/login/ext/keys.nhn

 

import re
import uuid
import requests
import rsa
import lzstring
from urllib3.util.retry import Retry
from requests.adapters import HTTPAdapter


def encrypt(key_str, uid, upw):
    def naver_style_join(l):
        return ''.join([chr(len(s)) + s for s in l])

    sessionkey, keyname, e_str, n_str = key_str.split(',')
    e, n = int(e_str, 16), int(n_str, 16)

    message = naver_style_join([sessionkey, uid, upw]).encode()

    pubkey = rsa.PublicKey(e, n)
    encrypted = rsa.encrypt(message, pubkey)

    return keyname, encrypted.hex()


def encrypt_account(uid, upw):
    key_str = requests.get('https://nid.naver.com/login/ext/keys.nhn').content.decode("utf-8")
    return encrypt(key_str, uid, upw)


def naver_session(nid, npw):
    encnm, encpw = encrypt_account(nid, npw)

    s = requests.Session()
    retries = Retry(
        total=5,
        backoff_factor=0.1,
        status_forcelist=[500, 502, 503, 504]
    )
    s.mount('https://', HTTPAdapter(max_retries=retries))
    request_headers = {
        'User-agent': 'Mozilla/5.0'
    }

    bvsd_uuid = uuid.uuid4()
    encData = '{"a":"%s-4","b":"1.3.4","d":[{"i":"id","b":{"a":["0,%s"]},"d":"%s","e":false,"f":false},{"i":"%s","e":true,"f":false}],"h":"1f","i":{"a":"Mozilla/5.0"}}' % (bvsd_uuid, nid, nid, npw)
    bvsd = '{"uuid":"%s","encData":"%s"}' % (bvsd_uuid, lzstring.LZString.compressToEncodedURIComponent(encData))

    resp = s.post('https://nid.naver.com/nidlogin.login', data={
        'svctype': '0',
        'enctp': '1',
        'encnm': encnm,
        'enc_url': 'http0X0.0000000000001P-10220.0000000.000000www.naver.com',
        'url': 'www.naver.com',
        'smart_level': '1',
        'encpw': encpw,
        'bvsd': bvsd
    }, headers=request_headers)

    finalize_url = re.search(r'location\.replace\("([^"]+)"\)', resp.content.decode("utf-8")).group(1)
    s.get(finalize_url)

    return s



if __name__ == "__main__":
    s = naver_session('idid', 'pwpw')
    print(1)
    pp = s.get('https://comic.naver.com/webtoon/detail.nhn?titleId=726095&no=42')
    print(pp)

완성한 소스 예제. 아이디랑 비밀번호 하드코딩돼있다.

naver_session 함수 중간에 Retry가 들어가있는데 크롤링할 때 쓰려고 한거라 빼도 된다. 로그인 후에는 네이버 웹툰에서 성인 만화 한 화에 접속해 로그인이 됐는지 확인한다.

 

위 소스로 로그인이 잘 안될 수 있는데, 이때는 response 확인해봐야한다. 아이디 보호모드? 그런게 나오면 해제를 해줘야 한다.

위 소스로 로그인했을 때 캡차가 나오면 로그인 루틴이 바뀐 것일 확률이 높다.

 

 

+ 2020. 06. 11

프로젝트를 깃허브에 올려놨습니다 (링크)

일단 동작은 잘 합니다. 문서화가 겁나 부실하고 리팩토링이 하나도 안돼있는데.. 동작은 합니다. 코딩테스트 준비한답시고 손도 못댔네요.

후우~ 이번주 안에 시간내서 프로젝트를 다듬어야겠습니다. 혹시 사용법이 궁금하면 댓글 달아주세요

반응형