상세 컨텐츠

본문 제목

LLM with Naver API - 크롤러 구현

NLP

by LYShin 2024. 5. 17. 19:30

본문

이번 글에서는 네이버 API로 네이버 블로그 / 지식인 글을 검색하고 질문과 유사도가 높은 글을 크롤링하는 것을 다룹니다. windows 환경에서 docker desktop을 사용하여 docker를 사용하고, 코드 작성 및 ssh 연결은 vscode를 사용합니다.

 

** 본문의 `Rerank`표현은 RAG 분야에서 사용되는 `Rerank`와 다를 수 있습니다. 본문에서는 단순히 랭크를 재정렬한다는 의미에서 `Rerank`라는 표현을 사용했습니다.

 

1. 네이버 블로그 크롤링

 

네이버 검색 API를 사용하면 검색 키워드를 기반으로 블로그의 [링크, 요약, 블로그명, 블로그링크, 게시날짜]를 전달받을 수 있습니다. 블로그 본문 요약과 질문의 유사도를 기반으로 N개의 블로그를 크롤링합니다. 이후 블로그 본문과 질문의 유사도를 기반으로 K개의 블로그 본문만 프롬프트에 추가합니다. 코드와 함께 차근차근 풀어보도록 하겠습니다.

 

 

1.1 네이버 검색 API 사용

가장 먼저, https://developers.naver.com/products/service-api/search/search.md [네이버 검색 API 링크]로 접속하여 API 이용신청하여 client id와 client secret을 받습니다. API를 신청하셨다면 이제 Python으로 이를 사용해 보겠습니다.

 

import urllib
import json

# 클라이언트 ID와 시크릿 키를 스트링 형태로 저장
client_id = "client_id"
client_secret = "Client_secret"

def search_naver_blog(keyword):
    # 검색어를 URL에 포함할 수 있도록 인코딩
    encText = urllib.parse.quote(keyword)
    
    # 네이버 블로그 검색 API의 요청 URL 생성
    url = "https://openapi.naver.com/v1/search/blog?query=" + encText
    
    # API 요청을 위한 Request 객체 생성
    query_response = urllib.request.Request(url)
    
    # API 요청 헤더에 클라이언트 ID와 시크릿 키 추가
    query_response.add_header("X-Naver-Client-Id", client_id)
    query_response.add_header("X-Naver-Client-Secret", client_secret)
    
    # API 요청을 보내고 응답을 받음
    response = urllib.request.urlopen(query_response)
    
    # 응답 코드 확인
    rescode = response.getcode()

    if(rescode == 200):  # 응답 코드가 200인 경우 (정상 응답)
        # 응답 본문을 읽어서 JSON 형식으로 변환
        response_body = response.read()
        result = json.loads(response_body.decode('utf-8'))
    else:  # 응답 코드가 200이 아닌 경우 (실패)
        return "fail", "fail", "fail"

    # 결과의 링크와 본문 요약을 저장할 리스트 초기화
    link_list = []
    desc_list = []
    
    # 응답 결과에서 필요한 정보 추출
    for item in result.get('items'):
        # 블로그 링크를 리스트에 추가
        link_list.append(item['link'])
        
        # HTML 태그 제거 후 본문 요약을 리스트에 추가
        desc_list.append(item['description'].replace('<b>', '').replace('</b>', ''))
        
    # 전체 결과, 링크 리스트, 본문 요약 리스트 반환
    return result, link_list, desc_list

 

 

함수 작성이 끝났다면, 키워드를 입력하여 검색이 수월하게 이루어지는지 확인해 보겠습니다. 기본적으로 API는 한 번에 10개의 블로그 글을 출력합니다.

 

result, link_list, desc_list = search_naver_blog("닭한마리 레시피")
link_list, desc_list
(['https://blog.naver.com/peace8012/223419477765',
  'https://blog.naver.com/ch-threeworld/223435298761',
  ... ...
  'https://blog.naver.com/yooniyoon__/223442719157',
  'https://blog.naver.com/ssieie/222879941051'],
 ['닭한마리 레시피 닭한마리 칼국수 레시피 닭한마리 양념 소스 닭한마리레시피 첫째의 중간고사... 메뉴 닭.한.마.리를 선택했어요 한 마리로 온 가족이 푸짐하게 즐길 수 있는 메뉴랍니다 국물을 조금... ',
  '그럼 진한 국물의 닭한마리 레시피 양념장 소스 만드는법까지 자세히 소개해볼게요! 닭한마리 + 양념장 소스 (2인기준) [재료] 절단 닭 1팩 감자 2~3알 대파 3~4대 양파 1개 -선택 : 부추, 버섯 [국물 양념] 다진마늘... ',
  ... ...
  '좋아하는 닭 한마리 레시피를 들고왔어요 바로 시작해볼게요 ! 준비재료 닭 (닭볶음탕용 600g + 닭다리... #집밥 #요리 #저녁메뉴추천 #닭한마리 #닭한마리칼국수 #레시피 #닭한마리레시피 #닭죽 #일상 #소통 #이웃',
  '닭백숙 끓이는법 환절기 보양식 닭한마리 레시피 환절기만 되면 컨디션이 다운되는 알콩.. 만사 귀찮고... 함께 만들어 보아요~~ 생 닭/750g 한 마 리 깐 마 늘 10알 양 파 1개 대 파 1.5대 깐 생 강 한 조 각 황 기/대... '])

 

 

1.2 블로그 본문 요약 기반 Rerank

 

블로그 본문 요약과 링크를 가져왔다면, 이제 10개의 블로그 글 중 질문과 유사도가 높은 N개의 글만 남겨야 합니다. 벡터 기반의 유사도를 계산하기 위해 임베딩 모델을 사용해야 합니다. 한국어 임베딩이 가능한 모델 중 BAAI의 `bge-m3`와 OpenAI의 `text-embedding-3-large`를 비교한 후 하나를 선택하겠습니다.

 

# bge-m3

import numpy as np
from FlagEmbedding import BGEM3FlagModel

# BGEM3FlagModel 인스턴스 생성: BAAI/bge-m3 모델 로드, FP16 방식으로 CUDA 디바이스 사용
bge_model = BGEM3FlagModel("BAAI/bge-m3", use_fp16=True, device="cuda")

def bge_top_k(model, question, content_list, link_list, top_k, max_length):
    # question과 content_list 내 각각의 content를 페어로 하는 문장 쌍 생성
    sentence_pairs = [[question, i] for i in content_list]
    
    # 문장 쌍에 대해 모델로 점수 계산, weighted average 방식으로 최종 점수 얻음
    scores = model.compute_score(
        sentence_pairs,
        max_passage_length=max_length,
        weights_for_different_modes=[0.5, 0.1, 0.3]
    )
    
    # content와 해당 점수를 딕셔너리 형태로 저장
    dict_ = [{'content': content, 'score': score} for content, score in zip(content_list, scores['colbert+sparse+dense'])]
    
    # colbert + sparse + dense 점수를 기반으로 상위 top_k 인덱스 추출
    top_k_index = np.argsort(scores['colbert+sparse+dense'])[-top_k:].tolist()
    
    # 상위 top_k에 해당하는 content 및 link 추출
    top_k_desc = np.array(content_list)[top_k_index].tolist()
    top_k_link = np.array(link_list)[top_k_index].tolist()
    
    return dict_, top_k_desc, top_k_link

# 예시 질문
question = '닭한마리 레시피를 정리해줘. 정확한 용량과 순서를 포함해서 정리해줘.'

# 질문에 대해 각 본문 요약의 점수 계산 및 상위 결과 얻기
bge_scores, bge_k_desc, bge_k_link = bge_top_k(bge_model, question, desc_list, link_list, 4, 200)
# text-embedding-3-large

from openai import OpenAI
from embedding_utils import cosine_similarity  # https://github.com/openai/openai-cookbook/blob/main/examples/utils/embeddings_utils.py
import numpy as np

# OpenAI 클라이언트를 초기화합니다. API 키를 사용하여 인증
openai_client = OpenAI(api_key="api_key")

def openai_top_k(emb_model, question, content_list, link_list, top_k, max_length):
    # question을 임베딩 벡터로 변환
    q_embed = emb_model.create(input=question, model='text-embedding-3-large').data[0].embedding
    
    # content_list를 임베딩 벡터로 변환
    c_embeds = emb_model.create(input=content_list, model='text-embedding-3-large')
    c_embeds = [data.embedding for data in c_embeds.data]
    
    scores = []
    # 각 content 임베딩 벡터와 question 임베딩 벡터 간의 코사인 유사도를 계산
    for c_embed in c_embeds:
        score = cosine_similarity(q_embed, c_embed)
        scores.append(score)
    
    # 유사도 점수를 기준으로 상위 top_k개의 인덱스를 추출
    top_k_index = np.argsort(scores)[-top_k:].tolist()
    
    # 추출된 인덱스에 해당하는 content와 link를 추출
    top_k_desc = np.array(content_list)[top_k_index].tolist()
    top_k_link = np.array(link_list)[top_k_index].tolist()
    
    # 컨텐츠와 유사도 점수를 딕셔너리 형태로 저장
    dict_ = [{'content': content, 'score': score} for content, score in zip(content_list, scores)]
    
    return dict_, top_k_desc, top_k_link

