라이브러리
프로그래밍언어, 각종코드, 관련동향, 논문 등의 저장소
[메타 사이트 만들기] ② RSS 수집 로봇 구현·분석

[메타 사이트 만들기] ② RSS 수집 로봇 구현·분석

 

연재순서
1회. 블로거들의 집합소「메타 사이트 구현」
2회. RSS 수집 로봇 구현과 문제점 분석

 

이번 글은 지난번에 이은 메타 사이트 만들기 강좌 마지막 편으로 XML 파일을 이용하여 RSS 주소들의 목록을 관리하는 클래스의 제작, RSS 파일들로부터 읽은 정보들을 데이터베이스에 저장하는 방법에 대해 알아보겠습니다. 그리고 그를 구현하는 테이블, 저장 프로시저, 클래스들을 제작하고, 간단한 RSS 수집 로봇 프로그램을 제작해 보도록 하겠습니다.

자동차는 수많은 부품들을 각각 생산하여 설계도에 맞게 각각을 연결하고 조립하는 과정을 통해 만들어집니다. 우리가 만들고자 하는 메타 사이트도 핵심 부품들을 하나하나 만들고 마지막에 그러한 각각의 부품들을 조립하여 우리가 원하는 하나의 기능을 수행하는 프로그램을 완성하는 단계로 진행되고 있습니다. 지난 글을 통해 우리는 다음과 같은 내용을 진행하였습니다.


[1] 메타 사이트는 RSS 파일로부터 블로그의 정보를 얻는다는 사실을 이해한다.
[2] RSS 파일의 구조에 대해서 공부한다.
[3] RSS 파일의 내용을 저장할 클래스를 제작한다.
[4] RSS 파일로부터 내용을 읽어오는 RssReader 클래스를 제작한다.


이번 글은 지난 시간에 이은 메타 사이트의 부품 만들기 강좌 마지막 편으로 RSS 주소들을 저장하는 클래스와, XML 파일을 이용하여 RSS 주소들의 목록을 관리하는 클래스의 제작, RSS 파일들로부터 읽은 정보들을 데이터베이스에 저장하는 방법과 그를 구현하는 테이블, 저장프로시저, 클래스들을 제작하고 간단한 RSS 수집 로봇 프로그램을 만들어 보도록 하겠습니다.

RSS 파일들의 주소들을 관리하자
지난 호를 통해 한 개의 파일 또는 주소로부터 RSS 정보를 읽을 수 있는 RssReader 클래스가 완성되었습니다. 남은 문제는 여러 RSS 주소들을 관리하는 작업입니다. 이러한 작업은 데이터베이스를 이용하면 손쉽게 제작할 수 있지만, 데이터베이스를 이용하는 방법은 이후 RSS 파일의 내용들을 기록할 때 접할 예정이니, 우선은 XML 파일을 이용하여 저장하는 방법으로 RSS 주소들의 리스트를 관리해 보도록 하겠습니다.

RSS 주소들을 관리하기 위해서는 RSS 주소와 더불어 사이트에 대한 간략한 설명과 부가적인 자료들이 포함되면 됩니다. 예를 들어 블로그의 이름 또는 블로그의 종류 등 RSS 주소를 보고 이해하기 쉽도록 추가할 수 있습니다. 지면상 가장 간단하게 RSS 파일의 경로와 블로그의 제목 정도만 가지고 있는 클래스를 만들어 보도록 하겠습니다.

Public Class RssAddress
    Public RssURL As String
    Public BlogName As String

    Public Sub New()
    End Sub

    Public Sub New(ByVal RssURL As String, ByVal BlogName As String)
        Me.New()
        Me.RssURL = RssURL
        Me.BlogName = BlogName
    End Sub
End Class

아무런 작업을 하지 않는 기본 생성자와 데이터를 입력하며 객체를 생성하기 편리하게 하기 위해 RSS 파일의 주소와 블로그의 이름을 인자로 받는 생성자가 있습니다. 여기서는 아무런 작업도 하지 않는 기본 생성자를 굳이 선언한 이유가 있습니다.

우리는 RSS 정보들을 저장하기 위해서 XML 파일을 사용하며 아주 간편하게 XML 파일로 만들기 위해 직렬화(serialization)를 사용하려고 합니다. 이를 위해 닷넷 프레임워크에서 직렬화를 지원하는 System.Xml.Serialization.XmlSerializer 클래스를 이용하려고 하는데, 어떤 객체를 직렬화하기 위한 조건 중 하나가 객체는 반드시 기본 생성자를 가지고 있어야 한다는 것입니다. 일반 생성자에 Optional을 사용하여 기본 값을 지정해 주는 생성자를 만들어도 직렬화는 되지 않습니다.

