프로그래밍 기술/웹 검색 엔진 만들기

11. 2 웹 검색 엔진 관리자 응용 만들기

언제나휴일 2017. 12. 7. 14:33
반응형

11. 2 웹 검색 엔진 관리자 응용 만들기


 

 이번에는 관리자 응용을 만듭시다. 관리자 응용은 Windows Form 형태의 응용 프로그램으로 WSEManager 이름으로 만듭시다.

 

 관리자 응용에서 제공할 기능은 다음과 같습니다.

 

 검색 서비스가 잘 동작하는지 확인할 수 있다.

 

 웹 로봇을 설정할 수 있다.

 

 수집 결과에 관한 상태 모니터링을 할 수 있다.

 

 사이트로 탐색할 수 있다.

 

 관리자 응용은 프로핑 단계에서 작성한 WSEManager 응용 프로그램 프로토 타이핑과 기본적인 구조는 유사합니다. 여기에서는 프로토 타이핑에서 구현하지 않았던 세부 기능을 구현할 것입니다.

 

11.2.1 폼 디자인

 

 관리자 응용은 MainFormEHBrowser를 만들어서 사용할게요.

 

 MainForm은 관리자가 검색 서비스 동작 확인 및 웹 로봇 설정, 현재 수집 결과 모니터링을 수행하는 폼이고 수집 결과 페이지 주소로 바로가기를 했을 때 EHBrowser를 통해 사이트 정보를 보여주게 합시다.

 

[그림 11.5] MainForm의 컨트롤 배치

[그림 11.5] MainForm의 컨트롤 배치

 

번호

컨트롤 이름

컨트롤 유형

설명

1

btn_start

Button

웹 로봇 수집 가동

2

btn_stop

Button

웹 로봇 수집 멈춤

3

tc_main

TabControl

탭 컨트롤

4

tp_search

TabPage

검색 페이지

5

tp_set_robot

TabPage

웹 로봇 설정 페이지

6

tp_monitoring

TabPage

모니터링 페이지

7

lb_query

Label

단순 정보 표시

8

tbox_query

TextBox

검색 질의 입력 창

9

btn_search

Button

검색 요청 버튼

10

ruc_result

RUListControl

검색 결과 컬렉션

[ 11.4] WSE Manager 메인 폼과 검색 탭 페이지의 자식 컨트롤

 

[그림 11.6] MainForm의 웹 로봇 설정 탭의 컨트롤 배치

[그림 11.6] MainForm의 웹 로봇 설정 탭의 컨트롤 배치 

 

번호

컨트롤 이름

컨트롤 유형

설명

1

lb_period

Label

단순 정보 표시

2

nud_period

NumericUpDown

수집 주기 선택

3

btn_set_period

Button

수집 주기 설정

4

lb_seed_site

Label

단순 정보 표시

5

tbox_seed_site

TextBox

Seed 사이트 입력 창

6

btn_add_seed

Button

Seed 사이트 추가

7

btn_refresh_candi

Button

수집 대상 목록 고침

8

btn_refresh_purl

Button

수집 완료 목록 고침

9

lv_candidate

ListView

수집 대상 리스트 뷰

10

ch_addr

ColumnHeader

컬럼 헤더

11

ch_depth

ColumnHeader

컬럼 헤더

12

lv_posted

ListView

수집 완료 리스트 뷰

13

ch_posted_addr

ColumnHeader

컬럼 헤더

14

ch_origin_addr

ColumnHeader

컬럼 헤더

15

ch_posted_depth

ColumnHeader

컬럼 헤더

[ 11.5] WSE Manager 설정 탭 페이지의 자식 컨트롤

 

[그림 11.7] MainForm의 모니터링 탭의 컨트롤 배치

[그림 11.7] MainForm의 모니터링 탭의 컨트롤 배치

 

번호

컨트롤 이름

컨트롤 유형

설명

1

lb_posted_page

Label

단순 정보 표시

2

lbox_posted_page

ListBox

수집 완료 목록

3

btn_refresh_moph

Button

형태소 목록 고침

4