# OpenAI의 임베딩 모델을 지정
openai_emb_model = openai_client.embeddings

# 질문에 대해 각 본문 요약의 점수 계산 및 상위 결과 얻기
openai_scores, openai_k_desc, openai_k_link = openai_top_k(openai_emb_model, question, desc_list, link_list, 4, 200)

 

 

두 코드 작성이 완료되면, 두 임베딩 벡터를 사용한 검색 결과를 살펴보겠습니다.

 

# bge-m3

['닭백숙이랑 레시피가 거의 같은데 닭을 통으로 삶으면 닭백숙, 잘라진 닭으로 만들면 닭한마리 같으다 ㅋㅋ 부재료도 살짝 다르긴 하지만 ㅋㅋ 닭한마리 레시피 (2인분 기준) 재료: 닭 1kg, 감자 1개, 부추 한 줌... ',
 '닭한마리 레시피 재료 (3-4인분) 닭한마리 재료 닭볶음탕용 1마리 감자 3알 대파 3대 양파 1개 통마늘 10알 통후추 10알 칼국수 사리 밀떡 사리 소금 1큰술 미원 1티스푼 치킨스톡 1큰술 소스 재료 채 썬 양배추 닭... ',
 '좋아하는 닭 한마리 레시피를 들고왔어요 바로 시작해볼게요 ! 준비재료 닭 (닭볶음탕용 600g + 닭다리... #집밥 #요리 #저녁메뉴추천 #닭한마리 #닭한마리칼국수 #레시피 #닭한마리레시피 #닭죽 #일상 #소통 #이웃',
 '닭한마리 레시피 닭한마리 칼국수 레시피 닭한마리 양념 소스 닭한마리레시피 첫째의 중간고사... 메뉴 닭.한.마.리를 선택했어요 한 마리로 온 가족이 푸짐하게 즐길 수 있는 메뉴랍니다 국물을 조금... ']
 
 # text-embedding-3-large
 
 ['닭한마리 레시피 재료 (3-4인분) 닭한마리 재료 닭볶음탕용 1마리 감자 3알 대파 3대 양파 1개 통마늘 10알 통후추 10알 칼국수 사리 밀떡 사리 소금 1큰술 미원 1티스푼 치킨스톡 1큰술 소스 재료 채 썬 양배추 닭... ',
 '그럼 진한 국물의 닭한마리 레시피 양념장 소스 만드는법까지 자세히 소개해볼게요! 닭한마리 + 양념장 소스 (2인기준) [재료] 절단 닭 1팩 감자 2~3알 대파 3~4대 양파 1개 -선택 : 부추, 버섯 [국물 양념] 다진마늘... ',
 '닭한마리 레시피 닭한마리 칼국수 레시피 닭한마리 양념 소스 닭한마리레시피 첫째의 중간고사... 메뉴 닭.한.마.리를 선택했어요 한 마리로 온 가족이 푸짐하게 즐길 수 있는 메뉴랍니다 국물을 조금... ',
 '닭백숙이랑 레시피가 거의 같은데 닭을 통으로 삶으면 닭백숙, 잘라진 닭으로 만들면 닭한마리 같으다 ㅋㅋ 부재료도 살짝 다르긴 하지만 ㅋㅋ 닭한마리 레시피 (2인분 기준) 재료: 닭 1kg, 감자 1개, 부추 한 줌... ']

 

결과적으로, 10개의 블로그 중 3개의 블로그 글이 두 모델에서 상위에 오른 것을 확인할 수 있습니다. 네이버 검색 API의 성능이 탄탄하기 때문에 두 모델의 결과가 크지 않는 것으로 보입니다.

 

결과에 따라, 베이스라인에서는 비용을 최소화하고자 bge-m3 모델을 사용하겠습니다.

 

 

1.3 블로그 본문 크롤링

 

Rerank한 블로그 글에 대해 본문 내용을 크롤링합니다. 일반적으로, 크롤링에서 가장 문제가 되는 부분은 HTML구조입니다. 모바일의 경우 네이버 블로그의 구조가 안정된 것으로 확인되었고, 이에 따라 일반 링크가 아닌 모바일 링크를 사용하여 블로그 본문을 크롤링합니다.

 

import requests
from bs4 import BeautifulSoup

def search_content(link):
    # 모바일 버전의 네이버 블로그 링크로 변환합니다.
    link = link.replace("blog", "m.blog")
    
    # 링크에 대해 HTTP GET 요청을 보냅니다.
    response = requests.get(link)
    
    # 요청이 실패한 경우 예외를 발생시킵니다.
    response.raise_for_status()
    
    # 응답 내용을 BeautifulSoup을 사용하여 HTML로 파싱합니다.
    soup = BeautifulSoup(response.content, "html.parser")
    
    # 메타 태그에서 포스트 제목을 추출합니다.
    title = soup.find('meta', property="og:title")['content']
    
    content = []
    
    # 블로그 내용이 담긴 div 태그를 모두 찾습니다.
    res_postct = soup.findAll('div', class_="post_ct")
    
    try:
        # 모든 div 태그의 텍스트를 결합한 다음, "URL복사신고하기" 이후의 내용을 추출합니다.
        content = " ".join([item.get_text(strip=True) for item in res_postct]).split('URL복사신고하기')[1]
    except:
        # 예외가 발생한 경우, 모든 div 태그의 텍스트를 결합한 내용을 그대로 사용합니다.
        content = " ".join([item.get_text(strip=True) for item in res_postct])
    
    # 블로그 제목과 내용을 반환합니다.
    return title, content
    
search_content(bge_k_link[0])
# 제목, 본문

