Development note/C#

[C#] Selenium을 사용하는 방법

v명월v 2020. 5. 29. 19:50

안녕하세요. 명월입니다.


이 글은 C#에서 Selenium을 사용하는 방법에 대한 글입니다.


먼저 Selenium에 대해서 간략하게 설명하면 브라우져의 API를 이용하여 웹 화면 테스팅을 하기 위한 모듈입니다. Selenium은 Open source로서 무료로 테스트 환경을 만들 수 있습니다.

Selenium으로 제어할 수 있는 브라우져는 firefox, Chrome, safari, IE등의 거의 메인 브라우저는 모두 접근이 가능합니다.

참고로 Selenium은 브라우저를 원격 제어하는 함으로 웹 서비스의 브라우저의 호환 테스팅, 자동화 테스팅에 적합한 모듈입니다. 웹 크롤링으로 사용하기에는 성능 및 환경의 제약으로 적합하지 않습니다.

크롤링을 하려면 HttpWebRequest나 Gecko 라이브러리를 사용할 것을 추천합니다.

링크 - [C#] HttpConnection을 이용해서 웹 페이지 가져오기

링크 - [C#] Gecko 라이브러리 (웹 스크래핑)

구글을 검색하니 Selenium을 이용한 크롤링에 대한 글이 많이 있어서(딱히 저격하는 건 아닙니다.)..

Selenium은 원격 제어이기 때문에, 일단 웹 브라우저가 설치되어 있어야하는 환경 제약, 그리고 브라우저 에러 발생할 시 그대로 멈춰버릴 수 있는 현상이 많이 있기 때문에 크롤링으로는 적합하지 않습니다. 크롤링를 만들어 놓고 제대로 작동하나 안하나 하루종일 감시해야 하는 상황이 발생하기 때문에..


먼저 Selenium를 사용하기 위해서 Nuget에서 라이브러리를 다운로드 받습니다.

그리고 저는 Chrome 브라우저를 이용할 것입니다. Chrome의 버전을 확인해야 합니다.

저는 81.xxx이네요.

그럼 Chrome Driver의 81.xxx를 다운 받습니다.

여기서 버전이 다르면 실행이 안될 수 있으니 꼭 버전을 맞추어야 합니다.

using System;
using System.Threading;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium;

namespace Example
{
  static class Program
  {
    // driver를 Script 실행 인터페이스로 변환
    static IJavaScriptExecutor Script(this IWebDriver driver)
    {
      return (IJavaScriptExecutor)driver;
    }
    // 스크립트 클릭 함수
    static void ClickScript(this IWebDriver driver, IWebElement element)
    {
      // 스크립트 클릭
      driver.Script().ExecuteScript("arguments[0].click();", element);
    }
    // 실행 함수
    static void Main(string[] args)
    {
      // ChomeDriver 인스턴스 생성
      // 스택 영역이 종료되면 자동으로 Chrome 브라우저는 닫힌다.
      using (IWebDriver driver = new ChromeDriver())
      {
        // 블로그 URL로 접속
        driver.Url = "https://nowonbun.tistory.com";
        // 대기 설정. (find로 객체를 찾을 때까지 검색이 되지 않으면 대기하는 시간 초단위)
        driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(2);
        // xpath로 element를 찾는다. 이 xpath는 명월 일지 블로그의 왼쪽 메뉴의 Dev note의 Javascript, Jquery, Css 카테고리다.
        var element = driver.FindElement(By.XPath("//*[@id='leftside']/div[2]/ul/li/ul/li[1]/ul/li[6]/a"));
        // 클릭한다. 사실 element.Click()로도 클릭이 가능한데 가끔 호환성 에러가 발생하는 경우가 있다.
        driver.ClickScript(element);
        // css selector로 "[Javascript] 팝업 라이브러리(bootbox)"의 포스트를 찾는다. 찾을 때까지 무한 루프
        while (true)
        {
          try
          {
            // css selector로 element를 찾는다.
            element = driver.FindElement(By.CssSelector("[href^='/626']"));
            // 클릭
            driver.ClickScript(element);
            // 루프 정지
            break;
          }
          catch (Exception)
          {
            // 해당 element가 없으면 아래의 다음 페이지 element를 찾는다.
            element = driver.FindElement(By.CssSelector(".paging li.active+li > a"));
            // 클릭
            driver.ClickScript(element);
          }
        }
        // id가 promptEx인 데이터를 찾는다.
        element = driver.FindElement(By.XPath("//*[@id='promptEx']"));
        // 버튼은 클릭이 되는데 link 계열은 script로 클릭해야 한다.
        element.Click();
        
        // xpath로 팝업의 dom를 찾는다.
        element = driver.FindElement(By.XPath("/html/body/div[6]/div/div/div[2]/div/form/input"));
        // input text에 test의 값을 넣는다.
        element.SendKeys("test");
        // 5초 기다린다.
        Thread.Sleep(5000);
        // xpath로 팝업의 dom를 찾는다.
        element = driver.FindElement(By.XPath("/html/body/div[6]/div/div/div[2]/div/form/input"));
        // 속성 value를 출력한다.
        Console.WriteLine(element.GetAttribute("value"));
        // .article의 글에 p 태그의 속성을 전부 가져온다.
        var elements = driver.FindElements(By.CssSelector(".article p"));
        foreach (var ele in elements)
        {
          // 속성의 NodeText를 전부 출력한다.
          Console.WriteLine(ele.Text);
        }
      }
      // 아무 키나 누르면 종료
      Console.WriteLine("Press any key...");
      Console.ReadKey();
    }
  }
}

브라우저에서 팝업을 열고 test라는 값을 넣는 것을 확인할 수 있습니다.

콘솔을 보면 팝업에서 가져온 데이터 test를 확인할 수 있고 제 블로그 글을 스크랩해 온것을 확인할 수 있습니다.


세부적으로 탐색하는 클래스에 대해서 알아보겠습니다.

FindElement에서 By클래스의 XPath와 CssSelector를 사용했습니다.

함수명 설명
By.Id 요소의 속성 id로 찾는 오브젝트를 찾습니다.
By.ClassName 요소의 속성 class가 포함된 오브젝트를 찾습니다.
By.Name 요소의 속성 name로 찾는 오브젝트를 찾습니다.
By.XPath xpath를 이용해서 오브젝트를 찾습니다.
By.LinkText 하이퍼 링크의 텍스트로 오브젝트를 찾습니다.(완전 일치) - 탐색이 잘 안됩니다.
By.PartialLinkText 하이퍼 링크의 텍스트로 오브젝트를 찾습니다.(포함) - 탐색이 잘 안됩니다.
By.TagName 요소의 태그 이름으로 찾습니다.
By.CssSelector css selector(sizzle)로 오브젝트를 찾습니다.

위 탐색 함수는 FindElement를 사용했는데 복수의 경우는 FindElements로 검색하면 복수의 요소를 탐색합니다.


그리고 복잡한 웹의 경우, xPath를 구하기 어려운데 이 경우는 브라우저의 개발자 모드로 찾을 수 있습니다.

먼저 개발자 모드의 왼쪽 위를 클릭해서 찾고자 하는 요소를 선택하면 개발자 모드에 태그가 선택이 됩니다.

선택된 태그에서 마우스 오른쪽 클릭을 하면 copy 메뉴가 있는데 그 안에 xpath와 css selector 경로가 나오게 됩니다. 단순히 직접 경로가 나오기는 하지만 그 값을 통해서 약간의 상대 경로(?)로 수정하면 path를 찾을 수 있습니다.


그 외에 selenium에서 자주 사용하는 함수에 대해 소개하겠습니다.

함수명 설명
IWebDriver.Manage().Timeouts().ImplicitWait = TimeSpan 탐색시 object가 없으면 대기하는 시간.
IWebDriver.Close() 브라우저를 종료한다.
IJavaScriptExecutor.Script().ExecuteScript("스크립트", 요소) 해당 페이지에 스크립트를 만들 때 사용합니다. 요소는 필수 파라미터는 아니고 요소가 있으면 요소에 스크립트가 실행되고 없으면 전체 페이지에 스크립트가 움직입니다.
참고, IJavaScriptExecutor는 IWebDriver의 인스턴스를 강제 캐스팅으로 구할 수 있다.
IWebElement.GetAttribute("속성") 속성 값을 가져온다.
IWebElement.SendKeys() text의 경우는 값을 넣거나 key값을 넣으면 동작을 지시합니다.
키의 값은 다음 링크를 참조해 주세요.
링크 - https://www.selenium.dev/selenium/docs/api/dotnet/html/T_OpenQA_Selenium_Keys.htm
IWebDriver.Manage().Cookies 쿠키 을 가져온다.

그 밖의 API는 아래 링크를 참고해 주세요.

링크 - https://www.selenium.dev/selenium/docs/api/dotnet/


여기까지 C#에서 Selenium을 사용하는 방법에 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.