데이터/크롤링

[Python] requests로 네이버 데이터랩 크롤링

Jerry_K 2023. 11. 10. 19:39

학교에서 하는 프로젝트가 서서히 끝나가서, 이제 조금식 여유가 생기고 있다.

이제 다시 내 프로젝트에 집중을 하자 !!  ( 문제는 했던 것들이 기억 안남...)

 

내 개인 프로젝트는 네이버 데이터들을 분석하는 것이다.

분석을 하려면 당연히 데이터가 필요하기때문에 자연스럽게 크롤링도 많이 하게된다.

api로 가져 올 수 없는게 많아 네이버 데이터랩에서 크롤링을 하는중 ㅎㅁㅎ 

 

예전에 데이터랩에서 카테고리별  top 500 keyword를 추출한 적 있었는데,

시간이 엄청 오래오래 걸린 기억이 있다 ㅠㅠ  

아무래도 selenium 방식으로 하다보니 시간을 너무 많이 쓰게된다...

 

그리고 현재 지금, top 500 keyword를 업데이트 해야하는데

selenium으로 하면 정신 나갈거 같아 requsests 방식으로 해보려 한다.

도오오저어언 !!   (사실 전 크롤링 포스터랑 거의 똑같다)

 

결론부터 말하며 curl, requests는 사기다 ... !!!

 


requests 실패 과정

어제 내가 request를 하면서 실패한 것도 기록하려하는데

만일 이 글을 읽으시는 분이 계시다면 생략해도 된다.

 

 

https://datalab.naver.com/shoppingInsight/sCategory.naver

 

네이버 데이터랩 : 쇼핑인사이트

쇼핑 분야별 클릭 추이와 검색어 현황을 확인할 수 있습니다.

datalab.naver.com

내가 크롤링하려는 사이트는 여기이다.

 

 

여기 보면 클릭량 추이와 인기검색어 top500이 있다.  

목표 : 인기검색어 Top 500을  requests로 가저오는 것 !! 

 

 

import requests
from bs4 import BeautifulSoup
import json
import openpyxl

url = "https://datalab.naver.com/shoppingInsight/sCategory.naver"
response = requests.get(url, headers={'User-agent': 'Mozilla/5.0'})

html = response.text
soup = BeautifulSoup(html,'html.parser')

print(soup.text)

 

그냥 아무 생각없이 requests.get 방식으로 헤더주고 시도해봤다.

 

 

예상했다 ... 

 

 

여기에있는 조건들을 아무리 바꿔도   "https://datalab.naver.com/shoppingInsight/sCategory.naver"의 url은 변동이 없다.

이럴때는 post 방식을 생각하고 network창을 유심히 보면 된다.

 

 

 

네트워크창을 열고 디지털/가전 > 휴대폰악세서리>휴대폰보호필름 (아무거나 함)로 설정하고 조회하기를 눌러봤다.

Name 창에 getCategory~~~들이 있는데 가장 눈에 띄는거는 getCategoryKeywordRank.naver 부분이다.

 

Payload를 봐보니 cid, page,count ... 등이 보인다.

대충  이 부분을 잘 다루면 크롤링이 될 것 같은 느낌이 든다.

 

정리하면  내가 디지털/가전 > 휴대폰악세서리>휴대폰보호필름 조회하기를 누르는 것은 서버에게 위와 같은 Form Data를 request해주고 서버로부터 response으로 keyword_rank등을 받는 것이다.

그렇다면 내가 서버에 저 Form Data를 Post하면 데이버를 받을 수 있을 것이다.

 

조아쓰 !!   Post 방식으로 간닷

 

url = "https://datalab.naver.com/shoppingInsight/sCategory.naver"
payload = {
    "cid": "50001378",
    "timeUnit": "date",
    "startDate": "2023-10-09",
    "endDate": "2023-11-09",
    "page": "1",
    "count": "20"}

response = requests.post('https://datalab.naver.com/shoppingInsight/sCategory.naver', 
                         headers={'User-agent': 'Mozilla/5.0'},
                         data = payload)