('닭한마리 레시피 닭한마리 칼국수 양념 소스 닭요리',
 '\u200b\u200b닭한마리 레시피닭한마리 칼국수 레시피닭한마리 양념 소스 닭한마리레시피\u200b\u200b\u200b첫째의 중간고사 시험기간이 다가옵니다나름? 공부한다고 요즘 열심히라체력 보충 좀 시키자 싶어닭요리 준비했어요저희 첫째는 닭백숙이나 삼계탕을즐겨 먹는 편이라서요오늘은 간단하게 만들 수 있는 메뉴닭.한.마.리를 선택했어요한 마리로 온 가족이 푸짐하게 즐길 수 있는메뉴랍니다\u200b\u200b\u200b국물을 조금 넉넉하게 잡아서남은 국물에는 이렇게 면을 넣어서삶아 먹어도 맛있답니다닭한마리칼국수 요것 또한 빠지면 섭섭하지요\u200b\u200b\u200b저는 고기보다도 요게 더 맛있더라는...^^호로록호로록 어찌나 맛있던지요~\u200b일부러 식당에 가서 드시는 분들도많으신데 이제 집에서 간편하게 만들어드셔보세요제가 만드는 방법 알려드릴게요^^\u200b\u200b계량 기준1T (큰 술) =15ml / 1t (작은 술) = 5ml1컵 = 180ml(3-4인분)\u200b재료 준비토막닭 1.1kg감자 작은 크기 2개양파 1/2개부추 2줌홍고추 약간칼국수 2인분참치액 1T (국간장 가능)소금 0.3T후춧가루 약간\u200b닭 1차 데칠 때월계수잎 1장통후추 0.3T청주 또는 소주 1바퀴물 2리터\u200b2차로 푹 삶을 때물 약 2리터통마늘 한 줌\u200b닭한마리 소스진간장 2T식초 1T다진 마늘 0.3T연겨자 0.3T올리고당 1T\u200b\u200b1. 핏물, 잡내 제거를 위한 초벌 데치기\u200b토막닭으로 준비했고요 바로 끓는 물에넣고 1차로 삶아줄 거랍니다\u200b냄비에 물 2리터가량 붓고월계수잎 1장, 통후추 0.3T를 넣어 끓여줍니다물이 팔팔 끓어오르면바로 이어서청주 또는 소주 1바퀴둘러줍니다\u200b\u200b\u200b포장 용기에서 바로 꺼내 끓는 물에 풍덩~삶아줄게요약 5분 정도 삶아주면 된답니다\u200b\u200b\u200b기름과 핏물 응고된 것들이둥둥 떠오르지요 ~\u200b\u200b2. 손질하기\u200b약 5분간 삶은 것은찬물에 헹궈줍니다이때 불필요한 껍질이나 지방 등이있으면 제거해 주고요특히 뼈 사이사이에 응고되어 굳은핏물이나 내장 등도 깨끗이 씻어서제거해 줍니다두터운 다리 살 등에도 칼집을 살짝 넣어줄게요\u200b\u200b3. 2차 삶기\u200b다시 깨끗하게 씻은 냄비에 물 10컵약 2리터를 붓고손질한 것을 모두 넣은 후센 불로 올려서 끓이기 시작합니다\u200b\u200b\u200b통마늘 한 줌은꼭지를 제거해 준 후넣어줍니다\u200b\u200b\u200b국물이 팔팔 끓기 시작하면총 30분-40분정도 푹 삶아주면 되는데요이때 5분간은 뚜껑을 열고 삶고,나머지 시간은 중불로 줄인 다음뚜껑을 살짝 걸쳐놓고 삶아줍니다\u200b\u200b4. 부재료 준비\u200b삶는 동안 나머지 부재료와 소스만들어볼게요\u200b양파 1/2개는 도톰하게 채 썰어줍니다\u200b\u200b\u200b작은 크기의 감자 2 개 준비했어요껍질을 깎고 마찬가지로 너무 얇지 않도록썰어줍니다\u200b\u200b\u200b영양부추가아주 저렴하길래 구입했어요정리가 안되고 제멋대로라서 ㅋㅋㅋ아쉽긴 하지만 그래도 영양가는 높은채소지요?깨끗이 씻어 물기를 빼준 후 칼로 듬성듬성잘라줍니다일부는 마지막 완성 때쯤 국물에 넣어마무리하고 일부는 소스에 넣어같이 찍어 먹어도 맛있답니다\u200b\u200b\u200b홍고추 1개어슷 썰어줍니다\u200b\u200b\u200b국물에 넣고 끓여줄칼.국.수 면 2인분준비했어요제가 좋아하는 칠갑농산 면 ㅎㅎ면은 나중에 넣기 전 체반에 담아물로 겉면의 가루를  헹궈내고 삶으면국물이 걸쭉하거나 탁해지지 않는답니다\u200b\u200b5. 찍어 먹는 소스 만들기\u200b살코기를 찍어 먹을 겨자소스 만들어볼게요소스 비율은 (T 기준)1진간장 2식초 1다진 마늘 0.3연겨자 0.3올리고당 1분량의 양념을 넣고 섞어줍니다식구가 많다면  2배로 늘려서만드시면 됩니다\u200b\u200b6. 감자, 양파 넣기\u200b약 30분 정도 삶았어요뚜껑을 열면 푹~~ 잘 익은 상태입니다위로 뜬 기름은 살살 걷어내고요\u200b\u200b\u200b밑간 양념으로참치액 1T, 소금 0.3T를함께 넣고\u200b\u200b\u200b감자 썬 것과 양파도 함께 넣고약 10분간 더 끓여줍니다\u200b\u200b7. 부추 , 홍고추 넣기\u200b10분 후 다시 뚜껑을 열고부추와 홍고추를 넣어주면끝~!\u200b\u200b\u200b간 보아 입맛에 맞도록 추가 간을 해주시고후춧가루 뿌려주면 완성입니다\u200b\u200b8. 완성\u200b저는 뚝배기에 옮겨 담아서 먹었습니다좀 더 따끈하게 드실 거면 식탁 위에 올려끓여 가면서 드셔도 되겠지요\u200b\u200b\u200b준비한 간장에 부추 약간 더해서즐겼어요!이렇게 먹어도 맛있답니다\u200b\u200b\u200b저는 파근파근 감자를 함께 먹어도맛있더라고요\u200b\u200b\u200b뚝배기에 떠서 담고 남은 국물에면을 넣고,남은 부추도 함께 넣어준 후삶아줄게요!이때 육수가 부족하다면 물 약간 추가하셔서끓여주어도 됩니다\u200b\u200b\u200b모자라는 간은 참치액이나 국간장소금 등을 활용하시면 됩니다\u200b\u200b\u200b닭칼국수라고 불러 되겠지요~요것만 먹어도 든든한 보양식이 된답니다\u200b온 가족이 모인 따뜻한 식탁에서즐기기에 좋은 메뉴~!크게 공을 들이지 ㅇ낳아도푸짐하답니다먹고 나면 정말 배가 든든해요!\u200b')

 

 

1.4 블로그 본문 기반 Rerank

 

블로그 본문 요약 내용 기반 Rerank와 동일하게, 이번에는 본문 내용을 기반으로 Rerank합니다. 4개의 링크로 블로그 본문을 크롤링한 후 이 본문 내용을 기반으로 Rerank하여 참고 문서로 사용할 2개의 블로그 본문을 뽑습니다.

 

# 결과를 저장할 리스트 초기화
top_k_content = []  # content를 저장할 리스트
top_k_title = []    # title을 저장할 리스트

# 주어진 bge_k_link를 순회
for link in bge_k_link:
    # link로 접속하여 title과 content를 추출
    title, content = search_content(link)
    
    # title_list에 title 추가
    top_k_title.append(title)
    
    # content를 EUC-KR 인코딩으로 변환 후 리스트에 추가
    # 'ignore' 옵션으로 변환 불가능한 문자열 무시
    top_k_content.append(content.encode('euc-kr','ignore').decode('euc-kr'))
    
    # 랜덤 시간(1초에서 4초 사이) 동안 일시정지 (크롤링 이슈 방지 및 서버 부담 감소 목적)
    time.sleep(np.random.randint(1,4))

# 상위 K개 요소를 선택하고, score, content, link를 반환 
scores, final_content, final_link = bge_top_k(emb_model, question, top_k_content, top_k_link, top_k=2, max_length=512)

