호쌤
호쌤 Just For Fun

[Python 데이터 수집] 카카오 Open API (1) - 이미지 검색

크리에이티브 커먼즈 라이선스 ITPAPER(호쌤,쭈쌤)에 의해 작성된 ≪[Python 데이터 수집] 카카오 Open API (1) - 이미지 검색≫은(는) 크리에이티브 커먼즈 저작자표시-비영리-동일조건변경허락 4.0 국제 라이선스에 따라 이용할 수 있습니다.
이 라이선스의 범위 이외의 이용허락을 얻기 위해서는 leekh4232@gmail.com으로 문의하십시오.

[Python 데이터 수집] 카카오 Open API (1) - 이미지 검색

카카오개발자사이트에 로그인을 하고 난 후 발급받는 REST API Key를 사용하면 카카오 Open API를 통해 다음에서 제공되는 검색 결과를 수집할 수 있습니다.

API 연동에 필요한 스팩은 https://developers.kakao.com/docs/latest/ko/daum-search/dev-guide에서 확인 가능합니다.

#01. 기본 준비 단계

1) 필요한 패키지 가져오기

1
2
3
4
5
6
7
import requests
import json    # 파이썬 기본 모듈
import urllib  # 파이썬 기본 모듈
import os  
import datetime as dt
import pandas as pd
from pandas import DataFrame

2) Open API 연동키 정의

1
api_key = "자신의_API_KEY"

3) 검색어 및 페이지 구현에 필요한 변수 준비

1
2
3
q = "파이썬"    # 검색어
page = 1       # 접근할 페이지 번호(1~50)
size = 80      # 가져올 데이터 수 (1~80)

4) 이미지가 저장될 폴더의 이름 만들기

검색어 + 현재 날짜 형식의 폴더 이름 준비

1
2
3
datetime = dt.datetime.now().strftime("%y%m%d_%H%M%S")
dirname = "%s_%s" % (q, datetime)
dirname
▶ 출력결과
1
'파이썬_200402_192648'

폴더 생성하기

1
2
if not os.path.exists(dirname):
    os.mkdir(dirname)

5) 접속 세션 만들기

1
2
3
4
# 접속 세션 만들기
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"

session = requests.Session()

6) 세션의 HTTP 헤더 설정

카카오 OpenAPI는 인증키를 HTTP 헤더에 포함시켜 전달하도록 요구하고 있다.

session.headers.update() 함수를 사용하여 HTTP Header에 추가적인 정보를 포함시킬 수 있다.

인증키를 header에 포함시켜야 함 “KakaoAK” 뒤에 공백 추가 주의

1
session.headers.update( {'User-agent': user_agent, 'referer': None, 'Authorization': 'KakaoAK ' + api_key} )

#02. API 연동하기

1) API에 전달할 파라미터 인코딩

인터넷 주소에는 한글이나 공백이 포함될 수 없기 때문에 검색어에 대해 인코딩 처리를 수행해야 한다.

한글값이 포함된 변수들을 인코딩하기 위해서는 딕셔너리 형식으로 묶은 다음 urllib.parse.urlencode() 함수를 활용하여 결과값을 반환받아야 한다.

1
2
3
params = {"page": page, "size": size, "query": q}
query = urllib.parse.urlencode(params)
query
▶ 출력결과
1
'page=1&size=80&query=%ED%8C%8C%EC%9D%B4%EC%8D%AC'

2) 최종 접속 주소 구성

1
2
3
url_tpl = "https://dapi.kakao.com/v2/search/image"
api_url = url_tpl + "?" + query
api_url
▶ 출력결과
1
'https://dapi.kakao.com/v2/search/image?page=1&size=80&query=%ED%8C%8C%EC%9D%B4%EC%8D%AC'

3) API에 접근하여 데이터 가져오기

1
2
3
4
5
r = session.get(api_url)

if r.status_code != 200:
    print("[%d Error] %s" % (r.status_code, r.reason))
    quit()

4) 가져온 결과를 딕셔너리로 변환

