[빅데이터 python] 웹 검색 엔진 만들기 – 7. 검색기(TF-IDF)

안녕하세요. 언제나휴일입니다.

앞에서 웹 수집 로봇 및 형태소 분석, 역파일 생성에 관하여 구현하였습니다.

이번에는 검색 요청한 내용을 포함하는 웹 페이지를 TF-IDF값으로 랭킹화하여 결과를 반환하는 검색기를 구현합시다.

(1~4에서 작성한 것을 이용합니다.)

1. ScoredWebPage 만들기

TF-IDF 방식으로 검색한 웹 페이지에 점수를 부여하여 반환할 거예요.

1.1 생성자

이를 위해 웹 페이지와 점수를 멤버로 갖는 ScroredWebPage 클래스를 정의합니다.

#ScoredWebPage.py
class ScoredWebPage:
    def __init__(self,wpage,score):
        self.wpage = wpage
        self.score = score

1.2 __lt__

ScoredWebPage를 원소로 하는 컬렉션을 정렬할 수 있게 __lt__ 메서드를 제공합시다.

    def __lt__(self,other):
        return self.score>other.score

1.3 전체 코드

#ScoredWebPage.py
class ScoredWebPage:
    def __init__(self,wpage,score):
        self.wpage = wpage
        self.score = score
    def __lt__(self,other):
        return self.score>other.score

2. WebPageSearcher 클래스 만들기

검색 질의를 입력받아 포함하는 웹 페이지를 검색하여 반환하는 클래스를 정의합시다.

2.1 사용할 라이브러리

앞에서 작성한 Database와 연동하기 위한 WebPageSql, MorphemeSql, InverseSql을 사용합니다.

질의 내용을 분석할 MorphemeParser도 사용합니다.

MSSQL 한글을 python 한글로 변환하기 위해 EHHelper를 사용합니다.

순위화한 웹 페이지를 정의한 ScoredWebPage를 사용합니다.

웹 페이지를 정의한 WebPage를 사용합니다.

log 계산을 위해 math를 사용합니다.

#WebPageSearcher.py
from WebPageSql import WebPageSql
from MorphemeSql import MorphemeSql
from InverseSql import InverseSql
from MorphemeParser import MorphemeParser
from EHHelper import EHHelper
from ScoredWebPage import ScoredWebPage
from WebPage import WebPage
import math

2.2 Search – 검색 메서드

검색 질의를 입력받아 포함하는 웹 페이지를 조사하여 반환합니다.

포함하는 웹 페이지들에 TF-IDF방식으로 점수를 부여하고 점수 순으로 정렬한 결과를 반환합니다.

class WebPageSearcher:
    @staticmethod
    def Search(query):

반환할 빈 컬렉션을 생성합니다.

        sws=[]

IDF값을 계산하기 위해 수집한 전체 문서 개수를 구합니다.

IDF = log(tdf/df)   , df:포함 문서 수, tdf:수집한 전체 문서 수

        tdcnt = WebPageSql.TotalDocumentCount()

쿼리를 분석하여 쿼리에 포함한 형태소 목록을 구합니다.

        moes = MorphemeParser.Parse(query)

형태소 목록에 포함하는 각 단어에 대하여 작업을 반복합니다.

        for mo in moes:

단어의 형태소 일련 번호(mid)를 얻어오고 역파일 요소 목록을 얻어옵니다.

            mid = MorphemeSql.FindMid(mo.word)
            ins = InverseSql.FindInv(mid)

idf값을 계산합니다. 포함 문서 수는 역파일 요소 개수(len(ins)입니다.

여기에서는 포함 문서 수가 0일 때 divide-zero 에러를 회피하기 위해 최소 1로 조절하였습니다.

여기에서는 idf값이 0이 나오지 않게 최소 0.1로 조절하였습니다.

            oidf = tdcnt/(max(len(ins),1)) #분모가 0이 나오지 않게 조절
            idf = max(math.log(oidf),0.1) #idf값이 0이 나오지 않게 조절

역파일 요소 목록의 각 항목에 대하여 작업을 반복합니다.

            for ri in range(0,len(ins)):

웹 페이지 일련번호로 웹 페이지 정보를 구합니다.

                wid,rcnt = ins[ri]
                title,url,description,mcnt = WebPageSql.FindPageByWid(wid)
                title = EHHelper.MssqlstrToStrKor(title)
                description = EHHelper.MssqlstrToStrKor(description)

TF값을 계산합니다.

TF = rcnt/mcnt   , rcnt:참조 개수, mcnt: 전체 개수

                tf = rcnt/mcnt
                score = tf*idf

TF-IDF값을 구하여 점수를 부여한 ScoredWebPage 개체를 생성합니다.

생성한 ScoredWebPage 개체를 컬렉션에 추가합니다.

                sw = ScoredWebPage(WebPage(url,title,description,[]),score)
                sws.append(sw)

모든 for문을 수행한 후에 같은 url인 것들을 하나로 합친 후에 반환합니다.

        return WebPageSearcher.MergeDupSns(sws)

2.3 전체 코드

#WebPageSearcher.py
from WebPageSql import WebPageSql
from MorphemeSql import MorphemeSql
from InverseSql import InverseSql
from MorphemeParser import MorphemeParser
from EHHelper import EHHelper
from ScoredWebPage import ScoredWebPage
from WebPage import WebPage
import math
class WebPageSearcher:
    @staticmethod
    def Search(query):
        sws=[]
        tdcnt = WebPageSql.TotalDocumentCount()
        moes = MorphemeParser.Parse(query)
        for mo in moes:
            mid = MorphemeSql.FindMid(mo.word)
            ins = InverseSql.FindInv(mid)
            oidf = tdcnt/(max(len(ins),1)) #분모가 0이 나오지 않게 조절
            idf = max(math.log(oidf),0.1) #idf값이 0이 나오지 않게 조절
            for ri in range(0,len(ins)):
                wid,rcnt = ins[ri]
                title,url,description,mcnt = WebPageSql.FindPageByWid(wid)
                title = EHHelper.MssqlstrToStrKor(title)
                description = EHHelper.MssqlstrToStrKor(description)
                tf = rcnt/mcnt
                score = tf*idf
                sw = ScoredWebPage(WebPage(url,title,description,[]),score)
                sws.append(sw)
        return WebPageSearcher.MergeDupSns(sws)
    @staticmethod
    def MergeDupSns(sws):
        res = list()
        for sw in sws:
            flag = False
            for i in range(0,len(res)):
                rsw = res[i]
                if(rsw.wpage.url== sw.wpage.url):
                    rsw.score += sw.score
                    flag = True
                    break
            if flag == False:
                res.append(sw)
        res = sorted(res)
        return res

3. 진입점 코드

검색 질의를 입력받아 검색을 요청합니다.

그리고 검색 결과를 콘솔 화면에 출력합니다.

from WebPageSearcher import WebPageSearcher 
query = input("검색:") 
sns = WebPageSearcher.Search(query) 
for sn in sns: 
    print("{0}:{1}".format(sn.wpage.title, sn.score))