본문 바로가기

C# .Net

[C#] Selenium을 이용한 대신증권 기사 크롤링

using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using RealTimeNews;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using System.Net.Sockets;
using System.Security.Policy;
using System.Windows.Forms;

namespace RealTimeNews
{
    public partial class Crawler : Form
    {
        string date = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
        ChromeDriver driver;
        bool Changed = false;
        List<string> New_Paticle = new List<string>();
        List<string> Changed_data = new List<string>();
        string Last_Paticle = string.Empty;
        Timer timer = new Timer();

        string url = "https://www.daishin.com/g.ds?m=1073&p=2062&v=1442";
        string Element = "/html/body/div[2]/div[7]/div/div[2]/div[1]/ul/li/a";

        public Crawler()
        {
            InitializeComponent();
            this.DoubleBuffered = true;
        }

        private void Crawler_Load(object sender, EventArgs e)
        {
            Getdriver();
            var headline = GetNewsData();
            addtitle(headline);
            Crawler_refreshing();
        }

        private void Crawler_refreshing()
        {
            timer.Interval = 7000; //주기 설정
            timer.Tick += new EventHandler(titmer_tick); //주기마다 실행되는 이벤트 등록
            timer.Start();
        }

        private void titmer_tick(object sender, EventArgs e)
        {
            var headline = refresh_News();
            if (Changed == true) // 변경된 데이터가 있다면
            {
                addtitle(headline); // 데이터 추가
                Changed = false;
            }
            else
            {
                return;
            }
        }

        private void Getdriver()
        {
            ChromeOptions options = new ChromeOptions();
            ChromeDriverService driverService = ChromeDriverService.CreateDefaultService();
            driverService.HideCommandPromptWindow = true;
            options.AddArgument("--headless");

            driver = new ChromeDriver(driverService, options);
            driver.Url = url;
        }

        private List<string> GetNewsData()
        {
            string Title = string.Empty;
            // 대신증권 뉴스페이지는 만약 이미지가 있는 기사를 선택했다면 이미지와 같이볼건지 묻는 팝업을 띄운다
            // 팝업을 무시하는 구문이다
            try
            {
                var alert = driver.SwitchTo().Alert(); // driver가 Alert 팝업을 선택
                alert.Dismiss(); // 취소
            }
            catch (NoAlertPresentException)
            {

            }

            var headline = driver.FindElementsByXPath(Element); // 기사 페이지에있는 전체 헤드라인들을 불러온다

            for (int i = 0; i < headline.Count; i++)
            {
                // 공백을 무시
                if (headline[i].Text == "")
                {

                }
                else
                {
                    // 대신증권 제목 특성상 문자열 가공
                    char[] delimiter = { ',' };
                    OpenQA.Selenium.IWebElement k = headline[i];
                    var RawTitle = k.GetAttribute("onclick");
                    RawTitle.Remove(8);
                    var processTitle = RawTitle.Split(delimiter);

                    if (processTitle[6].Contains("방금") || processTitle[6].Contains("분전"))
                    {
                        Title = processTitle[5];
                    }
                    else
                    {
                        Title = processTitle[5] + processTitle[6];
                    }

                    New_Paticle.Add(Title);
                }
            }

            Last_Paticle = New_Paticle[0]; // 가장 최신 데이터를 설정하여 새로고침할때 참고한다.

            // DataGridview의 특성은 가장 하단의 셀에 최신데이터가 추가된다.
            // List 특성상 가장 최근데이터가 먼저 추가되기때문에 List상에는 가장 먼저들어온 데이터가 가장 뒤에있는 현상이 일어난다.
            // 그러므로 DataGridview에 추가할때 가장 최근데이터가 가장 늦게 들어가는 현상이 일어난다.
            // 최신데이터를 DataGridveiw에서 가장 아래쪽에 배치하기위해서(DataGridview에 특성은 하단 셀이 최근의 추가된 데이터) 추가된 List를 Reverse(반대) 시킨다.
            New_Paticle.Reverse();

            return New_Paticle;
        }

        private List<string> refresh_News()
        {
            string Title = string.Empty;
            driver.Navigate().Refresh();

            try
            {
                // 페이지에 뜨는 팝업 창 닫기
                var alert = driver.SwitchTo().Alert(); 
                alert.Dismiss();
            }
            catch (NoAlertPresentException)
            {

            }

            var headline = driver.FindElementsByXPath(Element);

            New_Paticle.Clear();
            for (int i = 0; i < headline.Count; i++)
            {
                if (headline[i].Text == "") // 네이버 기사특성상 공백이 같이오기때문에 공백을 무시
                {

                }
                else
                {
                    char[] delimiter = { ',' };
                    IWebElement k = headline[i];

                    // 대신증권 기사 제목 특성을 감안해 문자열 가공
                    var RawTitle = k.GetAttribute("onclick");
                    RawTitle.Remove(8);
                    var processTitle = RawTitle.Split(delimiter);

                    if (processTitle[6].Contains("방금") || processTitle[6].Contains("분전"))
                    {
                        Title = processTitle[5];
                    }
                    else
                    {
                        Title = processTitle[5] + processTitle[6];
                    }

                    New_Paticle.Add(Title);
                }
            }

            if (New_Paticle.IndexOf(Last_Paticle) != 0 && New_Paticle.IndexOf(Last_Paticle) != -1) // 데이터가 존재하고 변동이 있다면
            {
                Changed = true;
                Changed_data.Clear();
                int Last_Paticle_li_Number = New_Paticle.IndexOf(Last_Paticle);

                for (int i = Last_Paticle_li_Number - 1; i >= 0; i--)
                {
                    Changed_data.Add(New_Paticle[i]);
                }
                Last_Paticle = Changed_data[Changed_data.Count - 1]; // 최신 기사로 바꾼다.

                return Changed_data;
            }

            else if (New_Paticle.IndexOf(Last_Paticle) == -1) // 기사가 삭제되거나 수정되면 다시 실행(제일 큰 문제..)
            {
                Debug.WriteLine("데이터가 존재하지않습니다. 프로그램을 재시작합니다.");
                Changed = false;
                timer.Stop();
                Crawler_Load(null, null); // 재시작
                
            }

            else if (New_Paticle.IndexOf(Last_Paticle) == 0) // 데이터가 변동이 없다면
            {
                Changed = false;
            }

            return New_Paticle;
        }

        public void addtitle(List<string> Paticle)
        {

            Debug.WriteLine("값 추가");
            var title = Paticle.ToArray();
            var key = keyword();

            for (int i = 0; i < title.Length; i++)
            {
                RawNews.Rows.Add(title[i]);

                foreach (string x in key)
                {
                    // 제목에 아래 keyword 함수에 추가해놓은 단어가 하나라도 있다면 알림을 띄우고 ClassificationNews로 분류한다.
                    if (title[i].Contains(x))
                    {
                        ClassificationNews.Rows.Add(title[i]);
                        notification(title[i], title[i]);
                        Debug.WriteLine(title[i]);
                        break;
                    }
                }
            }
        }

        public void notification(string Text, string Title)
        {
            var notification = new NotifyIcon()
            {
                Visible = true,
                Icon = System.Drawing.SystemIcons.Information,
                BalloonTipText = Text,
                BalloonTipTitle = Title,
            };

            notification.ShowBalloonTip(10000);
        }

        public List<string> keyword()
        {
            List<string> keyword = new List<string>();

            keyword.Add("체결");
            keyword.Add("수주");
            keyword.Add("승인");
            keyword.Add("FDA");
            keyword.Add("계약");
            keyword.Add("개발");
            keyword.Add("임상");
            keyword.Add("수출");
            keyword.Add("공급");
            keyword.Add("허가");
            keyword.Add("납품");
            keyword.Add("증자");
            keyword.Add("치료제");
            keyword.Add("인수");
            keyword.Add("취득");
            keyword.Add("합병");
            keyword.Add("정부");
            keyword.Add("특허");
            keyword.Add("진단키트");
            keyword.Add("공시");
            keyword.Add("정부지원");

            return keyword;
        }

        private void Crawler_FormClosed(object sender, FormClosedEventArgs e)
        {
            Process[] chromeDriverProcesses = Process.GetProcessesByName("chromedriver");

            foreach (var chromeDriverProcess in chromeDriverProcesses)
            {
                chromeDriverProcess.Kill();
            }
        }