# 최종 content 출력
final_content
['#닭한마리만들기#닭한마리레시피야심한 밤에 급 동대문 스타일의 닭한마리가 먹고 싶어서 쿠팡 로켓프레시로 재료 주문했다다음날 오후에 사부작 준비해서 하원한 아이들 저녁으로 먹이고나도 저녁식사로 칼국수까지 맛있게 끓여먹고퇴근한 남편도다음날 내 점심으로도 ㅋㅋ닭 한 마리로 몇 끼를 해결한 건지 수저로 국물 떠서 한입 맛보면 으어~ 소리가 절로 나오는 뜨끈한 요리. 쉽고 맛있는 닭한마리 같이 만들어보아요 닭한마리 레시피 재료 (3-4인분)닭한마리 재료닭볶음탕용 1마리감자 3알대파 3대양파 1개통마늘 10알통후추 10알칼국수 사리밀떡 사리소금 1큰술미원 1티스푼치킨스톡 1큰술소스 재료채 썬 양배추닭 육수 2국자고춧가루 2큰술다진 마늘 1큰술다진 파 2큰술설탕 1큰술진간장 2큰술연겨자 2티스푼식초 1큰술닭 요리할 때 가장 즐겨사는 다리 두 배 닭볶음탕!닭한마리 분량인데 다리가 4개 들어있어서 아주 가심비가 좋다목우촌 다리두배 닭볶음탕 (냉장), 1.2kg, 1개COUPANGshorturl.at닭은 팔팔 끓는 물에 1-2분 정도 초벌로 데친다찬물로 먼저 씻으면 생닭에 있는 바이러스나 균이 개수대에 튈 수 있어서 끓는 물에 먼저 퐁당 넣은 뒤에 씻는 걸 추천함초벌로 데친 닭은 물을 다 따라버리고 깨끗한 물로 꼼꼼하게 씻어준다2.5리터 정도의 물을 붓고 중강불로 닭을 40분간 삶는다이때 대파 2대, 양파 1개, 통마늘 10알, 통후추 10알을 함께 넣고 푹 고아준다이 재료들은 나중에 다 건져낼 것이므로 면보에 싸서 넣으면 더 편하다국물 위에 뜨는 거품도 잘 걷어내가며 끓인다여기까지만 해도 맛있는 냄새가 솔솔~그동안 닭한마리에 들어갈 부재료 손질하기감자는 2-3개 정도난 감자 좋아하니까 3개!!적당한 크기로 자르고 물에 담가 전분기를 빼준다대파 1대는 반 갈라서 큼직하게 썰어둔다그리고 빠질 수 없는 떡사리!꼭 밀떡으로 준비하기 미끄덩미끄덩 쫄깃쫄깃한 식감이 닭한마리 육수랑 완전 찰떡으로 어울림이번에 닭보다 떡을 더 맛있게 먹은 듯 ㅋㅋㅋ풀무원 밀떡볶이떡, 550g, 1개COUPANGshorturl.at밀떡도 물에 불려 준비한다처음에 넣었던 대파, 양파 건져내고육수에 간하기그리고 감자와 대파를 넣고 20분간 또 푹 끓여준다육수가 많이 줄어든 것 같으면 생수를 또 보충해 주면 된다!육수에 간은 소금, 미원, 치킨스톡으로 한다조금씩 넣어가며 간 맞추기미원과 치킨스톡은 선택 사항인데 넣어야 동대문에서 사 먹는 그 맛이 나서 기왕 먹는 거 더 맛있게 먹어야 하므로.. 넣어먹는 걸 추천한다 ㅎㅎㅎ청정원 쉐프의 치킨스톡, 340g, 1개COUPANGshorturl.at맛있게 끓는 동안 닭 찍어 먹을 겨자 소스 만들기닭 육수 2국자에 분량의고춧가루, 진간장, 설탕, 식초, 연겨자, 다진 마늘, 다진파를 넣고 잘 섞어준다식초와 연겨자를 넣어주는 게 맛의 핵심겨자 없다고 와사비 넣으면 영 어색하니 연겨자 꼭 준비하기 ㅎㅎ식초도 어느 정도 들어가야 닭육수, 양배추와 잘 어울린다드디어 완성늦게 퇴근하는 남편 먹을 분량은 따로 덜어두고~감자는 20분밀떡은 마지막에 넣고 5분 더 끓여주면 말캉하게 맛있게 먹을 수 있다이열치열 더운 날에 먹어도 좋고요즘처럼 쌀쌀한 날에 먹어도 좋은 동대문 닭한마리채 썬 양배추와 소스를 앞접시에 덜어서 취향껏 재료들을 찍어 먹으면 된다엄마가 국물 한입 먹더니 맛있게 잘 끓였다고 아빠도 좋아하겠다며 ㅋㅋ다음엔 아빠께도 끓여드려야지닭고기보다 떡, 감자가 더 좋은 나야..동대문표 소스도 매콤 새콤하니 찰떡~#닭칼국수만들기칼국수 사리도 전분 잘 씻어내고 푹 끓여왔다진한 닭 육수에 칼국수 사리 조합이면 말해 뭐해닭한마리 레시피대로 끓인 뒤 닭 가슴살 위주로 살코기 찢어서 같이 끓이면 그게 바로 닭칼국수 ㅎㅎ대상 찰진생칼국수, 1kg, 1개COUPANGshorturl.at우리 집 큰 언니도 탐내는 닭칼국수이맘때 제일 맛있는 김장김치 올려서 정신없이 흡입다음날 점심으로도 좋았던 요리이건 별다른 양념이 필요하지 않고 집에서도 식당 요리처럼 맛 따라 하기도 쉬워서 한번 도전해 보시길 강력 추천함!!',
 '육류 중에 닭을 참 좋아한다닭볶음탕, 닭백숙, 닭한마리, 찜닭.. ㅋㅋ 밖에서 먹을 수 있는 닭요리라면멀리라도 가서 먹는 편이다피자냐 치킨이냐 하면 치킨이고 (당연한거 아닙니까?ㅋㅋ)사실 닭볶음탕 해먹을까 싶어서 사둔 닭인데뜨끈한 국물이 먹고 싶기도 하고탄수화물을 되도록 안먹기 위해 닭한마리로 만들어 보았다~닭한마리는 닭백숙이랑 레시피가 거의 같은데닭을 통으로 삶으면 닭백숙, 잘라진 닭으로 만들면 닭한마리 같으다 ㅋㅋ부재료도 살짝 다르긴 하지만 ㅋㅋ닭한마리 레시피 (2인분 기준)재료: 닭 1kg, 감자 1개, 부추 한 줌, 팽이버섯 1개부재료: 양파 1개, 대파 1개, 마늘 8-10알, 통후추겨자 소스: 닭육수 2T+고춧가루1T+식초1T+간장1T+설탕 약간, 겨자닭은 사온지 이틀을 지나버려서 냉동해뒀었다닭한마리 만들기 전에 미리 실온에 해동했다~해동을 빨리 해야 하면 흐르는 따뜻한 물에 담가둔다닭을 해동시키는 동안 육수 낼 때 넣어줄 야채들을 손질했다대파는 굵게 잘라주고양파는 작은 크기라면 1개, 큰 양파라면 1/2개도 괜찮다마늘은 통마늘로~양파는 4등분 해주었다어차피 오래 끓일거라서 다 분리된다.. ㅋㅋ닭한마리 레시피에서 제일 손이 많이 가는 과정은닭 손질하기이다..뭐 대충 씻어줘도 상관없지만나는 최대한 기름을 떼주었다~이런 부분이라던지~;;윽 이만큼이나 나왔다..껍질도 굳이 필요없는 부분은 뗐다;;닭한마리 만들기 전에 닭을 팔팔 끓는 물에 넣어서 잠깐 샤워시키고 헹궈줘도 되고나는 뜨거운 물로 여러번 헹궈주었다쌀뜰물이라던지,, 우유에 담가둔다던지 하는 과정은 사실 안해도 괜찮다 ; ㅎㅎ닭이 충분히 담길 만큼 물을 붓고 (1.5L) 끓기 시작하면닭과 양파, 대파, 마늘 모두 넣어준다통후추도 이정도 넣었다~이제 팔팔 끓여주면 됩니다 ㅎㅎ닭한마리 레시피 정말 쉽다.. :)좀 센불에 10분 끓여주고중약불로 줄여서 10분 끓였다~중간 중간 거품과 기름을 걷어준다~끓이면서 소금을 살짝 넣고 간을 해주는데살짝 싱거워도 괜찮다. 겨자소스에 찍어먹어도 되니까~닭한마리 레시피에서 부재료는 사실 원하는 걸 넣으면 되는데보통 닭한마리에 많이 들어가는 감자 1개랑 부추, 팽이버섯을 준비했다처음엔 저정도만 먹어야지 하고 잘라뒀는데먹다 보니 야채를 많이 먹게 되어 부추랑 팽이버섯 세번은 더 넣은 것 같다 ㅋㅋㅋㅋ집에서 먹다보니 좋은점이기도 하면서.. 끝도 없이 들어간다는 단점도.. ㅋㅋㅋ닭이 아직 끓는 동안 겨자소스도 만들어준다끓고 있는 닭육수를 3스푼 정도 볼에 담고고춧가루 한 스푼을 먼저 넣고 잘 개어준다풋내가 날아가도록 살짝 둔다~여기에 식초, 간장 1스푼씩 넣고 설탕도 조금 넣어 준다신맛, 단맛은 개인 취향에 따라 가감하면 좋겠다~되직한 정도도 취향껏.. 더 묽은 소스가 좋으면 육수를 더 넣어주면 된다.이렇게 잘 섞어 두고 먹기 전에 연겨자를 살짝 곁들여준다~20분동안 팔팔 끓여준 닭한마리는육수를 내어준 부재료들만 건져내도 되고나는 식탁에서 약불에 끓이며 먹을거라 다른 냄비에 옮겨 담았다~닭고기들을 먼저 담고 육수를 자작하게 부어주었다~여기에 감자, 버섯, 부추를 올려 완성 ^^닭한마리 레시피 정말 간단하다!닭은 바로 먹어도 되고,한참 먹다 보면 감자가 푸욱 익어 건져 먹으면 맛있다 ㅎㅎ간단히 세가지 김치만 준비해서저녁상 차리기~~~만들어둔 소스에 연겨자 조금 풀어서섞어서 먹어준다 ㅎㅎ먹기 전에 요리 조리 찍어본다 >.<익어서 너무 맛있는 시어머니표 총각김치 +_+엄마표 배추김치랑 시어머니표 파김치 ㅎㅎㅎ김치부자다~~~~ ㅎㅎㅎ끓이면서 먹으니 닭고기가 더 부드러워진다가슴살도 안퍽퍽하고 맛나다 +_+소스에 콕 찍어먹으면 입맛이 확 돈다.. ㅋ오빠는 저 소스가 맛있다고 남은 걸 다 먹었는데도 부족해서내 소스까지 줌.. 많이 잡솨요 ㅎㅎㅎ팽이버섯이랑 부추랑도 같이 먹고~피를 맑게 하는 부추~~샐러드로 야채 주면 안좋아하는데이렇게 익혀먹는 야채는 잘먹는 남편이라;다행입니다 ㅎㅎ먹다가 부추, 버섯 계속 리필 ㅋㅋ감자가 잘 익어서 너무 맛있게 먹었다원래 고구마파인데.. ㅋㅋㅋ아..다이어트를 하고자 하는 분은 보시면 안됩니닷..예정에 없던 칼국수 면 등장 ^^얼마전 등촌샤브칼국수 밀키트에 들어있었던 칼국수인데이날 안먹으면 버릴 것 같아서 넣어보았다 ^^ (핑계....ㅋㅋ)육수 넉넉히 넣고 팔팔 끓이자~~닭한마리 먹고 나서는칼국수도 좋고, 수제비도 좋고, 밥도 좋다 ㅋㅋㅋ죽이나 볶음밥으로....의외로 면이 쫄깃하고 맛있어서안먹는다는 오빠한테도 한입만 먹어보라고 ㅋㅋㅋㅋㅋㅋ완전 닭칼국수가 따로 없다..국물도 찐~~하고 너무 너무 맛있음!!몸보신 제대로 한 느낌이다닭한마리 레시피 진짜 별거 없어서 ㅋㅋ자주 해먹고 싶은데. 은근 과식하게 되네 ㅋㅋ탄수화물 안먹으면 좀 낫지 싶다 ^^오빠도 너~~무 맛있다고 ㅎㅎ또 해주께유 ㅋㅋㅋ닭한마리 레시피  즐겁게 보셨다면 좋아요 와 팬 하기 꾹~  눌러주세요 +_+']

 

 