이렇게 한 개의 RSS 파일의 정보를 가지고 있는 RssAddress 클래스가 완성되었습니다. 이제 다수의 RssAddress들을 저장할 컬렉션 클래스를 만들어야 합니다. CollectionBase 클래스를 상속받고, 인덱서와 메쏘드 등을 구현하여 컬렉션 클래스를 만드는 방법은 지난 호에서 RssItemCollection 클래스를 통해 소개했습니다. 그와 같은 방법으로 RssAddressCollection 클래스를 만들어줍니다. 그리고 여기에 XML 파일로부터 손쉽게 데이터를 불러오고, XML 파일로 손쉽게 저장할 수 있는 2개의 메쏘드를 추가하려고 합니다.

 <리스트 1> RssAddressCollection 클래스
사용자 삽입 이미지

Imports System.IO
Imports System.Xml.Serialization

Public Class RssAddressCollection
    Inherits CollectionBase

    Public Shared Function LoadFromXml(ByVal XmlFileName As String) As RssAddressCollection

        Dim serializer As New XmlSerializer(GetType(RssAddressCollection))
        Dim rssAddrs As RssAddressCollection
        Dim sr As New StreamReader(XmlFileName)
        rssAddrs = serializer.Deserialize(sr)
        sr.Close()

        Return rssAddrs

    End Function

    Public Shared Sub SaveToXml(ByVal XmlFileName As String, ByVal RssAddrs As RssAddressCollection)

        Dim serializer As New XmlSerializer(GetType(RssAddressCollection))
        Dim sw As New StreamWriter(XmlFileName)
        serializer.Serialize(sw, RssAddrs)
sw.Close()

    End Sub
    ...
    Collection 구현 관련 내용은 생략
    ...
End Class
사용자 삽입 이미지

추가한 메쏘드들은 Shared 키워드를 이용하여 객체를 생성하지 않아도 사용할 수 있도록 선언하고 있습니다. SaveToXml 메쏘드는 인자로 파일명과 RssAddressCollection을 받아 이를 해당 파일로 클래스의 직렬화 결과를 저장합니다. 그리고 LoadFromXml 메쏘드는 인자로 받은 파일로부터 역직렬화된 RssAddressCollection 클래스를 반환해 줍니다.

이들을 이용해서 손쉽게 객체를 XML 파일로 만들거나, 이미 저장되어 있는 XML 파일로부터 객체를 생성할 수 있습니다. 이렇게 만들어진 클래스들은 다음과 같은 방법으로 사용해볼 수 있습니다.


' RSS 주소들을 추가하고, XML 파일로 저장
Dim rssaddrs As New RssAddressCollection
rssaddrs.Add(New RssAddress("http://php.chol.com/~ppk314/tt/index.xml", "하늘이네 블로그"))
rssaddrs.Add(New RssAddress("http://namida.pe.kr/tt/index.xml", "나미다님의 블로그"))
RssAddressCollection.SaveToXml("MyRssAddressCollection.xml", rssaddrs)

' XML 파일로부터 객체를 생성
Dim loadedRssAddrs As RssAddressCollection = RssAddressCollection.LoadFromXml("MyRssAddressCollection.xml")


이제 RssReader 클래스를 함께 이용하여 RSS 주소들로부터 내용을 읽어와서 콘솔에 표시하는 간단한 프로그램을 제작해 보도록 하겠습니다. 테스트에 사용할 RSS 주소들은 앞의 RSS 주소들을 XML 파일로 저장하는 예제에서 생성한 MyRssAddressCollection.xml을 이용하도록 하겠습니다.

 <리스트 2> RSS 파일들로부터 내용을 읽어 표시하는 프로그램
사용자 삽입 이미지

Dim rssAddrs As RssAddressCollection = RssAddressCollection.LoadFromXml("MyRssAddressCollection.xml")

Dim rssReader As New RSSReader
For addrIndex As Integer = 0 To rssAddrs.Count - 1
    Console.WriteLine(rssAddrs(addrIndex).BlogName + " 블로그의 글 목록입니다.")
    rssReader.XmlAddress = rssAddrs(addrIndex).RssURL
    rssReader.readItems()

    For itemIndex As Integer = 0 To rssReader.Items.Count - 1
        Console.WriteLine(rssReader.Items(itemIndex).PubDate.ToString("[MM:dd HH:mm] ") + rssReader.Items(itemIndex).Title)
    Next
