6.2.1 WebCollect 구현

이제 웹 로봇 라이브러리에 필요한 형식을 구현합시다.

먼저 관리자 응용인 WSE Manager로 웹 수집을 가동하거나 멈추게 하거나 수집 주기 등을 설정할 때 사용하는 WebCollect 형식부터 구현합시다.

public class WebCollect

WebCollect에서는 수집 주기와 상대적 깊이, 주기적인 수집을 위한 타이머가 필요합니다. 수집 주기의 최소값과 상대적 깊이의 최대값을 상수로 정의합니다. 그리고 타이머 개체를 참조할 멤버 필드를 선언하고 상대적 깊이를 설정할 멤버 필드를 선언합니다. 상대적 깊이의 초기값은 상수로 정의한 최대값으로 설정할게요.

public const int min_interval = 5000;
public const int max_depth = 100;
Timer timer;
int depth = max_depth;

사용하는 개발자 편의성을 위해 상대적 깊이를 가져오기 및 설정하기 속성을 추가합니다. 설정하기는 별도의 메서드를 정의하여 이를 호출하는 것으로 할게요.

public int Depth
{
    get
    {
        return depth;
    }
    set
    {
        SetDepth(value);
    }
}

상대적 깊이를 설정하는 메서드에서는 입력 인자로 받은 값이 최대값을 벗어나는지 확인하여 벗어나지 않게 조절해야 합니다.

public void SetDepth(int value)
{
    if (value > max_depth)
    {
        value = max_depth;
    }
    depth = value;
}

수집 주기를 설정하기 및 가져오기 속성을 추가합시다. 가져오기에서는 타이머의 주기를 반환하고 설정하기에서는 별도의 메서드를 정의하여 이를 이용합시다.

public int Period
{
    get
    {
        return timer.Interval;
    }
    set
    {
        SetInterval(value);
    }
}

수집 주기를 설정하는 메서드는 입력 인자로 수집 주기를 받습니다.

public void SetInterval(int interval)

수집 주기는 타이머의 주기를 변경하는 작업입니다. 이미 타이머가 활성화 상태이면 비활성화 바꾸어야 합니다. 따라서 타이머 주기를 바꾸기 전에 타이머의 활성화 상태를 기억한 후에 타이머를 비활성화 상태로 변경합니다.

bool check = timer.Enabled;
timer.Enabled = false;

만약 입력 인자로 받은 수집 주기가 최소 수집 주기보다 작으면 최소 수집 주기로 변경합니다.

if (interval < min_interval)
{
    interval = min_interval;
}

타이머의 수집 주기를 변경하고 기억해 놓았던 원래 타이머의 활성화 상태로 복원합니다.

timer.Interval = interval;
timer.Enabled = check;

웹 수집기 가동 상태를 활성화 혹은 비활성화할 수 있게 속성을 정의합시다. 웹 수집기의 가동을 활성화하는 것은 타이머를 활성화하는 것이며 비활성화하는 것도 타이머를 비활성화하는 것입니다.

public bool Enabled
{
    get
    {
        return timer.Enabled;
    }
    set
    {
        timer.Enabled = value;
    }
}

생성자 메서드에서는 초기 설정합니다.

public WebCollect()
{
    InitWebCollect();
}

Seed 사이트 주소를 추가하는 메서드를 제공합시다. 입력 인자로 추가할 사이트 주소를 받고 EHDbmForAll 라이브러리를 이용하여 Seed 사이트를 추가합니다.

public void AddSeedSite(string url)
{
    EHDbmForAll.AddSeedSite(url);
}

초기 설정에서는 타이머를 생성하고 수집 주기를 설정합니다. 그리고 타이머 Tick 이벤트 핸들러를 추가합니다.

private void InitWebCollect()
{
    timer = new Timer();
    SetInterval(min_interval);
    timer.Tick += new EventHandler(timer_Tick);
}

타이머 Tick 이벤트 핸들러에서는 정적 클래스 EHDbmForAll의 GetFrontCandidate 메서드를 이용하여 수집 대상 사이트 목록에서 맨 앞의 대상 사이트를 얻어옵니다.