이 과정은 자유롭게 커스터마이징 할 수 있습니다. 가령 10개의 블로그 본문에 대해 모두 스코어를 계산하여 Rerank를 한 번만 진행하거나, 임베딩 모델을 다른 모델로 사용하는 등 다양한 커스텀을 시도하여 최고의 효과를 얻을 수 있는 방법을 찾아보세요.

 

 

2. 네이버 지식인 크롤링

 

네이버 검색 API를 사용하면 검색 키워드를 기반으로 지식인의 [질문명, 링크, 내용 요약]을 전달받을 수 있습니다. 지식인 내용 요약과 질문의 유사도를 기반으로 N개의 지식인 답글을 크롤링합니다. 이후 지식인 답글과 질문의 유사도를 기반으로 K개의 지식인 답글만 프롬프트에 추가합니다. 코드와 함께 차근차근 풀어보도록 하겠습니다.

 

 

2.1 네이버 검색 API 사용

 

이전 파트에서 얻은 client id와 client secret을 사용하여 네이버 검색 API를 사용합니다. 블로그 검색과 거의 동일한 형태로 API를 호출합니다.

 

import urllib
import json

# 클라이언트 ID와 시크릿 키를 스트링 형태로 저장
client_id = "client_id"
client_secret = "Client_secret"

def search_naver_kin(keyword):
    # 검색어를 URL에 포함할 수 있도록 인코딩
    encText = urllib.parse.quote(keyword)
    
    # 네이버 지식인 검색 API의 요청 URL 생성
    url = "https://openapi.naver.com/v1/search/kin?query=" + encText
    
    # API 요청을 위한 Request 객체 생성
    query_response = urllib.request.Request(url)
    
    # API 요청 헤더에 클라이언트 ID와 시크릿 키 추가
    query_response.add_header("X-Naver-Client-Id",client_id)
    query_response.add_header("X-Naver-Client-Secret",client_secret)
    
    # API 요청을 보내고 응답을 받음
    response = urllib.request.urlopen(query_response)
    
    # 응답 코드 확인
    rescode = response.getcode()
    
    if(rescode == 200):  # 응답 코드가 200인 경우 (정상 응답)
        # 응답 본문을 읽어서 JSON 형식으로 변환
        response_body = response.read()
        result = json.loads(response_body.decode('utf-8'))
    else:  # 응답 코드가 200이 아닌 경우 (실패)
        return "fail", "fail", "fail"
        
    # 결과의 링크와 본문 요약을 저장할 리스트 초기화
    link_list = []
    desc_list = []
    
    for item in result.get('items'):
        # 블로그 링크를 리스트에 추가
        link_list.append(item['link'])
        
        # HTML 태그 제거 후 본문 요약을 리스트에 추가
        desc_list.append(item['description'].replace('<b>', '').replace('</b>', ''))
        
    # 전체 결과, 링크 리스트, 본문 요약 리스트 반환
    return result, link_list, desc_list
    
result, link_list, desc_list = search_naver_kin('신촌 세브란스 병원 인근 요양 병원')
link_list, desc_list
(['https://kin.naver.com/qna/detail.naver?d1id=7&dirId=70301&docId=467780914&qb=7Iug7LSMIOyEuOu4jOuegOyKpCDrs5Hsm5Ag7J246re8IOyalOyWkSDrs5Hsm5A=&enc=utf8&section=kin.qna&rank=1&search_sort=0&spq=0',
  'https://kin.naver.com/qna/detail.naver?d1id=7&dirId=70301&docId=467027533&qb=7Iug7LSMIOyEuOu4jOuegOyKpCDrs5Hsm5Ag7J246re8IOyalOyWkSDrs5Hsm5A=&enc=utf8&section=kin.qna&rank=2&search_sort=0&spq=0',
  ... ...
  'https://kin.naver.com/qna/detail.naver?d1id=7&dirId=710&docId=122366395&qb=7Iug7LSMIOyEuOu4jOuegOyKpCDrs5Hsm5Ag7J246re8IOyalOyWkSDrs5Hsm5A=&enc=utf8&section=kin.qna&rank=9&search_sort=0&spq=0',
  'https://kin.naver.com/qna/detail.naver?d1id=7&dirId=710&docId=356316864&qb=7Iug7LSMIOyEuOu4jOuegOyKpCDrs5Hsm5Ag7J246re8IOyalOyWkSDrs5Hsm5A=&enc=utf8&section=kin.qna&rank=10&search_sort=0&spq=0'],
 ['서울 50대 후반/여 신촌암요양병원 안녕하세요. 신촌세브란스 다니고 있어서 근처에 신촌 암요양병원 찾아보고 있습니다. 일단 구토억제제를 먹는데도 부작용이 너무 심해서 따로 구토 나오는거 관리 잘 해주는데면 좋겠구요 밥은 당연히... ',
  '서울 50대 중반/여 유방암요양병원 유방암 요양병원 괜찮은곳 찾고 있습니다. 한쪽 절제수술 했고 지금... *신촌세브란스와 차량으로 약 10분 거리에 위치해있으며, 강북 삼성병원 등 서울 유수의 암센터들과 지리적으로... ',
  ... ...
  '... 어머니의 직장암 치료와 건강 회복을 돕기 위해 암요양원을 찾고 있습니다. 신촌세브란스병원하고 가까웠으 면 합니다. 병원하고 거리가 멀면 치료받으시기에 어려울까봐요ㅠ...혹시나 급히 병원에 가봐야하는데ㅠ 거리가... ',
  '내일 신촌세브란스병원에 아토피 때문에 피부과에 가기로 했는데 진료 준비사항에 요양급여 의뢰서, 검사 결과지, 복용 중인 약 처방전을 가져오라고 하더라고요 꼭 가져가야 되나요? 진료의뢰서 가지고 가셔야 보험적용 받습니다.... '])

 

 

2.2 지식인 요약 기반 Rerank

 

지식인 요약과 링크를 가져왔다면, 이제 10개의 지식인 글 중 질문과 유사도가 높은 4개의 글만 남겨야 합니다. 벡터 기반의 유사도를 계산하기 위해 임베딩 모델을 사용해야 합니다. 이전에 사용한 것과 동일하게 BAAI의 `bge-m3` 모델을 사용하여 임베딩을 진행하겠습니다.

 

# bge-m3

import numpy as np
from FlagEmbedding import BGEM3FlagModel

# BGEM3FlagModel 인스턴스 생성: BAAI/bge-m3 모델 로드, FP16 방식으로 CUDA 디바이스 사용
bge_model = BGEM3FlagModel("BAAI/bge-m3", use_fp16=True, device="cuda")

def bge_top_k(model, question, content_list, link_list, top_k, max_length):
    # question과 content_list 내 각각의 content를 페어로 하는 문장 쌍 생성
    sentence_pairs = [[question, i] for i in content_list]
    
    # 문장 쌍에 대해 모델로 점수 계산, weighted average 방식으로 최종 점수 얻음
    scores = model.compute_score(
        sentence_pairs,
        max_passage_length=max_length,
        weights_for_different_modes=[0.5, 0.1, 0.3]
    )
    
    # content와 해당 점수를 딕셔너리 형태로 저장
    dict_ = [{'content': content, 'score': score} for content, score in zip(content_list, scores['colbert+sparse+dense'])]
    
    # colbert + sparse + dense 점수를 기반으로 상위 top_k 인덱스 추출
    top_k_index = np.argsort(scores['colbert+sparse+dense'])[-top_k:].tolist()
    
    # 상위 top_k에 해당하는 content 및 link 추출
    top_k_desc = np.array(content_list)[top_k_index].tolist()
    top_k_link = np.array(link_list)[top_k_index].tolist()
    
    return dict_, top_k_desc, top_k_link

# 예시 질문
question = '신촌 세브란스 병원 인근의 요양 병원을 소개해줘. 가능하면 주소와 장단점도 같이 소개해줬으면 좋겠어.'