Next
사용자 삽입 이미지

<리스트 2>를 실행하면 각각의 주소들로부터 RSS 파일을 불러와서 다음과 같이 결과를 표시해 주게 됩니다.


하늘이네 블로그 블로그의 글 목록입니다.
[11:09 11:25] 생각만으론 바뀌지 않는다.
... 생략 ...
나미다님의 블로그 블로그의 글 목록입니다.
[11:09 15:14] BRIDGET JONES: THE EDGE OF REASON
... 생략 ...


여기까지의 과정을 통해서 우리는 RSS 파일 주소들로부터 모든 정보를 얻어오는 과정까지 완성하게 되었습니다. RSS 수집 로봇 제작에 있어서 가장 바탕이 되는 처리 부분을 완성한 것입니다.

RSS의 내용을 정리하자
RSS 파일들로부터 블로그의 모든 내용들을 가져오는 과정까지 완성하게 되었습니다. 이제 이러한 블로그의 내용들을 하나로 정리하는 작업만을 남겨두고 있습니다.

정리한다는 것의 의미는 이렇게 볼 수 있습니다. 내용들 중에서 새롭게 업데이트된 내용과 이전의 내용들을 분류해내고, 각각의 데이터들을 각각 다른 기준으로 저장하는 것과 많은 블로그들의 각 글들의 남다른 기준은 무엇일까요? 그 글만이 가지는 유일한 정보 말입니다. 날짜나 제목도 있을 수 있겠지만 그것들은 다른 글이라고 하더라도 같을 수 있는 가능성이 있습니다. 하지만 웹에서 그 글을 표시하는 주소는 단 1개뿐이겠지요?

그리고 혹시라도 RSS 주소들의 리스트에 같은 RSS 파일이 여러 개가 저장되는 문제가 생기더라도 주소를 기준으로 저장한다면 같은 글이 2개로 저장되는 문제는 발생하지 않게 됩니다. 이러한 내용들을 바탕으로 RSS 파일의 내용들을 저장할 데이터베이스 테이블을 만들어보도록 하겠습니다.

사용자 삽입 이미지
<표 1> RSS 내용을 저장할 Rss_Datas 테이블


테이블의 구조는 RssItem과 비슷한 모양을 가지고 있습니다. 여기에 Link와 PubDate에 인덱스를 걸어주도록 하겠습니다. 차후에 주소를 기준으로 데이터를 자주 불러오고, 날짜순으로 정렬을 많이 시도할 예정이기 때문입니다. 여기에 차후 부가적인 내용들을 추가하면 되지만 이 시간에는 가장 기본적인 내용들을 기준으로 만들어 보았습니다.

이제 이렇게 만들어진 테이블에 간편히 데이터를 동기화할 수 있는 저장 프로시저 한 개를 만들어 보도록 하겠습니다. 이 저장프로시저는 인자 값으로 RssItem의 내용을 주면 데이터베이스와 동기화하게 됩니다. 즉 데이터베이스에 이미 존재하는 경우 내용을 업데이트하고, 존재하지 않는 경우 새로운 글을 추가하게 됩니다. 차후 동기화에 따른 몇 가지 조건들을 추가하고자 할 때에도 이 저장 프로시저를 통해 만들면 됩니다. 우선은 가장 기본적인 동기화 프로시저를 만들어보도록 합시다.

 <리스트 3> RSS 정보를 데이터베이스와 동기화하는 저장 프로시저
사용자 삽입 이미지

CREATE PROCEDURE sp_Rss_DataSync
@Link varchar(200),
@PubDate smalldatetime,
@Title varchar(200),

@Author varchar(50),
@Description text
AS
    DECLARE @OldLink varchar(200)
    SELECT @OldLink = Link FROM Rss_Datas
    WHERE Link = @Link

    IF @OldLink IS NULL
        BEGIN
            INSERT INTO Rss_Datas(Link, GroupIdx, PubDate, Title, Author, Description)
            VALUES (@Link, @GroupIdx, @PubDate, @Title, @Author, @Description)
        END
    ELSE
        BEGIN
            UPDATE Rss_Datas SET
                PubDate = @PubDate,
                Title = @Title,
                Author = @Author,
                Description = @Description
            WHERE Link = @Link
        END