void timer_Tick(object sender, EventArgs e)
{
    Candidate candidate = EHDbmForAll.GetFrontCandidate();

만약 얻어온 대상 사이트 개체가 null이면 아무 것도 하지 않고 반환합니다.

if (candidate == null)
{
   return;
}

얻어온 대상 사이트 개체가 null이 아니면 웹 페이지를 수집하는 WebPageGetter개체를 생성하고 수집을 시작합니다. 물론 수집이 완료할 때 수행할 수 있는 수집 완료 이벤트 핸들러를 등록해야 합니다.

WebPageGetter wpg = new WebPageGetter(candidate);
wpg.WebPosted += new WebPostedEventHandler(wpg_WebPosted);
wpg.Start();

WebPageGetter 클래스를 추가하고 WebPostedEventHandler 대리자 정의 및 인자 클래스를 추가하세요. 그리고 WebPageGetter 클래스에 수집 완료 이벤트를 추가하세요.

public delegate void WebPostedEventHandler(object sendor,WebPostedEventArgs e);
public class WebPostedEventArgs:EventArgs
{
}

class WebPageGetter
{
    public event WebPostedEventHandler WebPosted = null;
}

WebCollect의 웹 수집 완료 이벤트 핸들러에서는 수집 결과를 추가하고 수집 결과에 있는 링크를 다시 수집 대상에 추가합니다. 이들은 각각 메서드로 정의하여 이를 호출하기로 합시다.

void wpg_WebPosted(object sendor, WebPostedEventArgs e)
{
    AddResult(e);
    NeedMoreGet(e);
}

수집한 결과를 추가하는 AddResult 메서드를 구현합시다.

private void AddResult(WebPostedEventArgs e)

정적 클래스 EHDbmForAll의 StorePosteUrl 메서드를 호출합니다.

EHDbmForAll.StorePostedUrl(e.Page);

이를 위해 WebPostedEventArgs 클래스에 PostedUrl 개체를 가져오기 할 수 있는 속성을 추가하세요.

public PostedUrl Page
{
    get
    {
        throw new NotImplementedException();
    }
}

수집한 결과의 링크를 수집 대상 목록에 추가하는 NeedMoreGet 메서드를 구현합시다.

private void NeedMoreGet(WebPostedEventArgs e)

상대적 깊이는 수집한 사이트의 상대적 깊이에 1을 더한 값입니다.

int depth = e.Page.Depth + 1;

만약 상대적 깊이가 최대 상대적 깊이보다 크면 더 이상 작업하지 않습니다.

if (depth > this.depth)
{
    return;
}

수집한 결과의 링크 목록의 사이트 주소를 정적 클래스 EHDbmForAll의 AddCandidate 메서드를 호출하여 수집 대상 목록에 추가합니다.

foreach (string url in e.Links)
{
    EHDbmForAll.AddCandidate(url, depth);
}

이를 위해 WebPostedEventArgs 클래스에 링크 목록을 가져오기할 수 있는 속성을 추가합니다.

public List<string> Links
{
    get
    {
        throw new NotImplementedException();
    }
}

▷ WebCollect.cs

using System;
using System.Windows.Forms;
using WSE_Core;
using DBM_ForAll; 

namespace WEB_Robot_Lib
{
    /// <summary>
    /// 웹 수집기
    /// </summary>
    public class WebCollect
    {

        /// <summary>
        /// 웹 수집 완료 이벤트
        /// </summary>
        public event WebPostedEventHandler WebPosted = null;

        /// <summary>
        /// 수집 최소 주기 
        /// </summary>
        public const int min_interval = 5000;
        /// <summary>
        /// 수집 최대 상대적 깊이
        /// </summary>
        public const int max_depth = 100;

        Timer timer;
        int depth = max_depth;

        /// <summary>
        /// 상대적 깊이 - 가져오기 및 설정하기
        /// </summary>
        public int Depth
        {
            get
            {
                return depth;
            }
            set
            {
                SetDepth(value);
            }
        } 

        /// <summary>
        /// 수집 주기 - 가져오기 및 설정하기
        /// </summary>
        public int Period
        {
            get
            {
                return timer.Interval;
            }
            set
            {
                SetInterval(value);
            }
        }


        /// <summary>
        /// 수집 활성화 상태- 가져오기 및 설정하기
        /// </summary>
        public bool Enabled
        {
            get
            {
                return timer.Enabled;
            }
            set
            {
                timer.Enabled = value;
            }
        }

        /// <summary>
        /// 상대적 깊이 설정 메서드
        /// </summary>
        /// <param name="value">상대적 깊이</param>
        public void SetDepth(int value)
        {
            if (value > max_depth)
            {
                value = max_depth;
            }
            depth = value;
        }


        /// <summary>
        /// 수집 주기 설정 메서드
        /// </summary>
        /// <param name="interval">수집 주기</param>
        public void SetInterval(int interval)
        {
            bool check = timer.Enabled;
            timer.Enabled = false;
            if (interval < min_interval)
            {
                interval = min_interval;
            }
            timer.Interval = interval;
            timer.Enabled = check;
        }
        /// <summary>
        /// 생성자
        /// </summary>
        public WebCollect()
        {
            InitWebCollect();
        }
        /// <summary>
        /// Seed 사이트 추가 메서드
        /// </summary>
        /// <param name="url">사이트 주소</param>
        public void AddSeedSite(string url)
        {
            EHDbmForAll.AddSeedSite(url);
        }

        private void InitWebCollect()
        {
            timer = new Timer();
            SetInterval(min_interval);
            timer.Tick += new EventHandler(timer_Tick);
        }

        void timer_Tick(object sender, EventArgs e)
        {
            Candidate candidate = EHDbmForAll.GetFrontCandidate();

            if (candidate == null)
            {
                return;
            }

            WebPageGetter wpg = new WebPageGetter(candidate);
            wpg.WebPosted += new WebPostedEventHandler(wpg_WebPosted);
            wpg.Start();
        } 

        void wpg_WebPosted(object sendor, WebPostedEventArgs e)
        {
            AddResult(e);
            NeedMoreGet(e);
        } 

        private void NeedMoreGet(WebPostedEventArgs e)
        {
            int depth = e.Page.Depth + 1;

            if (depth > this.depth)
            {
                return;
            }

            foreach (string url in e.Links)
            {
                EHDbmForAll.AddCandidate(url, depth);
            }
        } 

        private void AddResult(WebPostedEventArgs e)
        {
            EHDbmForAll.StorePostedUrl(e.Page);

            if (WebPosted != null)
            {
                WebPosted(this, e);
            }
        }
    }
}