# 질문에 대해 각 지식인 요약의 점수 계산 및 상위 결과 얻기
bge_scores, bge_k_desc, bge_k_link = bge_top_k(bge_model, question, desc_list, link_list, 4, 200)
bge_k_desc
['종로 50대 후반/남 위암 암환자 요양병원 어디가 좋을까요 위암 진단을 받았는데 따로 집에서 돌봐줄 사람이... 위치해있어 신촌 세브란스 등 강북 암센터와 가까운 위치에 자리잡고 있습니다. 보통 대학병원에서 항암을... ',
 '서울 50대 중반/여 유방암요양병원 유방암 요양병원 괜찮은곳 찾고 있습니다. 한쪽 절제수술 했고 지금... *신촌세브란스와 차량으로 약 10분 거리에 위치해있으며, 강북 삼성병원 등 서울 유수의 암센터들과 지리적으로... ',
 '... 강남 세브란스 병원 인근에 위치한 암전문요양병원 몇 곳을 안내드립니다. 아래 병원명을 클릭하시면 상세정보확인과 전화/온라인상담이 가능합니다. 환자분의 상태를 알 수 있는 의사소견서를 구비하신 후 상담을 진행하시면 정확한... ',
 '... 어머니의 직장암 치료와 건강 회복을 돕기 위해 암요양원을 찾고 있습니다. 신촌세브란스병원하고 가까웠으 면 합니다. 병원하고 거리가 멀면 치료받으시기에 어려울까봐요ㅠ...혹시나 급히 병원에 가봐야하는데ㅠ 거리가... ']

 

 

2.3 지식인 답글 전문 크롤링

 

요약본을 통해 4개의 링크를 얻었다면, 이제 4개의 링크에 있는 답글 전문을 크롤링합니다. 블로그와 마찬가지로 링크를 모바일 링크로 변경하여 보다 안정적인 크롤링을 고려합니다.

 

from bs4 import BeautifulSoup
import requests
import re


def search_content(link):
    # 답변 내용을 포함하는 div 태그의 클래스 패턴을 컴파일
    pattern = re.compile(r'answerDetail\s+_hashtagHighlightingContents\s+_param|answerDetail _endContents _endContentsText')
    
    # 해당 링크로 HTTP GET 요청을 보내고, 응답 받음
    response = requests.get(link)
    
    # 응답에 오류가 있는 경우 예외를 발생시킴
    response.raise_for_status()
    
    # BS4를 이용하여 HTML 파싱
    soup = BeautifulSoup(response.content, "html.parser")
    
    # og:title 메타 태그에서 질문 제목을 추출
    title = soup.find('meta',property="og:title")['content']
    
    # 답변 내용을 포함하는 모든 div를 찾아 리스트에 저장
    res = soup.findAll('div', class_=pattern)
    
    # 답변 내용 텍스트를 담기 위한 리스트 초기화
    contents = []
    
    # 찾은 모든 답변 div 태그를 순회하면서 텍스트로 변환
    for item in res:
        # 각 div의 텍스트를 가져와 줄바꿈과 공백을 제거한 후 인코딩과 디코딩 과정 거침
        text = item.get_text(strip=True).encode('euc-kr', 'ignore').decode('euc-kr')
        
        # 클렌징된 텍스트를 리스트에 추가
        contents.append(text)
        
    # 제목과 답변 내용 리스트를 반환
    return title, contents
    
title, contents = search_content(link_list[0])
contents
['강남구청쪽에 모두가행복한연세병원 이라고 있어요.최근에 오픈해서 시설적으로 좋고, 초기라 그런지 식단이 엄청 잘나온다고해요~원장님이 세브란스병원 혈종 전문의시고 엄청 친절하시다고 들었습니다.무엇보다 대학병원급 암 치료 시설기반에 응급치료 시스템까지 갖추고 있다고 하니 참고바랍니다.',
 '안녕하세요~ 건강 정보를 찾아 떠돌다 문의하신 내용에 약간이나마도움을 드리고자 간단히 참고글 남깁니다.자연친화적인 환경(올림픽공원)과 전망, 첨단 의료시설, 쾌적하고 깨끗한 환경,최선의 친자연, 항암식 프리미엄 식단 제공.다른 병원(3분 거리 아산병원, 15분거리 삼성병원 등)에서 항암치료을 받으실 경우통원(셔틀) 서비스 지원, 24시간 간호사 케어도 진행하고 있다고 합니다.포웰의원 암요양센터 에서 꼼꼼히 따져보시고암환자요양병원 선택하시는데 도움이 되었으면 합니다.http://fowell.or.kr/포웰메디컬그룹-암요양센터(포웰의원 암요양센터), (구 포웰의원)포웰의원 암요양센타, 항암/방사선 치료지원, 서울아산병원 7분거리, 편리한 요양시설, 삼성의료원/서울삼성병원 13분, 주요 암 검진, 맛과 영양이 우수한 항암 맞춤식단, 쾌적한 숙면fowell.or.kr',
 '암요양병원 알아보시고 계신가요?힐링미 어플을 통해 쉽고 빠르게 안내받으실 수 있습니다.힐링미 어플을 실행하셔서[상급병원 주변검색] -> [강남 세브란스병원] 또는[지역별 병원찾기] -> [서울] -> [강남구] 를 선택하시면원하시는 조건의 암요양병원을 확인하실 수 있답니다.병원마다 실제 사진도 볼 수 있고입원후기도 확인이 가능하오니힐링미 어플을 통해 편하게 비교해보세요~!힐링미APP암환자 요양병원 비교 검색부터 비대면 무료 입원상담까지 쉽고 빠르게! 상급병원 대학병원 근처 위치 / 내 주변 찾기 / 항암치료 / 방사선치료 / 암전이 / 암재발 / 림프부종 / 여성전용 / 한방특화 / 통증관리 / 암전문 의료진 / 최신시설 / 자연 속 산책로 / 양한방협진medischool.page.link',
 '●●● 강남 세브란스 주변이라면, 포레스트, 메디움, 청담힐 정도가 지역환우 커뮤니티랑 세브란스 환우 커뮤니티에서 자주 추천되는 곳들이오니 참고하시기 바랍니다.환우분의 쾌유를 진심으로 기원합니다.●●●암이 살기 힘든 환경을 조성하여 암세포의 신생혈관 생성과 증식을 차단하고, 종국에는 자살을 택하도록 유도하는 의학적 기전을Apoptosis(아폽토시스) 기전이라고 합니다.독성이 없는 자연계 천연물질로서 해당기전을 뚜렷이 입증하며,5대암 암세포 사멸 성공 논문(간암 2012,폐암,위암 2014, 유방암, 대장암 2015 등)을 포함하여, 의학계 최고권위 국제학술지 SCIE에 5회 연속 논문이 게재되며 세계적으로 화제가 된 항암물질이 있습니다.2018년 노벨상을 수상한 면역항암제의 기전과 더불어 면역항암의 핵심인 암세포의 자살을 유도하는 의학적 기전(Apoptosis)을 뚜렷이 입증한 특허물질이 궁금하시다면, "KHZ Fusion Mycelium(융합균사체)"의 상세소개, 특허 및 인증, 논문자료 등을 열람할 수 있는 균사체 임상의학연구소 사이트 바이오셀리움www.biocelium.com을 참고하시기 바랍니다.(KHZ 융합균사체는 특정 회사명이나 제품명이 아닌, 연구물질의 국제공식명칭입니다.)https://search.naver.com/search.naver?where=nexearch&sm=top_hty&fbm=1&ie=utf8&query=%EB%B0%94%EC%9D%B4%EC%98%A4%EC%85%80%EB%A6%AC%EC%9B%80',
 '안녕하세요. 또하나의가족입니다.강남 세브란스 병원 인근에 위치한 암전문요양병원 몇 곳을 안내드립니다.아래 병원명을 클릭하시면 상세정보확인과 전화/온라인상담이 가능합니다.환자분의 상태를 알 수 있는 의사소견서를 구비하신 후 상담을 진행하시면 정확한 상담이 가능합니다!메디움강남요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 강남구 강남대로 276 (도곡동) | 등급제외, 중소형, 설립 7년, 물리치료, 암치료, 양한방협진 | 02-573-5737ddoga.co.kr포레스트요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 강남구 도산대로 209 | 등급제외, 중소형, 설립 6년, 물리치료, 암치료, 양한방협진 | 0218995868 | 요양원, 요양병원, 주야간보호, 주간보호, 데이케어센터, 방문요양, 방문목욕, 방문간호, 실버타운, 양로원ddoga.co.kr청담힐요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 강남구 영동대로 713 9~14층 (청담동) | 3등급, 중형, 설립 5년, 물리치료, 암치료, 양한방협진 | 025427582ddoga.co.kr포레힐요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 송파구 오금로 173 (방이동) | 2등급, 중소형, 설립 18년, 물리치료, 암치료, 양한방협진 | 0222036100ddoga.co.kr서울힐링요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 송파구 중대로 191 서울힐링요양병원 (가락동) | 5등급, 중형, 설립 4년, 물리치료, 암치료, 양한방협진 | 0218337037 | 요양원, 요양병원, 주야간보호, 주간보호, 데이케어센터, 방문요양, 방문목욕, 방문간호, 실버타운, 양로원ddoga.co.kr네이처요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 강남구 헌릉로569길 21-40 지하3~4층, 지하2층일부, 지하1층일부, 지상1층일부, 지상2~8층 (세곡동) | 등급제외, 중형, 설립 2년, 재활치료, 물리치료, 암치료, 양한방협진 | 025755114ddoga.co.kr서울연세요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 송파구 삼학사로 53 태영빌딩 (삼전동) | 등급제외, 중소형, 설립 3년, 암치료 | 02-573-9573 | 요양원, 요양병원, 주야간보호, 주간보호, 데이케어센터, 방문요양, 방문목욕, 방문간호, 실버타운, 양로원ddoga.co.kr다나움요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 송파구 가락로 278 지하1층~지상6층 (방이동) | 등급제외, 중소형, 설립 2년, 물리치료, 암치료 | 024127272ddoga.co.kr노블케어요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 강남구 언주로 858 1관, 2관동 (신사동) | 2등급, 중형, 설립 5년, 재활치료, 물리치료, 암치료, 양한방협진 | 025150007 | 요양원, 요양병원, 주야간보호, 주간보호, 데이케어센터, 방문요양, 방문목욕, 방문간호, 실버타운, 양로원ddoga.co.kr큰사랑요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 강남구 논현로2길 30 (개포동, 지하2층~1층, 지상1층~6층) | 1등급, 중소형, 설립 6년, 물리치료, 암치료, 양한방협진 | 0234614320ddoga.co.kr관련하여 더 궁금하신점이 있으시다면 또하나의가족 맞춤요양상담을 통해 남겨주세요! 빠르게 답변도와드리겠습니다. :)']

 

 