1
2
3
r.encoding = "utf-8"
image_dict = json.loads(r.text)
image_dict
▶ 출력결과
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{'documents': [{'collection': 'blog',
   'datetime': '2019-08-28T16:08:00.000+09:00',
   'display_sitename': '네이버블로그',
   'doc_url': 'http://blog.naver.com/kingammonite/221631209784',
   'height': 869,
   'image_url': 'http://postfiles8.naver.net/MjAxOTA4MjhfMjY4/MDAxNTY2OTcyMDgzMzI3.v-xlTkKeCqcCDyNP60HbAb5R-Qg_v9TYQ2Dll_AG8Aog.13cRmX5Sig6Zx2GmRXwGjxMtuKQDHsNC-KDQbjBwqI8g.JPEG.kingammonite/%ED%8C%8C%EC%9D%B4%EC%8D%AC%EC%8B%AC%ED%99%94.jpg?type=w773',
   'thumbnail_url': 'https://search2.kakaocdn.net/argon/130x130_85_c/9J6vWxvq5Sj',
   'width': 612},
  {'collection': 'blog',
   'datetime': '2019-02-18T11:24:00.000+09:00',
   'display_sitename': '네이버블로그',
   'doc_url': 'http://blog.naver.com/hollam/221468504292',
   'height': 863,
   'image_url': 'http://postfiles10.naver.net/MjAxOTAyMThfMTcg/MDAxNTUwNDU2MTg1ODE4.1pb70VoSq21L3dbIuYbo18ZFXTArTUrCwlmp6l6Kq1gg.7ukKYhQCUCiRSfYolou6y6bzYq0mKwsukS19oWW3c6sg.JPEG.hollam/%ED%8C%8C%EC%9D%B4%EC%8D%AC%EA%B8%B0%EC%B4%88.jpg?type=w966',
   'thumbnail_url': 'https://search2.kakaocdn.net/argon/130x130_85_c/8366k8GpNTJ',
   'width': 785},
  ... 생략 ...
  {'collection': 'blog',
   'datetime': '2020-03-08T22:22:00.000+09:00',
   'display_sitename': '네이버블로그',
   'doc_url': 'http://blog.naver.com/pjm222/221843900642',
   'height': 600,
   'image_url': 'http://postfiles14.naver.net/MjAyMDAzMDhfMTUg/MDAxNTgzNjcxNjc3NjM0.aDFp29JJadhax0eS8c1Y6xOyGpwon-O1EIguSw-TSC0g.CVmgsQMCXOSh9DFxXiD43pg2QnbgoogQoyTskLbgmhIg.PNG.pjm222/%ED%8C%8C%EC%9D%B4%EC%8D%AC%EC%A4%91%EA%B8%89.png?type=w773',
   'thumbnail_url': 'https://search2.kakaocdn.net/argon/130x130_85_c/6BxJ2McUxKn',
   'width': 450}],
 'meta': {'is_end': False, 'pageable_count': 3895, 'total_count': 398998}}

5) DataFrame 생성

딕셔너리 중에서 검색 결과에 해당하는 documents에 대한 부분을 DataFrame으로 변환한다.