html = response.text
soup = BeautifulSoup(html,'html.parser')

print(soup.text)

payload에서 필요없는 부분 제거하고 이런식으로 실행했다. 

 

이렇게하면 될거라 생각했는데...  ㅠㅠ

post 하는데  FormData가 부족해서 이런것이다.

 

 


requests 성공 과정

위에 작성한 글들은 내가 실제 해왔던 실패 과정을 기록했고,

지금부터가 requests에 성공한 코드이다.  

 

위에서는  payload 파라미터 값들을 끄적였는데, 그냥 getCategory~ 부분을 우클릭한다음 cURL 부분을 복사하면 된다.

 

 

https://curlconverter.com/

 

Convert curl commands to code

GitHub is matching all contributions to this project on GitHub Sponsors. Contribute Now

curlconverter.com

전 포스팅에 올렸던 사이트인데 curl 코드를 python 형태로 바꿔준다.

 

 

그러면 이렇게 파이썬 코드를 얻을 수 있다.

그리고 복사해서 본인의 파이썬 개발 환경에 붙여넣자. ( 저는 VS 코드 씁니다.)

 

 

import requests

cookies = {
    'NNB': '6TTK2RJU3JPWG',
    '_ga': 'GA1.2.1198469368.1669035888',
    'NV_WETR_LOCATION_RGN_M': '"MDUxMTAxMTE="',
    'ASID': '6f5bbfa400000184fa368e5f00000067',
    'NV_WETR_LAST_ACCESS_RGN_M': '"MDUxMTAxMTE="',
    'NID_AUT': 'xDAIttoc1Atdn7rAs4Y+rogecRjh3p1xevXbJnm0Nr0CEioRQ5/hhmRFwExAG7+T',
    'NID_JKL': 'b+0nlz0gwdbCoxClpc3utji+0en89OQHe0BEDbaUv/Y=',
    'nx_ssl': '2',
    'NID_SES': 'AAABy0Z0rCuAFFNEPUXlfcChEv1yPJpvLt1X4+/+C9sRd4datGvO27oyXkd2C+ohKhNTjTM9J3Rho0YRWOm/DSmU50AtuyN4rS5QsMGiTrwwAhFy7awewcPHkArAFMgE+8xgcHv37Nflty8WYokL0BTAAiiyNFaVzUqwbLNjBBqq4bnpVzwAEnf4ZB4Ejnkwkkk3zrXviQ9MbT+IBtkMO2AcV0bbtE6gymkDLn35qZ5nD7bHM2cmPq5MZndQ3FIR2htg2Z7tg6ead3hAaHLTpRiyeJdH19DNG1y8jAfTHBlPqIyct3CxAT1I34vXzeL9mMx8Juu9NbdzY46LP1BzthpeK8ypKz/Frp46RfnSrDpRqxyXtHCMt7rMfYMbmE3molAzseZsPl6hkMoVQVU6GFrEnOu3ioxPn9Zza/PBNr4N+xC7OP3t1VHg7fy1BvIWeEO3Us43R7XA+HbYVAuYiUbGF1pDt9CeZ/y0wVXTJsez+RstvQnaMfIiqZGmnnVB85q89Zu2AjMJdq/duzKsf5Wur6TUNTyShU3smmFB0iwbWWSxf9hfPouHCTcqd2C5pD0FcbWE8ZdrgAAqvFPDNaCJFv8zkXeN8Bm/zHZ0nDLQu1hN',
    '_datalab_cid': '50000003',
}