GO
사용자 삽입 이미지

해당 링크와 같은 글이 있는지를 체크하고 결과에 따라서 글을 저장하거나, 업데이트하는 간단한 쿼리 문장들을 사용한 저장 프로시저입니다. 차후에 동기화에 대한 여러 가지 기능들을 추가하고자 할 때 이 저장 프로시저만을 수정하면 됩니다.
간단한 예를 들면, 블로그에는 글을 쓰고 삭제하는 경우가 있습니다. 같은 내용인데 다시 정리해서 새롭게 쓰고자 하는 경우이지요.

이런 경우 같은 도메인이나 같은 블로그 주소이지만, 링크가 약간 달라집니다. 이런 경우 제목과 주소의 일부가 같은 경우 기존 글의 주소를 업데이트한다거나, 또는 시간대별로 동기화를 제한하는 기능 등을 들 수 있습니다. 이제 이렇게 만든 저장 프로시저를 쉽게 사용할 수 있는 RssItemSync 클래스를 만들어보도록 하겠습니다.

 <리스트 4> RssItemSync 클래스
사용자 삽입 이미지

Imports System.Data.SqlClient

Public Class RssItemSync
    Private sqlCon As SqlConnection

    Public Sub New(ByVal ConnectionObject As SqlConnection)
        sqlCon = ConnectionObject
    End Sub

    Public Sub ItemSync(ByVal Item As RSSItem)
        Dim isClosed As Boolean = False

        If sqlCon.State = ConnectionState.Closed Then
            isClosed = True
            sqlCon.Open()
        End If

        Dim sqlCmd As New SqlCommand("sp_Rss_DataSync", sqlCon)
        With sqlCmd
            .CommandType = CommandType.StoredProcedure
            .CommandTimeout = 180
            .Parameters.Add(New SqlParameter("@Link", SqlDbType.VarChar)).Value = Item.Link
            .Parameters.Add(New SqlParameter("@PubDate", SqlDbType.SmallDateTime)).Value = Item.PubDate
            .Parameters.Add(New SqlParameter("@Title", SqlDbType.VarChar)).Value = Item.Title
            .Parameters.Add(New SqlParameter("@Author", SqlDbType.VarChar)).Value = Item.Author
            .Parameters.Add(New SqlParameter("@Description", SqlDbType.Text)).Value = Item.Description
        End With
        sqlCmd.ExecuteNonQuery()

        If isClosed Then
            sqlCon.Close()
        End If
    End Sub
End Class
사용자 삽입 이미지

우선 이 클래스는 생성자로 SqlConnection을 받습니다. 그리고 ItemSync 메쏘드에서는 연결 객체가 닫혀 있는 경우 열고 작업을 하고 다시 닫아주고, 열려 있다면 열린 상태로 유지하도록 코드를 만들었습니다. 이는 이 메쏘드가 데이터 동화에서 여러 번 반복 사용되므로 연결?닫기의 작업을 반복하지 않게 하기 위해서 간단하게 구현해 보았습니다.

이제 여기까지의 완성된 각각의 클래스들을 이용하여 RSS 파일들로부터 데이터베이스와 동기화하는 프로그램을 간단히 만들어 보도록 하겠습니다.

 <리스트 5> RSS 정보를 읽어 데이터베이스와 동기화하는 RSS 수집 로봇
사용자 삽입 이미지

Dim rssAddrs As RssAddressCollection = RssAddressCollection.LoadFromXml("MyRssAddressCollection.xml")
Dim rssReader As New RSSReader
Dim sqlcon As New SqlClient.SqlConnection("[데이터베이스 연결 문자열]")
Dim ris As New RssItemSync(sqlcon)

Console.WriteLine(rssAddrs.Count + "개의 블로그의 싱크를 시작합니다.")
sqlcon.Open()
For addrIndex As Integer = 0 To rssAddrs.Count - 1
    Console.WriteLine(rssAddrs(addrIndex).BlogName + " 블로그를 싱크 시작")
    rssReader.XmlAddress = rssAddrs(addrIndex).RssURL
    rssReader.readItems()

For itemIndex As Integer = 0 To rssReader.Items.Count - 1
ris.ItemSync(rssReader.Items(itemIndex))
    Next
    Console.WriteLine(rssAddrs(addrIndex).BlogName + " 블로그 싱크 완료!")
Next
sqlcon.Close()
Console.WriteLine("전체 싱크가 완료되었습니다.")
사용자 삽입 이미지