lbox_morpheme

ListBox

형태소 목록

5

lb_page_info

Label

단순 정보 표시

6

ruc_selected

RankedUrlControl

사이트 상세 정보

7

lb_morpheme_info

Label

단순 정보 표시

8

lview_morpheme

ListView

형태소 상세 정보

9

ch_posted_url

ColumnHeader

컴럼 헤더

10

ch_refcnt

ColumnHeader

컬럼 헤더

11

ch_totalcnt

ColumnHeader

컬럼 헤더

[ 11.6] WSE Manager 모니터링 탭 페이지의 자식 컨트롤

 Windows Form 추가 메뉴를 통해 EHBrowser 폼을 추가합시다.

 

 EHBrowser 폼에는 자식 컨트롤로 페이지 제목과 주소 및 페이지 내용을 보여주는 자식 컨트롤을 배치합니다.

 

[그림 11.8] EHBrowser폼 자식 컨트롤 배치

[그림 11.8] EHBrowser폼 자식 컨트롤 배치

 

번호

컨트롤 이름

컨트롤 유형

설명

1

lb_title_info

Label

정보 표시

2

lb_title

Label

타이틀

3

lb_addr_info

Label

정보 표시

4

lb_addr

Label

사이트 주소

5

wb

WebBrowser

페이지 정보 표시

[ 11.7] RankedUrlControl의 자식 컨트롤

 

11.2.2 구현

 

 WSE Manager 응용은 검색 서비스와 웹 로봇 서비스를 이용합니다. 두 가지 서비스 모두 .NET 리모팅 서비스를 사용하므로 System.Runtime.Remoting 어셈블리를 참조 추가합니다.

 

 그리고 검색 서비스에서 제공하는 MashalByReference 개체를 참조하기 위해 서버와 클라이언트 모두 참조해야 하는 GenericSearchLib 어셈블리를 추가합니다.

 

 웹 로봇 서비스에서 제공하는 개체를 참조하기 위해 GenericWebRobotLib 어셈블리를 참조 추가합니다.

 

 그리고 RankedUrlControlLib 어셈블리와 DBMForAllLib, DBMForSearchLib, WSECore 어셈블리도 참조 추가합니다.

 

 MainForm Load 이벤트 핸들러를 추가하세요.

private void MainForm_Load(object sender, EventArgs e)

 

 이벤트 핸들러에서는 .NET 리모팅 서비스를 사용하기 위해 HttpChannel 개체를 생성하여 등록합니다.

HttpChannel hc = new HttpChannel();

ChannelServices.RegisterChannel(hc, false);

 

 웹 로봇의 활성화 상태에 따라 웹 로봇 수집 시작과 멈춤 버튼의 활성화 속성을 설정합니다. 웹 로봇의 MashalByReference 개체를 참조하기 위한 속성은 별도로 만들어서 사용합시다.

bool enable = WebRobot.GetEnabled();

SetStartStopButton(enable);

 

 웹 로봇 서비스의 GenericWebRobot 개체와 검색 서비스의 GenericSearch 개체를 참조하기 위한 속성을 제공합니다.

GenericWebRobot WebRobot

{

    get

    {

        GenericWebRobot robot = Activator.GetObject(

           typeof(GenericWebRobot),

           "http://[웹 로봇 서비스 서버의 IP 주소]:10400/WebRobotSVC")

           as GenericWebRobot;

        return robot;

    }

}

 

GenericSearch Searcher