headers = {
    'authority': 'datalab.naver.com',
    'accept': '*/*',
    'accept-language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
    'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
    # 'cookie': 'NNB=6TTK2RJU3JPWG; _ga=GA1.2.1198469368.1669035888; NV_WETR_LOCATION_RGN_M="MDUxMTAxMTE="; ASID=6f5bbfa400000184fa368e5f00000067; NV_WETR_LAST_ACCESS_RGN_M="MDUxMTAxMTE="; NID_AUT=xDAIttoc1Atdn7rAs4Y+rogecRjh3p1xevXbJnm0Nr0CEioRQ5/hhmRFwExAG7+T; NID_JKL=b+0nlz0gwdbCoxClpc3utji+0en89OQHe0BEDbaUv/Y=; nx_ssl=2; NID_SES=AAABy0Z0rCuAFFNEPUXlfcChEv1yPJpvLt1X4+/+C9sRd4datGvO27oyXkd2C+ohKhNTjTM9J3Rho0YRWOm/DSmU50AtuyN4rS5QsMGiTrwwAhFy7awewcPHkArAFMgE+8xgcHv37Nflty8WYokL0BTAAiiyNFaVzUqwbLNjBBqq4bnpVzwAEnf4ZB4Ejnkwkkk3zrXviQ9MbT+IBtkMO2AcV0bbtE6gymkDLn35qZ5nD7bHM2cmPq5MZndQ3FIR2htg2Z7tg6ead3hAaHLTpRiyeJdH19DNG1y8jAfTHBlPqIyct3CxAT1I34vXzeL9mMx8Juu9NbdzY46LP1BzthpeK8ypKz/Frp46RfnSrDpRqxyXtHCMt7rMfYMbmE3molAzseZsPl6hkMoVQVU6GFrEnOu3ioxPn9Zza/PBNr4N+xC7OP3t1VHg7fy1BvIWeEO3Us43R7XA+HbYVAuYiUbGF1pDt9CeZ/y0wVXTJsez+RstvQnaMfIiqZGmnnVB85q89Zu2AjMJdq/duzKsf5Wur6TUNTyShU3smmFB0iwbWWSxf9hfPouHCTcqd2C5pD0FcbWE8ZdrgAAqvFPDNaCJFv8zkXeN8Bm/zHZ0nDLQu1hN; _datalab_cid=50000003',
    'origin': 'https://datalab.naver.com',
    'referer': 'https://datalab.naver.com/shoppingInsight/sCategory.naver',
    'sec-ch-ua': '"Google Chrome";v="119", "Chromium";v="119", "Not?A_Brand";v="24"',
    'sec-ch-ua-mobile': '?0',
    'sec-ch-ua-platform': '"Windows"',
    'sec-fetch-dest': 'empty',
    'sec-fetch-mode': 'cors',
    'sec-fetch-site': 'same-origin',
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
    'x-requested-with': 'XMLHttpRequest',
}

data = {
    'cid': '50001737',
    'timeUnit': 'date',
    'startDate': '2023-10-09',
    'endDate': '2023-11-09',
    'age': '',
    'gender': '',
    'device': '',
    'page': '1',
    'count': '20',
}

response = requests.post(
    'https://datalab.naver.com/shoppingInsight/getCategoryKeywordRank.naver',
    cookies=cookies,
    headers=headers,
    data=data,
)

사실 이거면 ... 진짜 다  99% 다 해준거다 ... (curl 개꿀!!)

저기에 있는 cid, startDate...page,count 등을 잘 수정하면 원하는 부분 크롤링 가능하다.

 

 

keyword_ranks = json.loads(response.text)

보통은  html = response.text의 형식으로 BeautifulSoup으로 파싱하는데,

curl로 가져오는 경우, 위에 코드처럼 json 형태의 딕셔너리로 받는다.

 

 

print(keyword_ranks)

복잡한 딕셔너리 형태가 나온다.  

좀 보기 쉽게하기위해 정리를 해봤다.

 

 