1
2
image_df = DataFrame(image_dict['documents'])
image_df
▶ 출력결과
collection datetime display_sitename doc_url height image_url thumbnail_url width
0 blog 2019-08-28T16:08:00.000+09:00 네이버블로그 http://blog.naver.com/kingammonite/221631209784 869 http://postfiles8.naver.net/MjAxOTA4MjhfMjY4/M... https://search2.kakaocdn.net/argon/130x130_85_... 612
1 blog 2019-02-18T11:24:00.000+09:00 네이버블로그 http://blog.naver.com/hollam/221468504292 863 http://postfiles10.naver.net/MjAxOTAyMThfMTcg/... https://search2.kakaocdn.net/argon/130x130_85_... 785
2 blog 2019-08-28T16:08:00.000+09:00 네이버블로그 http://blog.naver.com/kingammonite/221631209784 841 http://postfiles15.naver.net/MjAxOTA4MjhfOSAg/... https://search2.kakaocdn.net/argon/130x130_85_... 612
3 blog 2019-08-28T16:08:00.000+09:00 네이버블로그 http://blog.naver.com/kingammonite/221631209784 810 http://postfiles5.naver.net/MjAxOTA4MjhfNzAg/M... https://search3.kakaocdn.net/argon/130x130_85_... 612
4 blog 2019-08-05T18:29:00.000+09:00 네이버블로그 http://blog.naver.com/speak1w111z/221606518419 623 http://postfiles10.naver.net/MjAxOTA4MDVfMTcx/... https://search4.kakaocdn.net/argon/130x130_85_... 580
... ... ... ... ... ... ... ... ...
75 blog 2020-03-11T21:02:08.000+09:00 티스토리 http://h0n9670.tistory.com/5 138 https://k.kakaocdn.net/dn/cqHQhz/btqCB9tBUgJ/1... https://search3.kakaocdn.net/argon/130x130_85_... 256
76 blog 2018-05-05T12:00:00.000+09:00 티스토리 http://datamod.tistory.com/54 506 http://cfile3.uf.tistory.com/image/99730D335AE... https://search2.kakaocdn.net/argon/130x130_85_... 900
77 blog 2016-09-17T12:02:00.000+09:00 네이버블로그 http://blog.naver.com/a994098/220813948476 675 http://postfiles2.naver.net/20160917_257/a9940... https://search3.kakaocdn.net/argon/130x130_85_... 900
78 blog 2020-01-28T11:49:24.000+09:00 티스토리 http://zzziito.tistory.com/2 1005 https://k.kakaocdn.net/dn/NDHZo/btqBt99PW2a/B7... https://search1.kakaocdn.net/argon/130x130_85_... 966
79 blog 2020-03-08T22:22:00.000+09:00 네이버블로그 http://blog.naver.com/pjm222/221843900642 600 http://postfiles14.naver.net/MjAyMDAzMDhfMTUg/... https://search2.kakaocdn.net/argon/130x130_85_... 450

80 rows × 8 columns

6) 이미지 다운받기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 저장되는 이미지 파일의 수를 카운트 하기 위한 변수
count = 0

# 이미지 주소에 대해서만 반복
for image_url in image_df['image_url']: 
    # 카운트 증가
    count += 1
    
    # 파일이 저장될 경로 생성
    path = "%s/단일저장_%04d.jpg" % (dirname, count)
    print( "[%s] >> %s" % (path, image_url) )

    # 예외처리 구문 적용
    try:
        # 이미지 주소를 다운로드를 위해 stream 모드로 가져온다.
        r = session.get(image_url, stream=True)
    
        # 에러 발생시 저장이 불가능하므로 건너뛰고 반복의 조건식으로 이동
        if r.status_code != 200:
            print("##########> 저장실패 (%d)" % r.status_code)
            continue
        
        # 추출한 데이터를 저장
        # -> 'w': 텍스트 쓰기 모드, 'wb': 바이너리(이진값) 쓰기 모드
        with open(path, 'wb') as f:
            f.write(r.raw.read())
            print("----------> 저장성공")
    except Exception as ex:
        print("~~~~~~~~~~~> 저장실패")
        print(ex) # 에러 메시지를 강제 출력 --> 에러 원인을 확인하기 위함.
        continue
▶ 출력결과
1
2
3
4
5
6
7
8
9
[파이썬_200402_192648/단일저장_0001.jpg] >> http://postfiles8.naver.net/MjAxOTA4MjhfMjY4/MDAxNTY2OTcyMDgzMzI3.v-xlTkKeCqcCDyNP60HbAb5R-Qg_v9TYQ2Dll_AG8Aog.13cRmX5Sig6Zx2GmRXwGjxMtuKQDHsNC-KDQbjBwqI8g.JPEG.kingammonite/%ED%8C%8C%EC%9D%B4%EC%8D%AC%EC%8B%AC%ED%99%94.jpg?type=w773
----------> 저장성공
[파이썬_200402_192648/단일저장_0002.jpg] >> http://postfiles10.naver.net/MjAxOTAyMThfMTcg/MDAxNTUwNDU2MTg1ODE4.1pb70VoSq21L3dbIuYbo18ZFXTArTUrCwlmp6l6Kq1gg.7ukKYhQCUCiRSfYolou6y6bzYq0mKwsukS19oWW3c6sg.JPEG.hollam/%ED%8C%8C%EC%9D%B4%EC%8D%AC%EA%B8%B0%EC%B4%88.jpg?type=w966
----------> 저장성공
[파이썬_200402_192648/단일저장_0003.jpg] >> http://postfiles15.naver.net/MjAxOTA4MjhfOSAg/MDAxNTY2OTcyMTQ0MDM1.PwZfmDrcwCIZEg60m6pYHPVPBcPk1ATfqlWZSXO2pQog.9iLHTmZPfrQxb04qg34Oa2z2Cew7rqFLRmjKbvR8unQg.JPEG.kingammonite/%ED%8C%8C%EC%9D%B4%EC%8D%AC%ED%8F%AC%ED%8F%B4_1.jpg?type=w773
----------> 저장성공
[파이썬_200402_192648/단일저장_0004.jpg] >> http://postfiles5.naver.net/MjAxOTA4MjhfNzAg/MDAxNTY2OTcyMTU3NTM5.aokdIzUaWYvnDKBYnlolhngxeuWHwcvK9QdyPiNh4Tcg.MyefXkBFoTp8H3JPB-C-7Nug2cC1_M04iulk8Alxregg.JPEG.kingammonite/%ED%8C%8C%EC%9D%B4%EC%8D%AC%ED%8F%AC%ED%8F%B4_2.jpg?type=w773
----------> 저장성공
... 생략 ...