다만, 블로그와 다르게 지식인은 1개 이상의 답글을 가지고 있습니다. 대부분의 답글은 비슷하지만, 다른 형식 혹은 약간의 다른 내용을 포함하고 있을 가능성이 있습니다. 이에 따라 각 지식인 링크에서 가장 긴 두 개의 답변을 선택하여 사용하기로 했습니다.

 

def content_top_k_by_length(contents, top_k):
    # 각 답변 내용의 길이를 계산하여 리스트로 저장
    lengths = [len(x) for x in contents]
    
    # 답변 내용의 길이를 기준으로 내림차순 정렬한 후, 상위 k개의 인덱스를 추출
    top_k_indexes = np.argsort(lengths)[-top_k:].tolist()
    
    # 상위 k개의 인덱스를 이용하여 답변 내용을 추출
    top_k_contents = np.array(contents)[top_k_indexes].tolist()
    
    # 상위 k개의 답변 내용 리스트를 반환
    return top_k_contents
    
top_k_contents_each_link = content_top_k_by_length(contents, 2)
top_k_contents_each_link
['●●● 강남 세브란스 주변이라면, 포레스트, 메디움, 청담힐 정도가 지역환우 커뮤니티랑 세브란스 환우 커뮤니티에서 자주 추천되는 곳들이오니 참고하시기 바랍니다.환우분의 쾌유를 진심으로 기원합니다.●●●암이 살기 힘든 환경을 조성하여 암세포의 신생혈관 생성과 증식을 차단하고, 종국에는 자살을 택하도록 유도하는 의학적 기전을Apoptosis(아폽토시스) 기전이라고 합니다.독성이 없는 자연계 천연물질로서 해당기전을 뚜렷이 입증하며,5대암 암세포 사멸 성공 논문(간암 2012,폐암,위암 2014, 유방암, 대장암 2015 등)을 포함하여, 의학계 최고권위 국제학술지 SCIE에 5회 연속 논문이 게재되며 세계적으로 화제가 된 항암물질이 있습니다.2018년 노벨상을 수상한 면역항암제의 기전과 더불어 면역항암의 핵심인 암세포의 자살을 유도하는 의학적 기전(Apoptosis)을 뚜렷이 입증한 특허물질이 궁금하시다면, "KHZ Fusion Mycelium(융합균사체)"의 상세소개, 특허 및 인증, 논문자료 등을 열람할 수 있는 균사체 임상의학연구소 사이트 바이오셀리움www.biocelium.com을 참고하시기 바랍니다.(KHZ 융합균사체는 특정 회사명이나 제품명이 아닌, 연구물질의 국제공식명칭입니다.)https://search.naver.com/search.naver?where=nexearch&sm=top_hty&fbm=1&ie=utf8&query=%EB%B0%94%EC%9D%B4%EC%98%A4%EC%85%80%EB%A6%AC%EC%9B%80',
 '안녕하세요. 또하나의가족입니다.강남 세브란스 병원 인근에 위치한 암전문요양병원 몇 곳을 안내드립니다.아래 병원명을 클릭하시면 상세정보확인과 전화/온라인상담이 가능합니다.환자분의 상태를 알 수 있는 의사소견서를 구비하신 후 상담을 진행하시면 정확한 상담이 가능합니다!메디움강남요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 강남구 강남대로 276 (도곡동) | 등급제외, 중소형, 설립 7년, 물리치료, 암치료, 양한방협진 | 02-573-5737ddoga.co.kr포레스트요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 강남구 도산대로 209 | 등급제외, 중소형, 설립 6년, 물리치료, 암치료, 양한방협진 | 0218995868 | 요양원, 요양병원, 주야간보호, 주간보호, 데이케어센터, 방문요양, 방문목욕, 방문간호, 실버타운, 양로원ddoga.co.kr청담힐요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 강남구 영동대로 713 9~14층 (청담동) | 3등급, 중형, 설립 5년, 물리치료, 암치료, 양한방협진 | 025427582ddoga.co.kr포레힐요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 송파구 오금로 173 (방이동) | 2등급, 중소형, 설립 18년, 물리치료, 암치료, 양한방협진 | 0222036100ddoga.co.kr서울힐링요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 송파구 중대로 191 서울힐링요양병원 (가락동) | 5등급, 중형, 설립 4년, 물리치료, 암치료, 양한방협진 | 0218337037 | 요양원, 요양병원, 주야간보호, 주간보호, 데이케어센터, 방문요양, 방문목욕, 방문간호, 실버타운, 양로원ddoga.co.kr네이처요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 강남구 헌릉로569길 21-40 지하3~4층, 지하2층일부, 지하1층일부, 지상1층일부, 지상2~8층 (세곡동) | 등급제외, 중형, 설립 2년, 재활치료, 물리치료, 암치료, 양한방협진 | 025755114ddoga.co.kr서울연세요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 송파구 삼학사로 53 태영빌딩 (삼전동) | 등급제외, 중소형, 설립 3년, 암치료 | 02-573-9573 | 요양원, 요양병원, 주야간보호, 주간보호, 데이케어센터, 방문요양, 방문목욕, 방문간호, 실버타운, 양로원ddoga.co.kr다나움요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 송파구 가락로 278 지하1층~지상6층 (방이동) | 등급제외, 중소형, 설립 2년, 물리치료, 암치료 | 024127272ddoga.co.kr노블케어요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 강남구 언주로 858 1관, 2관동 (신사동) | 2등급, 중형, 설립 5년, 재활치료, 물리치료, 암치료, 양한방협진 | 025150007 | 요양원, 요양병원, 주야간보호, 주간보호, 데이케어센터, 방문요양, 방문목욕, 방문간호, 실버타운, 양로원ddoga.co.kr큰사랑요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 강남구 논현로2길 30 (개포동, 지하2층~1층, 지상1층~6층) | 1등급, 중소형, 설립 6년, 물리치료, 암치료, 양한방협진 | 0234614320ddoga.co.kr관련하여 더 궁금하신점이 있으시다면 또하나의가족 맞춤요양상담을 통해 남겨주세요! 빠르게 답변도와드리겠습니다. :)']

 

 

2.4 지식인 답글 기반 Rerank

 

위 방식대로 지식인 답글을 얻으면, 총 8(지식인 글 4 * 답변 2)개의 답변을 얻을 수 있습니다. 마지막으로 프롬프트에 입력하기 위한 2개의 답변을 Rerank하여 저장하겠습니다. Rerank 방식은 블로그와 동일하게 답변과 질문의 유사도를 기준으로 상위 2개의 선택합니다.

 

import time

def search_n_contents(links, top_k):
    # 상위 k개의 답변 내용을 저장할 리스트 초기화
    top_k_content = []
    
    # 각 링크를 순회하면서
    for link in links:
        try:
            # 링크에서 제목과 답변 내용을 추출
            _, contents = search_content(link)
            
            # 추출된 답변 내용에서 상위 k개의 답변을 가져옴
            contents = content_top_k_by_length(contents, top_k)
            
            # 상위 k개의 답변을 top_k_content 리스트에 추가
            for item in contents:
                top_k_content.append(item)
                
            # 요청 간의 지연을 위해 랜덤한 시간을 대기
            time.sleep(np.random.randint(1, 4))
        except:
            # 예외 발생 시 다음 링크로 넘어감
            continue
            
    # k개의 답변 내용을 반환
    return top_k_content
    
