빅데이터/데이터분석 with C#

뉴스 크롤링을 넘어 형태소 분석 구현

언제나휴일 2020. 5. 4. 20:43
반응형

 

이전 강의에서 작성한 뉴스 크롤링 라이브러리를 참조 추가합니다.

그리고 이번 강의에서는 형태소를 분석하여 구현합니다.

참고로 2020년 5월 4일 저녁 7시 30분에 "스포츠"로 검색하였습니다.

1100개의 뉴스의 앞부분을 분석한 BEST50입니다.

(한 글자이며 의미없는 것은 필터링하였습니다.)

50. 스포티비뉴스:33
49. KBO:33
48. 손진아:33
47. 첫:34
46. 예정:34
45. 최고:35
44. 활동:35
43. 트랙스:35
42. 교육기부:36
41. 모델:38
40. 72개월:38
39. 스포츠마케팅:39
38. 한도:40
37. 대표적인:40
36. 후원:42
35. 관계자:43
34. 개막전:45
33. 5일:46
32. 사진:47
31. SUV:47
30. 개막:49
29. 연계:50
28. 지원:51
27. 브랜드:51
26. 옥영화:51
25. 마케팅:52
24. 잠실야구장에서:54
23. 대비:55
22. 오전:60
21. 출시:60
20. KBO리그:63
19. 미국:64
18. 경기:66
17. 잠실:68
16. 선수:69
15. 렉스턴:69
14. 트윈스:72
13. 스포츠투데이:75
12. 시즌:77
11. 훈련:81
10. 코로나19:82
9. 두산:87
8. LG:88
7. 2020:93
6. 프로야구:117
5. 매경닷컴:127
4. MK스포츠:130
3. 기자:207
2. 4일:216
1. 스포츠:442

 

소스 코드

  • Morpheme.cs
using System;

namespace 형태소_분석기_만들기
{
    /// <summary>
    /// 형태소
    /// </summary>
    public class Morpheme:IComparable
    {
        /// <summary>
        /// 형태소 단어 - 가져오기 및 설정하기
        /// </summary>
        public string Name
        {
            get;
            set;
        }
        /// <summary>
        /// 빈도수 - 가져오기 및 설정하기
        /// </summary>
        public int Count
        {
            get;
            set;
        }
        /// <summary>
        /// 형태소 생성자
        /// </summary>
        /// <param name="name">단어</param>
        /// <param name="count">빈도수</param>
        public Morpheme(string name,int count)
        {
            Name = name;
            Count = count;
        }
        /// <summary>
        /// ToString 재정의
        /// </summary>
        /// <returns>단어</returns>
        public override string ToString()
        {
            return Name;
        }

        /// <summary>
        /// 비교 - 정렬을 위해 제공
        /// </summary>
        /// <param name="obj">비교 대상 - 형태소 형식 개체여야 함</param>
        /// <returns>비교 결과</returns>
        public int CompareTo(object obj)
        {
            Morpheme mo = obj as Morpheme;
            if(mo == null)
            {
                throw new ApplicationException("비교 대상 개체는 Morpheme 형식이어야 합니다.");
            }
            return Count.CompareTo(mo.Count);
        }
    }
}
  • MorphemeParser.cs
using System;
using System.Collections.Generic;

namespace 형태소_분석기_만들기
{
    /// <summary>
    /// 형태소 분석기 - 정적 클래스
    /// </summary>
    public static class MorphemeParser
    {
        static string[] filters = {"~","!","#","$","%","^","&","*","(",")","-","_","=","+","|",@"\",
                "{","}","[","]",";",":","\"","'","<",">",",",".","/","?"," ","","quot","amp","apos","lt","gt"};
        static string[] pf_filters = { "는", "은", "을", "를", "가", "에", "게", "에게","의","들","있다.","없다","이다","입니다", "합니다" ,
            "합시다"};

        /// <summary>
        /// 형태소 분석 메서드
        /// </summary>
        /// <param name="source">원본 문자열</param>
        /// <returns>형태소 컬렉션</returns>
        public static List<Morpheme> Parse(string source)
        {
            List<Morpheme> mlist = new List<Morpheme>();
            string[] elems = source.Split(filters, StringSplitOptions.RemoveEmptyEntries);
            foreach(string elem in elems)
            {
                ParseElem(elem, mlist);
            }
            mlist.Sort();
            return mlist;
        }

        private static void ParseElem(string elem, List<Morpheme> mlist)
        {
            if(elem == string.Empty)
            {
                return;
            }
            foreach(string pf_filter in pf_filters)
            {
                if(elem.EndsWith(pf_filter))
                {
                    MakeElem(pf_filter, elem, mlist);
                    return;
                }
            }
            RecordElem(elem, mlist);
        }

        private static void RecordElem(string elem, List<Morpheme> mlist)
        {
            foreach(Morpheme mo in mlist)
            {
                if(mo.Name == elem)
                {
                    mo.Count++;
                    return;
                }
            }
            Morpheme mo2 = new Morpheme(elem, 1);
            mlist.Add(mo2);
        }

        private static void MakeElem(string pf_filter, string elem, List<Morpheme> mlist)
        {
            int pos = elem.Length - pf_filter.Length;
            string sub = elem.Substring(0, pos);
            RecordElem(sub, mlist);
        }

        /// <summary>
        /// 형태소 통합 메서드
        /// </summary>
        /// <param name="src">원본 형태소 컬렉션</param>
        /// <param name="dest">타겟 형태소 컬렉션</param>
        /// <returns>타겟 형태소 컬렉션</returns>
        public static List<Morpheme> Merge(List<Morpheme> src, List<Morpheme> dest)
        {
            foreach(Morpheme mo in src)
            {
                RecordElem2(mo, dest);
            }
            dest.Sort();
            return dest;
        }

        private static void RecordElem2(Morpheme mo, List<Morpheme> dest)
        {
            foreach(Morpheme mo2 in dest)
            {
                if(mo2.Name == mo.Name)
                {
                    mo2.Count += mo.Count;
                    return;
                }
            }
            dest.Add(mo);
        }
    }
}
  • Program.cs
using System;
using System.Collections.Generic;
using 네이버_뉴스_크롤링_라이브러리_제작;

namespace 형태소_분석기_만들기
{
    class Program
    {
        static void Main(string[] args)
        {
            string id = [네이버 개발자센터에서 발급받은 애플리케이션 ID];
            string secret = [네이버 개발자센터에서 발급받은 애플리케이션 Secret];
            NaverNews nn = new NaverNews(id, secret);
            int total = nn.Find("스포츠");
            Console.WriteLine(total);

            List<Morpheme> morphemes = new List<Morpheme>();
            List<Morpheme> src_morphemes;
            List<News> nc;
            for (int start = 1; (start < 1000) && (start < total); start += 100)
            {
                nc = nn.FindNews(start, 100);
                foreach (News news in nc)
                {
                    Console.WriteLine(news.Title);
                    Console.WriteLine("==");
                    Console.WriteLine(news.Description);
                    Console.WriteLine("==================================================");

                    src_morphemes = MorphemeParser.Parse(news.Description);
                    MorphemeParser.Merge(src_morphemes, morphemes);
                }
            }

            foreach(Morpheme morpheme in morphemes)
            {
                Console.WriteLine("{0}:{1}", morpheme, morpheme.Count);
            }
        }
    }
}
반응형