작업 폴더 안을 확인하면 디렉토리가 생성되고 그 안에 수집된 이미지들이 저장되어 있습니다.

png

#03. 반복문을 수행하면서 이미지 수집하기

OpenAPI에 전달하는 page 파라미터는 1부터 50 사이의 값을 전달할 수 있다. 이를 통해 최대 한 페이지당 최대 80건씩 50페이지까지 검색 결과를 얻을 수 있다.

즉, 최대 4000건의 검색 결과를 얻을 수 있다.

1~50까지의 조건값 범위를 갖는 반복문 안에서 위의 #02. API 연동하기의 모든 코드를 구현하면서 page 파라미터를 반복문의 조건값으로 지정한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# 저장되는 이미지 파일의 수를 카운트 하기 위한 변수
count = 0
    
for i in range(0, 10):  # 0~9까지 --> 반복 범위를 조절하면 최대 다운받을 이미지 수가 제어된다.
    page = i + 1        # 1~10까지가 된다.
    
    # API에 전달할 파라미터 인코딩
    params = {"page": page, "size": size, "query": q}
    query = urllib.parse.urlencode(params)
    #print(query)
    
    # 최종 접속 주소 구성
    url_tpl = "https://dapi.kakao.com/v2/search/image"
    api_url = url_tpl + "?" + query
    #print(api_url)
    
    # API에 접근하여 데이터 가져오기
    r = session.get(api_url)

    if r.status_code != 200:
        print("[%d Error] %s" % (r.status_code, r.reason))
        # 예를 들어 21번째 페이지 접근에 실패했더라도
        # 22페이지부터 이어서 수행되어야 하므로 continue
        continue
        
    # 가져온 결과를 딕셔너리로 변환
    r.encoding = "utf-8"
    image_dict = json.loads(r.text)
    #print(image_dict)

    # DataFrame 생성
    image_df = DataFrame(image_dict['documents'])
    #print(image_df)
    
    # 이미지 주소에 대해서만 반복
    for image_url in image_df['image_url']: 
        # 카운트 증가
        count += 1

        # 파일이 저장될 경로 생성
        path = "%s/반복저장_%04d.jpg" % (dirname, count)
        print( "[%s] >> %s" % (path, image_url) )

        # 예외처리 구문 적용
        try:
            # 이미지 주소를 다운로드를 위해 stream 모드로 가져온다.
            r = session.get(image_url, stream=True)

            # 에러 발생시 저장이 불가능하므로 건너뛰고 반복의 조건식으로 이동
            if r.status_code != 200:
                print("##########> 저장실패 (%d)" % r.status_code)
                continue

            # 추출한 데이터를 저장
            # -> 'w': 텍스트 쓰기 모드, 'wb': 바이너리(이진값) 쓰기 모드
            with open(path, 'wb') as f:
                f.write(r.raw.read())
                print("----------> 저장성공")
        except Exception as ex:
            print("~~~~~~~~~~~> 저장실패")
            print(ex) # 에러 메시지를 강제 출력 --> 에러 원인을 확인하기 위함.
            continue