{
    'message': None,
    'statusCode': 200,
    'returnCode': 0,
    'date': '',
    'datetime': '',
    'range': '2023.10.08. ~ 2023.11.08.',
    'ranks': [
        {'rank': 21, 'keyword': '여성가디건', 'linkId': '여성가디건'},
        {'rank': 22, 'keyword': '내셔널지오그래픽패딩', 'linkId': '내셔널지오그래픽패딩'},
        {'rank': 23, 'keyword': '스파오후리스', 'linkId': '스파오후리스'},
        {'rank': 24, 'keyword': '가죽자켓', 'linkId': '가죽자켓'},
        {'rank': 25, 'keyword': '트위드원피스', 'linkId': '트위드원피스'},
        {'rank': 26, 'keyword': '티셔츠', 'linkId': '티셔츠'},
        {'rank': 27, 'keyword': '트레이닝세트', 'linkId': '트레이닝세트'},
        {'rank': 28, 'keyword': '몽클레어패딩', 'linkId': '몽클레어패딩'},
        {'rank': 29, 'keyword': '조거팬츠', 'linkId': '조거팬츠'},
        {'rank': 30, 'keyword': '여성패딩조끼', 'linkId': '여성패딩조끼'},
        {'rank': 31, 'keyword': '폴로니트', 'linkId': '폴로니트'},
        {'rank': 32, 'keyword': '지고트원피스', 'linkId': '지고트원피스'},
        {'rank': 33, 'keyword': '롱패딩', 'linkId': '롱패딩'},
        {'rank': 34, 'keyword': '올리비아로렌', 'linkId': '올리비아로렌'},
        {'rank': 35, 'keyword': '여성패딩', 'linkId': '여성패딩'},
        {'rank': 36, 'keyword': '듀엘', 'linkId': '듀엘'},
        {'rank': 37, 'keyword': '청바지', 'linkId': '청바지'},
        {'rank': 38, 'keyword': '케네스레이디원피스', 'linkId': '케네스레이디원피스'},
        {'rank': 39, 'keyword': '핸드메이드코트', 'linkId': '핸드메이드코트'},
        {'rank': 40, 'keyword': '지오다노경량패딩', 'linkId': '지오다노경량패딩'}
    ]
}

keyword_ranks를 출력하면 이렇게 깔끔하게 나오지 않는다.  (보기 편함을 위해 정리함 )

이제  "ranks"를 출력해보자.

 

 

print(keyword_ranks["ranks"])

잘 나온다 !! 

이제 단어만 한번 추출 해보자

 

 

for idx,keyword_rank in enumerate(keyword_ranks["ranks"]):
     print(keyword_rank["keyword"])

좋았으 !!! 

마지막으로 top 500 까지의 모든 keyword를 추출하고 끝내보자

 

 

page = 25 
num = 1
for i in range(1,page+1):

    data = {
        'cid': '50000393',
        'timeUnit': 'date',
        'startDate': '2023-10-09',
        'endDate': '2023-11-09',
        'age': '',
        'gender': '',
        'device': '',
        'page': i,
        'count': '20',
    }

    response = requests.post(
        'https://datalab.naver.com/shoppingInsight/getCategoryKeywordRank.naver',
        cookies=cookies,
        headers=headers,
        data=data,
    )

    keyword_ranks = json.loads(response.text)

    for idx,keyword_rank in enumerate(keyword_ranks["ranks"]):
        print(f"{num}. {keyword_rank['keyword']}")
        num += 1

(참고로 위에 cookies 딕셔너리에서  '_datalab_cid' 부분은  아니 cid와 동일해서 지워줌 )

 

이렇게 하면 될 줄 알았다 ...

근데 자꾸 160번째에서 

"json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)"

이런 에러가 뜬다 ㅠㅠ 

 

 

print(response.text)

어떤 오류인지 확인하기 위해 이 부분을 for 문안에 넣어봤다.

 

아... 또 너냐 ?!

이런 경우는 쉽게 time.sleep을 해줘서 약간의 딜레이를 주면 된다.

 

 

import time
import numpy as np

time이랑 numpy를  import 한다음

 

 time.sleep(np.random.rand())

0~1 사이의 timesleep을 주면 된다.

이러고 다시 위의 코드를 실행하면 

 

짜쟌 ㅎㅁㅎ

500개 까지 잘 나온다~~ 다만 조금 느려진게 아쉽다 ㅠㅠ 

저 모든 카테고리 하려면 requests도 많은 시간이 필요할 것 같다 ...


전체 코드 참고용

import requests
from bs4 import BeautifulSoup
import json
import time
import numpy as np