top_k_contents = search_n_contents(bge_k_link, 2)

def content_top_k_by_embedding(model, question, contents, top_k, max_length):
    # 질문과 각 답변 내용을 쌍으로 만드는 리스트 생성
    pairs = [[question, item] for item in contents]
    
    # 모델을 사용하여 각 쌍에 대한 점수 계산
    scores = model.compute_score(pairs, max_passage_length=max_length, weights_for_different_modes=[0.4, 0.2, 0.4])
    
    # 계산된 점수를 기준으로 상위 k개의 인덱스를 추출
    indexes = np.argsort(scores['colbert+sparse+dense'])[-top_k:].tolist()
    
    # 상위 k개의 인덱스를 사용하여 답변 내용을 추출
    top_k_contents = np.array(contents)[indexes].tolist()
    
    # 상위 k개의 답변 내용을 반환
    return top_k_contents
    
final_contents = content_top_k_by_embedding(bge_model, question, top_k_contents, 2, 512)
final_contents
['안녕하세요, 닥톡-네이버 지식iN 상담한의사 김지호입니다.암환자 요양병원을 찾고 계시는군요.주로 아래와 같은 상황이신 분들께서 입원하실 만한 곳을 찾는 경우가 많으십니다.[암 진단을 받고, 입원을 하면 좋은 점이 무엇인가요?]-항암, 방사선 등으로 인한 후유증을 개선해드립니다.-매 끼 항암 맞춤 식단을 제공해드립니다.-지방에서 살고 계신 환우분들께서는 서울에 계시면 편리합니다-갑작스러운 응급 상황에 빠른 대처가 필요합니다-의료진 일 2회 회진을 통해 상시 몸상태 체크가 가능합니다메디람은 홍대입구역 근처에 위치해있어 신촌 세브란스 등 강북 암센터와 가까운 위치에 자리잡고 있습니다.보통 대학병원에서 항암을 진행하신 후에 기력이 많이 떨어지게 되고 구토 등에 시달리시게 되기 때문에 지리적으로 가까운 곳에서 입원 치료를 받으시면 통원을 하실 때에 힘든 점을 줄일 수 있다는 장점이 있습니다.[암환자 요양병원, 중요하게 봐야 할 것은?]암환자 요양병원을 고르실때 중요한 것이 후유증 관리, 면역 케어, 그리고 식단이라고 크게 나누어 말씀을 드릴 수 있습니다.악성 종양 치유 과정 중 나타나는 부작용을 그대로 방치할 경우 환우분 본인께서 큰 고통을 겪으실 뿐만 아니라 면역력이 지속적으로 떨어져 더 이상 대학병원의 과정을 지속하지 못하게 되는 심각한 경우에 이를 수도 있어서, 이에 대한 적극적인 관리와 개선이 필요합니다.[메디람의 후유증 관리][구역, 구토 개선]- Cannabis/Cannabinoids.- Dexamethasone Sparing Regimen.- Netupitant-Palonosetron Combination(NEPA).- Neurokinin-1 Receptor Antagonist(NK1).- Olanzapine.- Serotonin 5HT3 Receptor Antagonists(5HT3).- Sustained Release Granisetron.- Transdermal Granisetron.- Benzodiazepine for Anticipatory Chemotherapy-Induced Nausea and Vomiting또는 황련해독탕, 반하사심탕 등 천연 한약재 처방을 내려드리고 있습니다.[메디람의 면역 관리]-싸이모신알파1주사싸이모신알파1은 흉선에 존재하는 면역 호르몬이며, 암 세포를 사멸시킬 수 있는 T세포를 훈련하는 역할을 합니다.싸이모신알파1을 주사제로 투여하면 장기적으로 생존율이 증가하는 연구 결과가 있습니다.-미슬토주사식약처 인증을 받은 보조면역치료제로, 렉틴과 비스코톡신 성분이 주를 이루고 있습니다.이 성분들은 악성 종양을 사멸시키는 자연살해세포(NK세포)를 활성화시키고 변이된 세포 벽을 허물어뜨릴 수 있습니다.미슬토주사는 고용량에서는 항암 효과, 저용량에서는 면역효과를 나타냅니다.이외에도 고주파온열치료, 고압산소 치료 등 기존 암치료의 효과를 높일 수 있는 여러가지 방법이 존재합니다.[메디람 식사원칙]-친환경 채소를 위주로 한 유기농 식재료-국산콩, 국산 간장-천연조미료-현미유, 들기름 사용(콩, 옥수수기름 등 유전자변형 작물 유래 기름 미사용)-수제 착즙 주스-목쵸 사료를 먹인 청정 쇠고기 , 무상생제 국산 돼지고기지금까지 암환자 요양병원을 찾으실 때 어떤 면을 보시면 좋은지, 그리고 메디람에서는 어떤 것들을 해드릴 수 있는지 말씀드렸습니다.더 궁금한점이 있으시면 언제든 문의주세요. 감사합니다.',
 '안녕하세요. 또하나의가족입니다.강남 세브란스 병원 인근에 위치한 암전문요양병원 몇 곳을 안내드립니다.아래 병원명을 클릭하시면 상세정보확인과 전화/온라인상담이 가능합니다.환자분의 상태를 알 수 있는 의사소견서를 구비하신 후 상담을 진행하시면 정확한 상담이 가능합니다!메디움강남요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 강남구 강남대로 276 (도곡동) | 등급제외, 중소형, 설립 7년, 물리치료, 암치료, 양한방협진 | 02-573-5737ddoga.co.kr포레스트요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 강남구 도산대로 209 | 등급제외, 중소형, 설립 6년, 물리치료, 암치료, 양한방협진 | 0218995868 | 요양원, 요양병원, 주야간보호, 주간보호, 데이케어센터, 방문요양, 방문목욕, 방문간호, 실버타운, 양로원ddoga.co.kr청담힐요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 강남구 영동대로 713 9~14층 (청담동) | 3등급, 중형, 설립 5년, 물리치료, 암치료, 양한방협진 | 025427582ddoga.co.kr포레힐요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 송파구 오금로 173 (방이동) | 2등급, 중소형, 설립 18년, 물리치료, 암치료, 양한방협진 | 0222036100ddoga.co.kr서울힐링요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 송파구 중대로 191 서울힐링요양병원 (가락동) | 5등급, 중형, 설립 4년, 물리치료, 암치료, 양한방협진 | 0218337037 | 요양원, 요양병원, 주야간보호, 주간보호, 데이케어센터, 방문요양, 방문목욕, 방문간호, 실버타운, 양로원ddoga.co.kr네이처요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 강남구 헌릉로569길 21-40 지하3~4층, 지하2층일부, 지하1층일부, 지상1층일부, 지상2~8층 (세곡동) | 등급제외, 중형, 설립 2년, 재활치료, 물리치료, 암치료, 양한방협진 | 025755114ddoga.co.kr서울연세요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 송파구 삼학사로 53 태영빌딩 (삼전동) | 등급제외, 중소형, 설립 3년, 암치료 | 02-573-9573 | 요양원, 요양병원, 주야간보호, 주간보호, 데이케어센터, 방문요양, 방문목욕, 방문간호, 실버타운, 양로원ddoga.co.kr다나움요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 송파구 가락로 278 지하1층~지상6층 (방이동) | 등급제외, 중소형, 설립 2년, 물리치료, 암치료 | 024127272ddoga.co.kr노블케어요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 강남구 언주로 858 1관, 2관동 (신사동) | 2등급, 중형, 설립 5년, 재활치료, 물리치료, 암치료, 양한방협진 | 025150007 | 요양원, 요양병원, 주야간보호, 주간보호, 데이케어센터, 방문요양, 방문목욕, 방문간호, 실버타운, 양로원ddoga.co.kr큰사랑요양병원 | 또하나의가족, 또가요양병원 | 서울특별시 강남구 논현로2길 30 (개포동, 지하2층~1층, 지상1층~6층) | 1등급, 중소형, 설립 6년, 물리치료, 암치료, 양한방협진 | 0234614320ddoga.co.kr관련하여 더 궁금하신점이 있으시다면 또하나의가족 맞춤요양상담을 통해 남겨주세요! 빠르게 답변도와드리겠습니다. :)']

 

 

이렇게 네이버 블로그 / 지식인 크롤링이 마무리되었습니다. 베이스라인을 커스터마이징하여 더 나은 성능의 크롤링 방법을 구현해 보세요.

'NLP' 카테고리의 다른 글

LLM with Naver API - OpenAI, Cohere API 확인 & Gradio 서버  (0) 2024.05.20
LLM with Naver API  (0) 2024.05.16
LLM with RAG - Gradio server  (0) 2024.05.09
LLM with RAG - LLM server  (0) 2024.05.08
LLM with RAG - milvus server  (0) 2024.05.07

관련글 더보기

댓글 영역