▶ 출력결과
1
2
3
4
5
6
7
8
9
10
11
12
13
[파이썬_200402_192648/반복저장_0001.jpg] >> http://postfiles8.naver.net/MjAxOTA4MjhfMjY4/MDAxNTY2OTcyMDgzMzI3.v-xlTkKeCqcCDyNP60HbAb5R-Qg_v9TYQ2Dll_AG8Aog.13cRmX5Sig6Zx2GmRXwGjxMtuKQDHsNC-KDQbjBwqI8g.JPEG.kingammonite/%ED%8C%8C%EC%9D%B4%EC%8D%AC%EC%8B%AC%ED%99%94.jpg?type=w773
----------> 저장성공
[파이썬_200402_192648/반복저장_0002.jpg] >> http://postfiles10.naver.net/MjAxOTAyMThfMTcg/MDAxNTUwNDU2MTg1ODE4.1pb70VoSq21L3dbIuYbo18ZFXTArTUrCwlmp6l6Kq1gg.7ukKYhQCUCiRSfYolou6y6bzYq0mKwsukS19oWW3c6sg.JPEG.hollam/%ED%8C%8C%EC%9D%B4%EC%8D%AC%EA%B8%B0%EC%B4%88.jpg?type=w966
----------> 저장성공
[파이썬_200402_192648/반복저장_0003.jpg] >> http://postfiles15.naver.net/MjAxOTA4MjhfOSAg/MDAxNTY2OTcyMTQ0MDM1.PwZfmDrcwCIZEg60m6pYHPVPBcPk1ATfqlWZSXO2pQog.9iLHTmZPfrQxb04qg34Oa2z2Cew7rqFLRmjKbvR8unQg.JPEG.kingammonite/%ED%8C%8C%EC%9D%B4%EC%8D%AC%ED%8F%AC%ED%8F%B4_1.jpg?type=w773
----------> 저장성공
[파이썬_200402_192648/반복저장_0004.jpg] >> http://postfiles5.naver.net/MjAxOTA4MjhfNzAg/MDAxNTY2OTcyMTU3NTM5.aokdIzUaWYvnDKBYnlolhngxeuWHwcvK9QdyPiNh4Tcg.MyefXkBFoTp8H3JPB-C-7Nug2cC1_M04iulk8Alxregg.JPEG.kingammonite/%ED%8C%8C%EC%9D%B4%EC%8D%AC%ED%8F%AC%ED%8F%B4_2.jpg?type=w773
----------> 저장성공
[파이썬_200402_192648/반복저장_0005.jpg] >> http://postfiles10.naver.net/MjAxOTA4MDVfMTcx/MDAxNTY0OTk1MzA1NjU2._Kyl0SRi1tdzpyvH70J3VJeOfaMrZASzSGSbJzNiRrkg.BQdPooPRCel96SPktXCHZmSRzU7aUKZ9tcrb6goiu7Qg.JPEG.speak1w111z/%ED%8C%8C%EC%9D%B4%EC%8D%AC%EA%B8%B0%EC%B4%88_%EC%BB%A4%EB%A6%AC%ED%81%98%EB%9F%BC.jpg?type=w580
----------> 저장성공
[파이썬_200402_192648/반복저장_0006.jpg] >> http://postfiles12.naver.net/MjAxOTA4MDVfMTgg/MDAxNTY0OTk1MzA1Njc0.pnqQXrJbeyNWkGn6k8ZXesYWq6TjfPSTMmFF6pAWRkUg.E4OE0EM4irKtPkCa3qMF39UbUiXI3R4EgIMVoyWaJCIg.JPEG.speak1w111z/%ED%8C%8C%EC%9D%B4%EC%8D%AC_%EC%9B%B9%EC%84%9C%EB%B2%84%EA%B5%AC%EC%B6%95.jpg?type=w580
----------> 저장성공
... 생략 ...
Rating:

크리에이티브 커먼즈 라이선스 ITPAPER(호쌤,쭈쌤)에 의해 작성된 ≪[Python 데이터 수집] 카카오 Open API (1) - 이미지 검색≫은(는) 크리에이티브 커먼즈 저작자표시-비영리-동일조건변경허락 4.0 국제 라이선스에 따라 이용할 수 있습니다.
이 라이선스의 범위 이외의 이용허락을 얻기 위해서는 leekh4232@gmail.com으로 문의하십시오.

comments powered by Disqus