{

    get

    {

        GenericSearch searcher = Activator.GetObject(

           typeof(GenericSearch),

           "http://[검색 서비스 서버의 IP 주소]:10200/EHSearchSVC"as GenericSearch;

        return searcher;

    }

}

 

 웹 로봇 수집 멈춤과 시작 버튼 활성화를 설정하는 메서드를 작성하세요.

private void SetStartStopButton(bool enable)

{

    btn_start.Enabled = !enable;

    btn_stop.Enabled = enable;

}

 

 

 검색 버튼 클릭 이벤트 핸들러를 추가하세요.

private void btn_search_Click(object sender, EventArgs e)

 

 검색 결과를 보여주는 ruc_result Clear 메서드를 호출하여 결과를 초기화합니다.

ruc_result.Clear();

 

 쿼리 입력 창의 Text 속성을 입력 인자로 Searcher Request 메서드를 호출하여 결과 목록을 얻어와서 ruc_result에 추가합니다.

string query = tbox_query.Text;

ArrayList list = Searcher.Request(query);

foreach (RankedUrl rurl in list)

{

    ruc_result.AddRankedUrl(rurl);

}

 

 검색 결과를 보여주는 ruc_reuslt 컨트롤의 RUClick 이벤트 핸들러를 추가합시다.

private void ruc_result_RUClick(object sender, RUrlClickEventArgs e)

 

 이벤트 핸들러에서는 이벤트 처리 인자의 RankedUrl 개체를 입력 인자로 EHBrowser 폼을 생성하여 활성화합니다. EHBrowser 부분은 EH 응용 만들기에서 작성한 것과 일치합니다.

if (e.RUrl != null)

{

    RankedUrl rurl = e.RUrl;

    EHBrowser eb = new EHBrowser(rurl);

    eb.Show();

}

 

 웹 로봇 수집 시작 버튼과 멈춤 버튼 클릭 이벤트 핸들러를 추가하여 웹 로봇의 수집 여부를 설정합니다.

private void btn_start_Click(object sender, EventArgs e)

{

    WebRobot.SetEnabled(true);

    SetStartStopButton(true);

}

 

private void btn_stop_Click(object sender, EventArgs e)

{

    WebRobot.SetEnabled(false);

    SetStartStopButton(false);

}

 

 수집 주기 설정 버튼 클릭 이벤트 핸들러와 Seed 사이트 추가 버튼 클릭 이벤트 핸들러를 추가하여 웹 로봇의 설정 주기 및 Seed 사이트 추가 메서드를 호출합니다.

private void btn_set_period_Click(object sender, EventArgs e)

{

    WebRobot.SetPeriod((int)nud_period.Value);

}

 

private void btn_add_seed_Click(object sender, EventArgs e)

{

    WebRobot.SetSeedSite(tbox_seed_site.Text);

} 

 

 수집 대상 목록 새로고침 버튼 클릭 이벤트 핸들러를 추가하세요.

private void btn_refresh_candi_Click(object sender, EventArgs e)

 

 수집 대상 목록 ListView의 항목을 지워줍니다.

lv_candidate.Items.Clear();

 

 EHDbmForAll 정적 클래스를 이용하여 수집 대상 목록을 얻어옵니다.

List<Candidate> list = EHDbmForAll.GetCandidates();

 

 얻어온 목록으로 ListViewItem 개체를 생성하여 수집 대상 목록 ListView에 추가합니다.

foreach (Candidate candi in list)

{

        ListViewItem lvi = new ListViewItem(

            new string[] { candi.Url, candi.Depth.ToString() });

        lv_candidate.Items.Add(lvi);

}

 

 수집 완료 사이트 목록 새로고침 버튼 클릭 이벤트 핸들러를 추가하세요.

private void btn_refresh_purl_Click(object sender, EventArgs e)

 

 수집 완료 사이트 목록을 보여주는 ListView의 항목을 지워줍니다.

lv_posted.Items.Clear();

 

 EHDbmForAll 정적 클래스를 이용하여 수집 완료 사이트 목록을 얻어옵니다.

List<PostedUrl> list = EHDbmForAll.GetPostedUrls();

 

 수집 완료 사이트 목록의 각 PostedUrl 개체로 ListViewItem 개체를 생성하여 수집 완료 사이트 목록에 추가합니다.

foreach (PostedUrl purl in list)

{

        ListViewItem lvi = new ListViewItem(

            new string[] { purl.Url, purl.OriginUrl, purl.Depth.ToString() });

        lv_posted.Items.Add(lvi);

}

 

 

 모니터링 탭 페이지의 수집 목록 새로고침 버튼의 클릭 이벤트 핸들러를 추가합니다.

private void bnt_refresh_purl2_Click(object sender, EventArgs e)

 

 수집 목록을 보여주는 ListBox 컨트롤의 항목을 지워줍니다.

lbox_posted_page.Items.Clear();

 

 수집 목록을 얻어오는 부분은 DbmForAllLib를 이용해야 할 것 같은데 아직 제공하지 않는 기능입니다. EHDbmForAll 정적 클래스에 GetPostedUrls 메서드 이름으로 수집한 페이지 목록을 제공하는 메서드를 추가하여 이를 이용합시다.

List<PostedUrl> list = EHDbmForAll.GetPostedUrls();

 

 얻어온 수집 목록을 리스트 박스 목록에 추가합니다.

foreach (PostedUrl purl in list)

{

    lbox_posted_page.Items.Add(purl);

}

 

 잠시 DbmForAllLib로 가서 EHDbmForAll 클래스에 GetPostedUrls 메서드를 작성합시다.

public static List<PostedUrl> GetPostedUrls()

 

 결과 목록을 보관할 컬렉션을 생성합니다.

List<PostedUrl> list = new List<PostedUrl>();

 

 PostedUrlTable의 모든 항목을 얻어올 SqlCommand 개체를 만듭니다.

SqlCommand scom = MakeSPCommand("Select  * From PostedUrlTable"

           , CommandType.Text);

 

 SqlCommand 개체의 연결을 개방한 후 명령을 실행합니다.

scom.Connection.Open();

SqlDataReader sdr = scom.ExecuteReader();

 

 명령으로 얻어온 SqlDataReader 개체의 결과로 PostedUrl 개체를 만들어서 결과를 보관할  컬렉션에 추가하고 SqlDataReader 개체를 닫습니다.

while (sdr.Read())

{

    PostedUrl purl = new PostedUrl();

    purl.Url = sdr["Url"].ToString();

    purl.OriginUrl = sdr["OriginUrl"].ToString();

    purl.Depth = (int)sdr["Depth"];

    purl.Title = sdr["Title"].ToString();

    purl.PostedTime = (DateTime)sdr["PostedTime"];

    purl.Content = sdr["PostedContent"].ToString();

    list.Add(purl);

}

sdr.Close();

 

 SqlCommand 개체의 연결을 닫고 결과 목록을 반환합니다.

scom.Connection.Close();

return list;

 

EHDbmForAll.cs에 추가한 내용(DbmForAllLib)

        /// <summary>

        /// 수집한 사이트 목록 가져오기 메서드

        /// </summary>

        /// <returns>수집한 사이트 목록</returns>

        public static List<PostedUrl> GetPostedUrls()

        {

            List<PostedUrl> list = new List<PostedUrl>();

            SqlCommand scom = MakeSPCommand("Select  * From PostedUrlTable"

               , CommandType.Text);

 

            scom.Connection.Open();

            SqlDataReader sdr = scom.ExecuteReader();

            while (sdr.Read())

            {

                PostedUrl purl = new PostedUrl();

                purl.Url = sdr["Url"].ToString();

                purl.OriginUrl = sdr["OriginUrl"].ToString();

                purl.Depth = (int)sdr["Depth"];

                purl.Title = sdr["Title"].ToString();

                purl.PostedTime = (DateTime)sdr["PostedTime"];

                purl.Content = sdr["PostedContent"].ToString();

                list.Add(purl);

            }

            sdr.Close();

            scom.Connection.Close();

            return list;

        }

 

  

 수집 목록 선택 변경 이벤트 핸들러를 추가하세요.

private void lbox_posted_page_SelectedIndexChanged(object sender, EventArgs e)

 

 선택 항목이 없으면 핸들러를 종료합니다.

if (lbox_posted_page.SelectedIndex == -1)

{

    return;

}

 

 선택 항목을 PostedUrl 개체 형식으로 참조하여 결과를 보여주는 컬렉션에 추가합니다.

PostedUrl purl = lbox_posted_page.SelectedItem as PostedUrl;

ruc_selected.SetRankedUrl(new RankedUrl(purl, 0));

 

 형태소 목록 새로고침 버튼의 클릭 이벤트 핸들러를 추가하세요.

private void btn_refresh_moph_Click(object sender, EventArgs e)

 

 EHDbmForAll 정적 클래스를 이용하여 형태소 목록을 얻어와서 ListBox DataSource 속성을 설정합니다.

List<string> list = EHDbmForAll.GetMorphemes();

lbox_morpheme.DataSource = list;

 

 형태소 목록 선택 변경 이벤트 핸들러를 추가하세요.

private void lbox_morpheme_SelectedIndexChanged(object sender, EventArgs e)

 

 형태소 목록을 보여주는 ListView 컨트롤의 항목을 지워줍니다.

lview_morpheme.Items.Clear();

 

 만약 선택 항목이 없으면 이벤트 핸들러를 종료합니다.

if (lbox_morpheme.SelectedIndex == -1)

{

    return;

}

 

 선택 항목의 이름을 참조합니다.

string mname = lbox_morpheme.SelectedItem as string;

 

 EHDbmForSearch 정정 클래스를 이용하여 역 파일 목록을 얻어옵니다.

List<InvertedElem> list = EHDbmForSearch.GetInvertedFile(mname);

 

 얻어온 정보의 페이지 주소로 형태소 전체 개수를 얻어와서 ListViewItem 개체를 생성하여 ListView 컨트롤의 목록에 추가합니다.

foreach (InvertedElem ie in list)

{

    int tcnt = EHDbmForSearch.GetTotalCountInUrl(ie.Url);

    ListViewItem lvi = new ListViewItem(

        new string[] { ie.Url,ie.RefCount.ToString(),tcnt.ToString() });

    lview_morpheme.Items.Add(lvi);

}

 

MainForm.cs

using System;

using System.Windows.Forms;

using GenericWebRobotLib;

using GenericSearchLib;

using System.Runtime.Remoting.Channels.Http;

using System.Runtime.Remoting.Channels;

using System.Collections;

using WSE_Core;

using DBM_ForAll;

using System.Collections.Generic;

using DBMForSearchLib;

using RankedUrlControlLib;

 

namespace WSEManager

{

    public partial class MainForm : Form

    {

        GenericWebRobot WebRobot

        {

            get

            {

                GenericWebRobot robot = Activator.GetObject(

                   typeof(GenericWebRobot),

                   "http://[웹 수집 로봇 서버의 IP 주소]:10400/WebRobotSVC")

                   as GenericWebRobot;

                return robot;

            }

        } 

 

        GenericSearch Searcher

        {

            get

            {

                GenericSearch searcher = Activator.GetObject(

                   typeof(GenericSearch),

                   "http://[검색 서비스 서버의 IP 주소]:10200/EHSearchSVC")

                    as GenericSearch;

                return searcher;

            }

        }

 

        public MainForm()

        {

            InitializeComponent();

        }

        private void MainForm_Load(object sender, EventArgs e)

        {

            HttpChannel hc = new HttpChannel();

            ChannelServices.RegisterChannel(hc, false);

            bool enable = WebRobot.GetEnabled();

            SetStartStopButton(enable);

        }

 

        private void SetStartStopButton(bool enable)

        {

            btn_start.Enabled = !enable;

            btn_stop.Enabled = enable;

        }

 

 

        private void btn_search_Click(object sender, EventArgs e)

        {

            ruc_result.Clear();

 

            string query = tbox_query.Text;

            ArrayList list = Searcher.Request(query);

            foreach (RankedUrl rurl in list)

            {

                ruc_result.AddRankedUrl(rurl);         

                

            }

        }

 

        private void ruc_result_RUClick(object sender, RUrlClickEventArgs e)

        {

            if (e.RUrl != null)

            {

                RankedUrl rurl = e.RUrl;

                EHBrowser eb = new EHBrowser(rurl);

                eb.Show();

            }

        }

 

        private void btn_start_Click(object sender, EventArgs e)

        {

            WebRobot.SetEnabled(true);

            SetStartStopButton(true);

        } 

 

 

        private void btn_stop_Click(object sender, EventArgs e)

        {

            WebRobot.SetEnabled(false);

            SetStartStopButton(false);

        }

 

        private void btn_set_period_Click(object sender, EventArgs e)

        {

            WebRobot.SetPeriod((int)nud_period.Value);

        }

 

        private void btn_add_seed_Click(object sender, EventArgs e)

        {

            WebRobot.SetSeedSite(tbox_seed_site.Text);

        }

 

        private void btn_refresh_candi_Click(object sender, EventArgs e)

        {

            lv_candidate.Items.Clear();

            List<Candidate> list = EHDbmForAll.GetCandidates();

 

            foreach (Candidate candi in list)

            {

                ListViewItem lvi = new ListViewItem(

                    new string[] { candi.Url, candi.Depth.ToString() });

                lv_candidate.Items.Add(lvi);

            }

        } 

 

        private void btn_refresh_purl_Click(object sender, EventArgs e)

        {

            lv_posted.Items.Clear();

            List<PostedUrl> list = EHDbmForAll.GetPostedUrls();

            foreach (PostedUrl purl in list)

            {

                ListViewItem lvi = new ListViewItem(

                    new string[] { purl.Url, purl.OriginUrl, purl.Depth.ToString() });

                lv_posted.Items.Add(lvi);

            }

        }

 

        private void bnt_refresh_purl2_Click(object sender, EventArgs e)

        {

            lbox_posted_page.Items.Clear();

            List<PostedUrl> list = EHDbmForAll.GetPostedUrls();

            foreach (PostedUrl purl in list)

            {

                lbox_posted_page.Items.Add(purl);

            }

        }

 

        void lbox_posted_page_SelectedIndexChanged(object sender, EventArgs e)

        {

            if (lbox_posted_page.SelectedIndex == -1)

            {

                return;

            }

            PostedUrl purl = lbox_posted_page.SelectedItem as PostedUrl;

            ruc_selected.SetRankedUrl(new RankedUrl(purl, 0));

        }

         private void btn_refresh_moph_Click(object sender, EventArgs e)

        {

            List<string> list = EHDbmForAll.GetMorphemes();

            lbox_morpheme.DataSource = list;

        }

 

        void lbox_morpheme_SelectedIndexChanged(object sender, EventArgs e)

        {

            lview_morpheme.Items.Clear();

            if (lbox_morpheme.SelectedIndex == -1)

            {

                return;

            }

            string mname = lbox_morpheme.SelectedItem as string;

            List<InvertedElem> list = EHDbmForSearch.GetInvertedFile(mname);

            foreach (InvertedElem ie in list)

            {

                int tcnt = EHDbmForSearch.GetTotalCountInUrl(ie.Url);

                ListViewItem lvi = new ListViewItem(

                    new string[] { ie.Url,ie.RefCount.ToString(),tcnt.ToString() });

                lview_morpheme.Items.Add(lvi);

            }

        }

    }

}

 

 

 EHBrowser 컨트롤의 생성자는 순위화 한 페이지 개체를 입력 인자로 받는 것으로 변경하세요.

public EHBrowser(RankedUrl rurl)

 

 생성자 메서드에서는 마법사에 의해 작성한 InitializeComponent 메서드 호출로 초기 자식 컨트롤 배치 등을 마친 후에 입력 인자로 받은 순위화 한 페이지 개체의 속성으로 자식 컨트롤의 Text속성을 설정합니다. 그리고 페이지 주소를 입력 인자로 WebBrowser 컨트롤인 wb Navigate 메서드를 호출합니다.

 

EHBrowser.cs

using System.Windows.Forms;

using WSE_Core;

 

namespace WSEManager

{

    public partial class EHBrowser : Form

    {

        public EHBrowser(RankedUrl rurl)

        {

            InitializeComponent();

            lb_addr.Text = rurl.Url;

            lb_title.Text = rurl.PUrl.Title;

            wb.Navigate(rurl.Url);

        }

    }

}

 

 이제 WSA Manager 응용을 빌드하여 테스트 해 보세요.

 

반응형