728x90

대표적인 소셜사이트에서 제공하는 검색api를 활용해서, 원하는 검색어에 대한 블로그 리뷰글들을 모아서 가지고 와야했다.

대표적으로 구글, 카카오(다음), 네이버 api를 활용할 수 있는데, 나는 최종적으로 네이버 검색 api를 사용했다.

 

우선, 검색 api 사용 시에, 위 세개의 api를 모두 사용할 필요는 없는게, 각 브라우저에서 최대한 모든 사이트의 검색 결과를 제공하기 때문이다. 즉 네이버 api를 사용한다고 해서, 다음의 티스토리 블로그 리뷰를 불러올 수 없는 것은 아니다. 

 

그렇다면 굳이 네이버 api를 사용한 이유는?

1. 아무래도 한국 서비스이다보니, 가이드가 좀 더 따라가기 쉬웠다가 어쩔 수 없는 가장 큰 이유.....

2. 그렇다면 카카오 대신 네이버를 쓴 이유

-> 카카오 검색 api와 네이버 검색 api 결과값의 가장 큰 이유는 해당 블로그 리뷰의 썸네일을 제공하느냐 아니냐였다. 그래서 카카오 api를 써보려했다.

-> 그러나 카카오 검색 api는 검색어 쿼리를 보낼때 검색 연산자 적용이 되지 않았다.

썸네일값을 결과에 제공하지 않아도 네이버 검색 api를 사용할 수 밖에 없었던 가장 큰 이유였다. 특정 검색어 하나를 쿼리로 보내서 결과값을 얻더라도, 여러 이유로 전혀 관련없는 결과값들이 잡힐 가능성이 컸고 그에 따라서, 검색 연산자로 미리 제거하고 결과값을 받는 것이 효율적이었기 때문이다.

 

네이버 검색 api로 블로그 리뷰 받아오기.

 

1. 네이버 개발자 센터에서 오픈 api 신청하고, client id와 client secret값 받기

 

2. 네이버 개발자 센터 사이트에서 제공하는 가이드를 보고 api 코드 작성하기

-> 검색 api의 경우, 기본적인 코드는 매우 간단하다. 나는 네이버에서 제공하는 가이드를 기반으로 아래와 같이 코드를 작성했다.

query = '원하는 검색어'
url = "https://openapi.naver.com/v1/search/blog"
params = {"query": query, "sort": "sim", "display": 100}
headers = {
    "X-Naver-Client-Id": NAVER_CLIENT_ID,
    "X-Naver-Client-Secret": NAVER_CLIENT_SECRET,
}
resp = requests.get(url, headers=headers, params=params)
resp.raise_for_status()
result = resp.json()

    

매우 간단하다! 검색 api의 경우는 api를 이해하고 코드를 작성하는 부분은 금방 해결했고,

시간이 오래 걸렸던 부분은 검색 결과의 정확도를 높이기 위한 검색어 필터 작업이었다.

 

우선 요청 변수 값을 조정하여, 결과값의 정렬 옵션과, 한 페이지에 보여지는 결과값 수를 지정해주었다. 해당 값은 네이버 개발자 센터의 블로그 검색 api 사이트에 잘 설명되어있다.

3. 검색어 쿼리에 필터링 하여 결과값 정확도 높이기

-> 나는 해당 부분을 총 2단계를 거쳐서 진행했다.

 

 1) 쿼리값에 검색 연산자를 적용하여, 꼭 들어가야 하는 검색어와 없어야 하는 검색어 적용

 -> 이 때의 검색어 필터는 리뷰글의 제목 혹은 컨텐츠에 포함 여부이다. 즉 제목이든 내용이든 어디에도 있거나 없어야 하는 내용이다.

반대로, 특정 검색어가 본문에 잠깐 등장하는 것은 상관없는데, 제목에 존재해서, 블로그 리뷰 내용의 메인이 해당 검색어 일 경우만 제거하고 싶다면, 해당 검색 연산자를 통해서 제거하면 안된다.

 

ex) 제주도의 x호텔에 대한 리뷰 결과를 받고 싶다. 호텔에 대한 리뷰글이지만, 내용에는 카페나 식당에 대한 내용이 잠깐 언급될수도 있다. 그러나 제목이 x호텔에 대한 리뷰라면, 메인은 호텔 리뷰글이므로 해당 리뷰는 검색결과에 포함되는 것이 맞다.