cookies = {
    'NNB': '6TTK2RJU3JPWG',
    '_ga': 'GA1.2.1198469368.1669035888',
    'NV_WETR_LOCATION_RGN_M': '"MDUxMTAxMTE="',
    'ASID': '6f5bbfa400000184fa368e5f00000067',
    'NV_WETR_LAST_ACCESS_RGN_M': '"MDUxMTAxMTE="',
    'NID_AUT': 'xDAIttoc1Atdn7rAs4Y+rogecRjh3p1xevXbJnm0Nr0CEioRQ5/hhmRFwExAG7+T',
    'NID_JKL': 'b+0nlz0gwdbCoxClpc3utji+0en89OQHe0BEDbaUv/Y=',
    'nx_ssl': '2',
    'NID_SES': 'AAABxny17QsRI7XWjIT82lSlLduWZgRrEWC3JXPZ7deYdXvL/c2PUt1Ud42JJjVFG5cfcZocwetwDbTKM0T8sJafQPaeZs/im79ODtVzYIAPqP3dKeq/LuWtkHGyFQdwG7MhhuyvFj6J6Fjb52dGxkZQdTyw9sDIjJGvuVmNz1ttmJhzOo6FGK9SFy5t+b4ILktWm42H76poEeZCHN1x5JahBU1wpphZyTwI+1KJX/JK1+ns5Y5cDRyAZU//7MRxFOnwxNmqPdxI22TCSewIukKlRgfYtVrlXGVf7YTZhfgbd8P7P0xX6RZ4BDEoyApB9j396AnXLVKffXdfpjrJ+BXwpjObqUB8u/vc0fY0976qptW65Ks1m9/P5PhZq3KxvRObMCt9N4u10a18rL63GeOb/oQuN1EKDcFGEWttGb5tSTb85djotOL1/2inlv2C735F7EvQAqLxPidQygSNLfIFWh/6hUICJbzW8rJpE0bEhrqZi2ljp1moUjsub7bO2hj2/+FXoH4ws1/qxxLNpVugB0fRERY1N+CzrXI3UASpoNaYybe3nsDFVaAtwrnKmwiYk6coDGQHLxd3ZN76Y4pRPqYF+r1iHchZP+TPdu/SMUsI',
}

headers = {
    'authority': 'datalab.naver.com',
    'accept': '*/*',
    'accept-language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
    'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
    'origin': 'https://datalab.naver.com',
    'sec-ch-ua': '"Google Chrome";v="119", "Chromium";v="119", "Not?A_Brand";v="24"',
    'sec-ch-ua-mobile': '?0',
    'sec-ch-ua-platform': '"Windows"',
    'sec-fetch-dest': 'empty',
    'sec-fetch-mode': 'cors',
    'sec-fetch-site': 'same-origin',
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
    'x-requested-with': 'XMLHttpRequest',
}


page = 25
num = 1
for i in range(1,26):
    data = {
        'cid': '50000393',
        'timeUnit': 'date',
        'startDate': '2023-10-09',
        'endDate': '2023-11-09',
        'age': '',
        'gender': '',
        'device': '',
        'page': i,
        'count': '20',
    }

    time.sleep(np.random.rand())

    response = requests.post(
        cookies=cookies,
        headers=headers,
        data=data,
    )
    # print(response.text)

    keyword_ranks = json.loads(response.text)

    for idx,keyword_rank in enumerate(keyword_ranks["ranks"]):
        print(f"{num}. {keyword_rank['keyword']}")
        num += 1

이런 방식으로 통계도 가져  올 수 있다. 

post와 curl은 진짜 꿀...

확실한거는 아니지만, 아마 판다랭크에 데이터도 이런 방식으로 가져오지 않을까 생각한다.

응용을 하고 싶다면 rank 옆에 그래프를 크롤링해보자 !!

 

+ 추가

통계는 getCategoryKeywordRank 이 부분만 바꿔주면 된다.   

cookies,headers는 똑같고 , data 부분만 약간 다른데  수정 안해도 잘 된다.

 'https://datalab.naver.com/shoppingInsight/getCategoryClickTrend.naver',