본문 바로가기

그 땐 IT활동했지/그 땐 영일영 근무했지

[010/API] Naver API | 블로그, 카페 글 크롤링 해오기

728x90

1.  문제상황


우리 사이트에는 각 핸드폰 대리점마다 상세페이지가 있는데 이 상세페이지에 해당 대리점의 인터넷 검색결과를 불러오려고 한다. 이를 위해 Naver API를 활용하게 되었다!

(공식 문서 참고! https://developers.naver.com/docs/serviceapi/search/cafearticle/cafearticle.md#%EC%B9%B4%ED%8E%98%EA%B8%80)

 

2.  Naver API 활용하기


키 발급받기


API를 활용하기 위해 키를 발급받자!

https://developers.naver.com/apps/#/register

 

애플리케이션 - NAVER Developers

 

developers.naver.com

👉🏻위 링크로 들어가 키를 발급받장!

👉🏻본인이 필요한대로 내용을 채우면 된다!

  • 애플리케이션 이름은 본인이 지으면 된다.
  • 나는 검색 결과가 필요하기 때문에 검색 API를 선택했다.
  • API를 활용하는 url을 적는다. (일단 로컬 url만 적었다.)

👉🏻짜잔! 그럼 키와 시크릿키 발급 성공!

🐰이제 이 키를 활용해 카페 검색 결과를 불러오자!

 

view에서 검색결과 불러오기


https://developers.naver.com/docs/serviceapi/search/blog/blog.md#%EB%B8%94%EB%A1%9C%EA%B7%B8

 

블로그 - Search API

블로그 NAVER Developers - 검색 API 블로그 검색 개발가이드 검색 > 블로그 네이버 블로그 검색 결과를 출력해주는 REST API입니다. 비로그인 오픈 API이므로 GET으로 호출할 때 HTTP Header에 애플리케이션

developers.naver.com

👉🏻위 링크는 블로그 검색 결과를 불러오는 API긴 한데 JAVA, Python 등 다양한 언어의 예제가 있으니 읽어보는 것이 좋을 것 같다!

def detail(request, pk):
	store = get_object_or_404(Store, pk=pk)

	client_id = "본인의 API KEY"
	client_secret = "본인의 Secret KEY"

	encText = urllib.parse.quote(store.name)
	urlCafe = "https://openapi.naver.com/v1/search/cafearticle?query=" + encText + "&display=50&start=1"# json 결과

	assembled_requestCafe = urllib.request.Request(urlCafe)
	assembled_requestCafe.add_header("X-Naver-Client-Id",client_id)
	assembled_requestCafe.add_header("X-Naver-Client-Secret",client_secret)

	response_c = urllib.request.urlopen(assembled_requestCafe)
	rescode_c = response_c.getcode()

	if(rescode_c == 200):
		response_body_c = json.load(response_c)
	else:
		print("Error Code:" + rescode_b)
	return render(request, 'store/detail.html', locals())

👉🏻일단 전체 코드 투척ㅋㅋㅋㅋ 하나씩 살펴보자.

store = get_object_or_404(Store, pk=pk)

client_id = "본인의 API KEY"
client_secret = "본인의 SECRET KEY"
  • 먼저 해당 대리점을 불러와 store 변수에 넣어준다.
  • client_id, client_secret에 본인 API KEY와 SECRET KEY를 넣어준다.
encText = urllib.parse.quote(store.name)
urlCafe = "https://openapi.naver.com/v1/search/cafearticle?query=" + encText + "&display=50&start=1"
  • 검색 url을 생성해준다.
    • encText에는 네이버에 검색할 단어를 넣어 생성한다. (나는 대리점을 검색하기 때문에 store.name을 넣었다.)
    • request를 보낼 url과 조합해서 최종적으로 검색 url을 생성한다.

👉🏻display 쿼리스트링 부분에는 다양한 옵션을 넣을 수 있다. 아래 표를 참고하자! 아래 표대로면 내가 설정한 옵션은 검색결과를 첫번째부터 50개를 가져오는 것이다.

display 출력 건수
start 검색의 시작 지점
sort 정렬(sim(기본): 유사도 순, date: 날짜 순)
assembled_requestCafe = urllib.request.Request(urlCafe)
assembled_requestCafe.add_header("X-Naver-Client-Id",client_id)
assembled_requestCafe.add_header("X-Naver-Client-Secret",client_secret)
response_c = urllib.request.urlopen(assembled_requestCafe)
rescode_c = response_c.getcode()
  • 검색결과를 response_c에 통신상태를 rescode_c에 저장한다.
if(rescode_c == 200):
    response_body_c = json.load(response_c)
else:
    print("Error Code:" + rescode_b)
return render(request, 'store/detail.html', locals())
  • 만약 rescode_c가 200이라면 API 통신에 성공한것이므로 그대로 json으로 파싱해 template로 보내준다.
  • 200이 아니라면 error 메세지를 출력해준다.
# {'lastBuildDate': 'Tue, 12 Apr 2022 16:55:04 +0900', 
#  'total': 1327, 
#  'start': 1, 
#  'display': 50, 
#  'items': [
# 	{'title': '버거킹 아이스아메리카노 쿠폰 드려요(오늘까지 사용가능, <b>직영점</b>만)', 
# 	'link': 'http://cafe.naver.com/msbabys/4136166',
# 	'description': '<b>직영매장</b>에서만 사용 가능(사용불가 <b>매장</b>:대전관평점, 대전도안점, 대전시청점, 대전현대아울렛점, )... 제외<b>매장 서울</b> 방배카페골목,석관점,숭례문점,연희점, 청담주성점 인천/경기 인천공항점, 인천공항1점... ',
#  	'cafename': '대전세종맘스베이비(대세맘,대전맘,세...', 'cafeurl': 'https://cafe.naver.com/msbabys'},
# 	{'title': '한가롭게 쉬던 도중에 <b>KT</b> <b>직영점</b>으로부터 전화가 왔습니다',
#  	'link': 'http://cafe.naver.com/anycallusershow/2938742',
# 	'description': '일단 070번호는 완전 수신안되게 제가 통신사에 전화해서 막아놨고, <b>서울</b>에서 연락 올 일이 있을수도 있기에... 이전에 제가 한번 방문했었던 <b>KT</b>플라자 <b>직영점</b>이었습니다. 쓰던 폰이 고장나서 중고폰 사고 전산상으로 새로... ',
#  	'cafename': '삼성스마트폰-갤럭시S22/Z플립3/갤럭시...', 'cafeurl': 'https://cafe.naver.com/anycallusershow'},
# 	...중략...
# ]}

👉🏻검색결과는 다음과 같은 형식으로 온다. 이 결과를 이용해서 template에서 잘 출력해보자!

 

template에서 검색결과 출력하기


<h4>인터넷 검색 결과</h4>

{% if response_body_c %}
  {% for item in response_body_c.items %}
    <div>{{ forloop.counter }} {{ item.title }}</div>
  {% endfor %}
{% endif %}

👉🏻template에 다음과 같은 코드를 추가해주자!

  • response_body_c의 items를 for문에 돌려주면서 for문 순서와 함께 title을 뽑아주었다.

50번까지 title을 잘 출력하고 있다!

👉🏻결과는 잘 나오고 있지만 주황색 화살표 부분처럼 곳곳에 '<b></b>'라는 알수없는 문자도 같이 출력되고 있다🥲 또한 대리점과는 관련없는 검색결과도 많다.

💡그럼 이제 '<b></b>'이 문자를 삭제해주고 관련도가 높은 검색결과를 딱 2개만 보여주도록 코드를 수정해보자!

 

관련도가 높은 검색결과 2개만 출력하기


관련도가 높다는 기준을 무엇으로 잡아야 할까?
우리는 대리점 이름이 검색결과 title에 포함되어 있으면 검색결과가 높다고 판단하기로 했다!
def detail(request, pk):
	store = get_object_or_404(Store, pk=pk)
	client_id = "eIeH_u1umjYnDgZ6Kj5Z"
	client_secret = "NBwbDfJtHO"

	encText = urllib.parse.quote(store.name)
	urlCafe = "https://openapi.naver.com/v1/search/cafearticle?query=" + encText + "&display=50&start=1"#

	assembled_requestCafe = urllib.request.Request(urlCafe)
	assembled_requestCafe.add_header("X-Naver-Client-Id",client_id)
	assembled_requestCafe.add_header("X-Naver-Client-Secret",client_secret)

	response_c = urllib.request.urlopen(assembled_requestCafe)
	rescode_c = response_c.getcode()

	name_list = store.name.split(' ')
	cafe_list = []
	def get_post(response_body, list):
		count = 0
		up = False
		for item in response_body['items']:
			if count == 2:
				break
			up = False
			for name_part in name_list:
				if up == True:
					break
				if name_part in item['title']:
					title = re.sub("<b>|</b>","",item['title'])
					description = re.sub("<b>|</b>","",item['description'])
					
					list.append(
						{'title': title,
						'link': item['link'],
						'description': description}
					)
					count += 1
					up = True

	if(rescode_c == 200):
		response_body_c = json.load(response_c)
		if (len(response_body_c['items']) != 0):
			get_post(response_body_c, cafe_list)
	else:
		print("Error Code:" + rescode_b)
	return render(request, 'store/detail.html', locals())

👉🏻변경된 최종 코드!

name_list = store.name.split(' ')
cafe_list = []
#각 아이템들의 <b></b> 제거하고 제목,링크,글 저장
def get_post(response_body, list):
    count = 0
    up = False
    for item in response_body['items']:
        #글을 이미 2개 저장했다면 더 볼 필요가 없다.
        if count == 2:
            break
        up = False
        for name_part in name_list:
            #이미 제목에 이름이 포함되었다는 것을 알았다면 더 볼 필요가 없다.
            if up == True:
                break
            if name_part in item['title']:
                title = re.sub("<b>|</b>","",item['title'])
                description = re.sub("<b>|</b>","",item['description'])

                list.append(
                    {'title': title,
                    'link': item['link'],
                    'description': description}
                )
                count += 1
                up = True

👉🏻먼저 '<b></b>'문자를 제거해주고, 관련도가 높은 글 2개만 가려주는 코드이다.

  • 대리점 이름을 띄어쓰기 기준으로 나눠 name_list에 담아주고, cafe_list는 최종적으로 출력할 결과를 넣기 위한 리스트이다.
  • count를 이용해 list에 글이 2개까지만 저장되게 한다. 그리고 up을 이용해 name_list의 요소 하나라도 title에 포함되면 for문을 중단해 불필요한 연산을 막아준다.
  • response_body에서 items를 for문 돌리고 name_list도 for문 돌려준다.
    • 각 item에 대리점 이름이 포함되어있는지 확인한다.
    • 포함되어 있다면 title과 description에서 '<b></b>'문자를 제거해주고 인자 list에 title, link, description을 추가해준다.
if(rescode_c == 200):
    response_body_c = json.load(response_c)
    if (len(response_body_c['items']) != 0):
        get_post(response_body_c, cafe_list)
else:
    print("Error Code:" + rescode_b)

👉🏻위의 함수(get_post)는 rescode_c가 200이고 response_bodey_c의 items에 요소가 하나라도 들어있으면 실행한다.

<h4>인터넷 검색 결과</h4>

{% if cafe_list %}
  <h5>cafe</h5>
  {% for item in cafe_list %}
    <h5><a href="{{item.link}}" target="_blank">{{item.title}}</a></h5>
    <h6><div>{{item.description}}</div></h6>
{% else %}
    <div class="no-review">인터넷 상에서 이 매장과 관련된 정보가 없어요.</div>
    <br>
{% endif %}

👉🏻그리고 template는 다음과 같이 바꿔준다.

  • title과 descriptiton을 표시한다.
  • title을 누르면 해당 카페 글로 이동한다.
  • 글이 하나도 없다면 관련 정보가 없다는 글을 띄운다.

검색결과가 잘 나오고 있다!!
검색결과가 없을 때 화면

728x90