여기에 시간대별로 반복해서 작업을 수행하게 하거나, MyRssAddressCollection.xml 파일을 메모장이 아닌 좀 더 쉽게 수정할 수 있는 방법만 만들어주고, 표현 계층을 멋지게 꾸미는 건 여러분들에게 맡기도록 하겠습니다. 멋진 RSS 수집 로봇을 완성해 주세요.

RSS 수집 로봇의 문제점
이번 강좌를 통해서 만들어본 RSS 수집 로봇은 RSS 파일을 읽고 관리하는 최소한의 기능들을 비교적 쉽게 구현하기 위해서 만들어진 프로그램입니다. 덕분에(?) 이 RSS 수집 로봇을 만들면서 여러 문제점들을 발견할 수 있었을 것입니다. 바로 RSS 파일을 일정 시간 간격으로 계속해서 읽는다면 네트워크 트래픽과 그것을 데이터베이스에 저장하는 과정에서 데이터베이스 처리량이 상당히 증가한다는 점인데요.

이러한 문제점들을 해결하기 위해서는 RSS 파일을 지금과 같이 RssTextReader 클래스를 이용하지 않고, HttpRequest, HttpResponse 클래스를 이용해서 RSS 파일을 읽는 방법을 사용하면 됩니다. 이 경우 파일을 요청할 때 HTTP 프로토콜에 있는 If-Modified-Since 필드를 사용하면 변경되지 않은 파일의 경우 데이터의 내용 대신에 헤더의 값만 넘어오게 되므로 트래픽은 물론 처리해야 할 데이터도 줄어들게 됩니다.

사용자 삽입 이미지
<그림1> RSS 내용을 저장할 Rss 수집로봇의 원리


그리고 RSS 파일의 주소가 몇 개 안 되는 지금 상태에서는 상관이 없지만, 100개가 넘어가는 경우 전체 RSS 파일을 싱크하는 시간은 점점 길어지게 됩니다. 하지만 자세히 살펴보면 데이터베이스를 처리하는 시간보다는 네트워크에서 파일을 불러오는 것에 상당한 시간이 소요되는 것을 볼 수 있습니다. 이 부분은 쓰레드를 이용하여 처리하면 더욱 빠른 속도로 싱크 작업을 할 수 있는 RSS 수집 로봇을 만들 수 있을 것으로 생각됩니다.

이밖에도 일정 시간 간격으로 올라오는, 흔히 ‘도배’라고 불리는 글들에 대해서는 싱크하지 않게 하는 방법 등 여러 가지 기능들에 대해서 생각해 보기 바랍니다. 여기까지 완성되었다면 ASP.NET을 이용하여 메타 사이트의 모습을 만드는 마지막 과정은 여러분에게 맡겨도 되겠지요?


메타 사이트 완성을 위해서 앞으로 해야 할 일들
[1] RSS 파일을 읽어오는 부분의 보완
[2] 싱크 기능을 멀티 쓰레드를 이용하여 처리하기
[3] 데이터베이스 싱크 기능의 강화(도배 방지 등의 기능)
[4] ASP.NET으로 웹 사이트 제작하기


더 멋진 메타 사이트를 꿈꾸다
아쉽게도 국내 3개 메타 사이트들의 기능은 모두 비슷합니다. RSS 파일을 읽고 그것을 표시해 주는 기본적인 기능들이죠. 메타 사이트의 등장은 오래 되었지만 아직도 메타 사이트는 걸음마 수준이라고 말하고 싶습니다. 이 말은 아직도 이 부분은 많은 실험과 도전 정신을 테스트해 볼 수 있는 미개척지란 말입니다.

이 강좌를 통해 메타 사이트를 만들기 위한 아주 기본적인 기술들을 익혔다면 이제 그것을 활용하여 지금까지 없었던 새로운 메타 사이트를 설계해 보는 것은 어떨까요? 저는 인터넷 강국 대한민국이 블로그에서도 강국이 되기를 바라며 오늘도 더 멋진 메타 사이트를 꿈꾸고 있습니다. 이제 여러분의 상상력을 보여주세요.@

* 이 기사는 ZDNet Korea의 제휴매체인마이크로소프트웨어에 게재된 내용입니다

 

박영욱 (Allblog.net 운영자)

2005/03/21

 

사용자 삽입 이미지

 

원문 :http://www.zdnet.co.kr/techupdate/lecture/dotnet/0,39024986,39134054,00.htm

  Comments,     Trackbacks