이런 애매한 검색어는 우선 연산자에 적용하지 말자.

 

검색 연산자

  • "검색어" : 꼭 포함되어야 하는 검색어
  • +검색어 : 해당 검색어를 포함
  • -검색어 : 해당 검색어를 제외
query = "무조건 포함되어야 하는 검색어1" "무조건 포함되어야 하는 검색어2" +포함되어야 하는 검색어 
         -제거되어야 하는 검색어1 -제거되어야 하는 검색어2'

-> 해당 연산자에서 핵심은 앞의 검색어와 뒤의 검색어 사이에 스페이스바 하나를 띄워주고, 연산기호와 검색어 사이는 띄우면 안되는 것이다. 실제로 연산자 적용할 때 띄어쓰기를 고려하지 않고 했는데 연산자가 먹히지 않아서 초반에 애를 먹었다.

 

 2) 위에서 언급한 애매한 검색어가 제목에 있는 경우 필터링

 -> 1)의 경우에서 언급한 검색어의 경우, 제목에서 등장할 때, 가령 '제주도 x호텔 카페' 인 경우에는, 제외해 주어야 한다. 

리뷰 내용에 잠깐 등장하는 것은 괜찮지만, 제목에 등장하면 이는 검색결과값이 완전히 달라질 수도 있기 때문!

이의 경우에 대해 score필터를 넣었다. 즉, 특정 검색어가 제목에 있는 경우, 해당 리뷰의 점수에 마이너스를 주는 것이다.

 F_KEYWORDS = ["검색어1", "검색어2"]
  idx = 0
  while idx < len(result["items"]):
      review = result["items"][idx]
  
      score = 0
      # false keyword 포함 개당 -300점
      score += sum(
          list(
              map(
                  lambda x: -300 if x in review["title"].replace(" ", "") else 0,
                  F_KEYWORDS,
              )
          )
      )
      idx += 1

-> 띄어쓰기를 고려해서, 검색결과의 제목에 모든 띄어쓰기를 없애주고 비교해주었다.

 

4. 제목과 컨텐츠에 태그 값 제거해주기

# 태그 제거
review["title"] = re.sub(
    "&(.*?);", "", review["title"].replace("<b>", "").replace("</b>", "")
)
review["description"] = re.sub(
    "&(.*?);",
    "",
    review["description"].replace("<b>", "").replace("</b>", ""),
)

 

최종 코드

query = '"꼭 포함되어야 하는 검색어1" "꼭 포함되어야 하는 검색어2" +포함되어야 하는 검색어 
      -제거되어야 하는 검색어1 -제거되어야 하는 검색어2'
url = "https://openapi.naver.com/v1/search/blog"
params = {"query": query, "sort": "sim", "display": 100}
headers = {
    "X-Naver-Client-Id": NAVER_CLIENT_ID,
    "X-Naver-Client-Secret": NAVER_CLIENT_SECRET,
}
resp = requests.get(url, headers=headers, params=params)
resp.raise_for_status()
result = resp.json()

F_KEYWORDS = ["검색어1", "검색어2"]
# score / 태그 제거 while문으로 실행
idx = 0
while idx < len(result["items"]):
    review = result["items"][idx]
    # 태그 제거
    review["title"] = re.sub(
        "&(.*?);", "", review["title"].replace("<b>", "").replace("</b>", "")
    )
    review["description"] = re.sub(
        "&(.*?);",
        "",
        review["description"].replace("<b>", "").replace("</b>", ""),
    )
    score = 0
    # false keyword 포함 개당 -300점
    score += sum(
        list(
            map(
                lambda x: -300 if x in review["title"].replace(" ", "") else 0,
                F_KEYWORDS,
            )
        )
    )
    idx += 1

 


리뷰글들은 계속해서 새로 올라오므로 주기적으로 api를 실행해 주어야 한다.

나는 해당 api를 celery task에 등록하여 2주에 한 번씩 등록하게 해주었다.

(celery는 python django의 비동기 스케줄링 라이브러리인데, 추가적으로 공부해서 정리할 예정!)

소셜리뷰는 db에 저장하여 보관했으며, api를 새로 실행할때마다, 기존 db에 있는 link와 새로 등록할 리뷰의 link를 비교하여, 중복 등록을 방지했다. 

728x90

+ Recent posts