//새탭 생성
_driver.ExecuteScript("window.open();");

//*탭 포커싱
//마지막 탭(우측) 포커싱하기
_driver.SwitchTo().Window(_driver.WindowHandles.Last());

//첫번째 탭(좌측) 포커싱하기
_driver.SwitchTo().Window(_driver.WindowHandles.First());

//*특정 탭정보 저장, 특정 탭 포커싱

//현재 탭정보 저장
//string window = _driver.CurrentWindowHandle;

//모든 탭정보중 0번째 인덱스탭 저장
string window = _driver.WindowHandles.ToList()[0];
_driver.SwitchTo().Window(window);

//현재 탭 닫기
//탭을 닫은경우 포커싱을 잃어버리기 때문에 다른 탭으로 포커싱을 잡아줘야 합니다!!!
_driver.Close();

탭을 닫았으면 크롬드라이버는 포커싱중인 탭이 없기 때문에 문제가 생깁니다

꼭 다른 탭으로 포커싱을 잡아주세요!!

 

https://github.com/ForestBird1/TestSelenium.git

 

GitHub - ForestBird1/TestSelenium

Contribute to ForestBird1/TestSelenium development by creating an account on GitHub.

github.com

 


스크린샷

//*스크린샷 찍기
ITakesScreenshot _takesScreenshot = (ITakesScreenshot)_driver;
Screenshot screenshot = _takesScreenshot.GetScreenshot();
screenshot.SaveAsFile("screenshot.png", ScreenshotImageFormat.Png);

저장된 파일을 확인하면 웹 전체스크린샷이 찍혀있습니다.

그런데 만약 특정요소부분만 짤라서 사진을 찍고싶다면??

 


스크린샷 자르기

정확히 로고만 스크린샷을 찍고싶은데 어떻게 해야할까요?

어렵지 않습니다 로고이미지의 요소를 가져온뒤

가로, 세로, 위치 값을 가져와서 그만큼 전체스크린샷에서 잘라 저장하면 됩니다. 

//네이버 로고요소 가져오기
_web_elem = _driver.FindElement(By.XPath("//*[@id='header']/div[1]/div/div[1]/h1/a"));

//스크린샷을 비트맵으로 변경
Bitmap bitmap_screenshot = new Bitmap(new System.IO.MemoryStream(screenshot.AsByteArray));

//*전체스크린샷에서 로고크기에 맞춰자릅니다
Rectangle rect_crop_size = new Rectangle(_web_elem.Location.X, _web_elem.Location.Y, _web_elem.Size.Width, _web_elem.Size.Height);
bitmap_screenshot = bitmap_screenshot.Clone(rect_crop_size, bitmap_screenshot.PixelFormat);
bitmap_screenshot.Save(String.Format(Environment.CurrentDirectory + "//screenshot_2.png", ImageFormat.Png));

//*비트맵은 사용후 꼭 Dispose해야 메모리누수가 발생하지 않습니다
//*Dispose를 사용하지 않으려면 using문으로 사용해도 됩니다
bitmap_screenshot.Dispose();

차근차근 보시면 절대 어렵지 않습니다

 


화면에 보이지 않는 요소 스크린샷

로고는 대부분 사이트 상단에 있어서 웹 접속시 바로 보이기 때문에 스크린샷을 찍을 수 있습니다

그런데 꼭 스크롤을 해야할 정도로 밑에 있는 요소는 스샷을찍고 자르려고하면

System.OutOfMemoryException: '메모리가 부족합니다.'

라고 크래시가 납니다

위에서 우리가 비트맵에 스크린샷바이트값을 입력했고 그 범위를 넘긴것이라고 생각하면됩니다

우리가 직접 웹에서 스크롤을 해도 마찬가지로 크래시가 납니다

 

이유는 아무리 스크롤을해도 스크린샷의 사이즈인 가로,세로길이는 강제로 늘리지 않는 한 모니터사이즈만큼 고정되어 있습니다

그러나 요소의 위치는 스크린샷의 사이즈와 별개로 있기 때문에 단순히 요소의 위치만 구하고 스샷을 자르려고하면 문제가 생기기 마련입니다

전체스크린샷의 사이즈는 1000x1000이라면 스크롤하려는 요소의 위치는 1000,1300으로 스크린샷 사이즈,비트맵사이즈의 범위밖으로 넘어가게되기 때문에 문제가 생깁니다.

 

그래도 해결하는 방법인 다 존재하니 알아봅시다

우선 전체스샷을 찍을때 요소가 보여야겠죠? 이미 이런 기능은 존재합니다!!

개발자모드에서 바로 해당 요소로 스크롤이 됩니다 이걸 우리는 코드에서 작동시켜야하는데 방법은 말이죵

_web_elem = _driver.FindElement(By.XPath("//*[@id='footer']/div/div[3]/div[2]/div[1]/a/img"));
_driver.ExecuteScript("arguments[0].scrollIntoView(true);", _web_elem);

 

딱~ 해보시면 스크롤이 됩니다

이제 안보이던 요소를 스크린샷찍을 수 있게 되었습니다

그 다음 우리는 스크롤된 길이(y축)만큼 추가로 계산을 해야합니다

 

스크린샷은 왼쪽위를 (0,0)으로 기준점을 잡습니다

그런데 스크롤된 웹의 기준점은 (0, 스크롤길이) 입니다

이 격차만큼 우리는 계산을 해줘서 웹 기준점을 0, 0으로 맞춰줘야합니다

 

자 그럴려면 스크롤된 y축값을 가져옵시다

//웹 페이지의 Y기준점 찾기
var y_offset_object =_driver.ExecuteScript("return window.pageYOffset;");
int y_offset = (int)float.Parse(y_offset_object.ToString());

코드가 좀 이상하죠? int로 받고있는데 왜 굳이 float를 거치는지,,,

가끔 특정상황에서는 페이지Y오프셋이 소수점으로 떨어지는 경우가 있습니다.

그래서 바로 int.Parse를 하면 이상하게 작동하더라구요.

안전장치로 일단 float로 불러온뒤 int로 캐스팅후 가져왔습니다

물론 float로 가져와서 바로 사용해도 됩니다만 이따 뺄셈을 해야해서 정확한 계산을 하기 위해 int로 캐스팅해줬습니다

 

y축값을 가져왔으니 우리가 스샷을 찍으려는 요소의 위치를 y축값만큼 뺍니다

//*전체스크린샷에서 요소크기에 맞춰자릅니다
Rectangle rect_crop_size = new Rectangle(
    _web_elem.Location.X,
    _web_elem.Location.Y - y_offset, 
    _web_elem.Size.Width, 
    _web_elem.Size.Height);
bitmap_screenshot = bitmap_screenshot.Clone(rect_crop_size, bitmap_screenshot.PixelFormat);
bitmap_screenshot.Save(String.Format(Environment.CurrentDirectory + "//screenshot_scroll_crop.png", ImageFormat.Png));

y값만 계산했습니다

 

이 방법은 최대스크린샷사이즈보다 큰 요소는 찍기 어렵다는게 단점입니다...

 


분명 Capture node screenshot이라는 편한 기능이 있는데...

개발자모드에서 원하는 요소를 우클릭하면 Capture node screenshot이라는 기능이 있습니다

이걸사용하면 스크롤과 요소사이즈 상관없이 곧바로 요소를 스크린샷할 수 있습니다

하...근데 코드상에서 돌리는 방법은 아무리 삽질해도 안나오네요 아는사람있으면 댓글 부탁드립니다

 

 

https://github.com/ForestBird1/TestSelenium.git

 

GitHub - ForestBird1/TestSelenium

Contribute to ForestBird1/TestSelenium development by creating an account on GitHub.

github.com

 

이전편까지의 셀레니움기능들로 대부분의 간단한 매크로를 만들 수 있습니다.

게시글 제목만 가져오기, 특정기간내의 게시글 가져오기, 여러계정들의 정보바꾸기 등등

 

하지만 다음 페이지로 넘어가고 페이지 로딩이 되지도 않았는데 프로그램은 페이지가 로딩 안된지 몰라서 다음 단계로 진행할 수 있습니다. 

 


웹 로딩 대기

1.

_driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);

해당코드는 페이지로딩까지 최대 10초까지 기다린다는 뜻 입니다

10초내로 페이지로딩이 완료되면 다음 작업을 진행하고 10초가 넘어가면 더이상 기다리지않고 다음 작업을 진행하게됩니다

한번만 설정하면 모든 드라이버에서 적용됩니다

원하는 '초'단위의 숫자를 입력하시면 됩니다


2.

WebDriverWait wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(10));
IWebElement SearchResult = wait.Until(SeleniumExtras.WaitHelpers.ExpectedConditions.ElementExists(By.XPath(xpath)));

10초동안 xpath요소가 존재할때까지 대기한다는 뜻입니다

여기서 특별한 셀레니움기능이 추가되는데

해당 패키지를 먼저 설치하셔야 합니다


3.

Thread.Sleep(1000);

특정 상황에서는 페이지로딩을 기다려도 특정한 이유로 더 기다려야하는 상황이 생길 수 있습니다

이럴 땐 강제로 슬립을 걸어서 기다리는 방법이 있습니다.

ms단위 이므로 1초를 기다리려면 위의 코드처럼 1000으로 입력해야합니다

 


자바스크립트

IJavaScriptExecutor _js = (IJavaScriptExecutor)_driver;
_js.ExecuteScript(js);

이건 별거 없고 이렇게 바로 실행해주시면 됩니다

 

https://github.com/ForestBird1/TestSelenium.git

 

GitHub - ForestBird1/TestSelenium

Contribute to ForestBird1/TestSelenium development by creating an account on GitHub.

github.com

 


쓰기, 읽기, 지우기

이전의 3번게시글을 읽어서 XPath값을 가져오는 방법으로 아이디텍스트박스의 XPath값을 가져옵니다

private IWebElement _web_elem = null;

//쓰기
_web_elem = _driver.FindElement(By.XPath("//*[@id='id']"));
_web_elem.SendKeys("id");

//읽기
_web_elem = _driver.FindElement(By.XPath("//*[@id='id']"));
Console.WriteLine(_web_elem.Text);

//지우기
_web_elem = _driver.FindElement(By.XPath("//*[@id='id']"));
//둘중에 하나만 사용해도 됩니다
_web_elem.SendKeys("");
_web_elem.Clear();

꼭 XPath의 큰따옴표를 작은따옴표로 바꿔주셔야합니다!


이제 기본적인 매크로를 만들 수 있습니다

우리는 특정한 요소의 내용을 가져올 수 있고 텍스트박스에 입력할 수 있으며 클릭을통해 모든 행동을 제어할 수 있습니다.

아직 고급단계가 남아있긴하지만 어지간한 매크로를 만드는데는 충분합니다. 

물론 아직 완벽하게 만들기는 무리가 있습니다. 자바스크립트도 사용못하고 쓰레드슬립도 모르고 웹로딩을 대기하는 방법도 모릅니다. 특별히 스크린샷기능도 모르죠

다음 포스팅엔 조금더 고급단계로 넘어가보겠습니다

 

https://github.com/ForestBird1/TestSelenium.git

 

GitHub - ForestBird1/TestSelenium

Contribute to ForestBird1/TestSelenium development by creating an account on GitHub.

github.com

 


XPath?

'특정한 요소, 속성의 경로' 입니다

직접 네이버로그인을 셀레니움으로 해보며 알아봅시다

 


XPath가져오기

우리가 클릭하고 입력해야할 버튼과 텍스트박스

먼저 로그인을 하기 위해 사진의 로그인버튼을 눌러야 합니다

어 그런데 우리는 마우스커서를 버튼에 올려서 클릭만 하면 되었는데 '로그인버튼'을 어떻게 코드로 작성할지 아리송하시죠? 그럴때 필요한게 바로 로그인버튼의 경로를 알려주는 XPath를 이용하면됩니다

 

발퀄 ㅈㅅ..

성격상 각잡고 하려면 시작조차 안해서 대충이라도 올리려고 그림판으로 대충 표시만 했습니다.. 양해부탁...

아무튼 브라우저에서 F12를 누르면 개발자도구창이 나옵니다

우상단에 빨갛게 동그라미로 표시한 버튼을 누른뒤 웹에서 XPath를 얻고자하는 요소를 누릅니다

 

그러면 개발자 도구창에 파랗게 선택이 되는데 이것이 클릭한 요소입니다.

우리는 XPath값을 얻어야하므로 우클릭->Copy->Copy XPath를 눌러서 XPath값을 얻을 수 있습니다!!

컨트롤V하면 XPath를 볼 수 있는데

//XPath원본
//*[@id="account"]/a

//꼭 큰따옴표를 작은따옴표로 변경해야합니다
//*[@id='account']/a

이렇게 표시되는것을 알 수있습니다. 꼭 따옴표를 변경하세요!!

 

여기서 full XPath가 무엇인지 궁금해하실 수 있는데

XPath는 요소의 '상대경로'

full XPath는 '절대경로' 입니다

가끔 XPath로 작동이 되지 않는 웹이 있는데 이럴땐 fullXPath로 작동하면 대부분 잘 됩니다

 

자 이제 XPath를 가져올 수 있으니 클릭을 해봅시다


클릭

먼저 네이버URL로 이동합시다

//브라우저 생성
_driver = new ChromeDriver(_driverService, _options);
_driver.Navigate().GoToUrl("https://www.naver.com/");

아주 간단하죠? 브라우저 생성후 이동하면 됩니다

 

클릭코드는 아래와 같습니다

private void btn_click_loginpage_Click(object sender, EventArgs e)
{
    try
    {
        //요소를 찾습니다
        _web_elem = _driver.FindElement(By.XPath("//*[@id='account']/a"));
        //클릭합니다
        _web_elem.Click();
    }
    catch(NoSuchElementException exception)
    {
        //요소가 없습니다
    }
}

정말 간단하죠? 만약 요소를 찾을 수 없다면 catch문으로 빠져나옵니다

 

다음 포스트에서는 페이지로딩대기, 아이디와 비밀번호입력까지 진행하겠습니다

 

https://github.com/ForestBird1/TestSelenium.git

 

GitHub - ForestBird1/TestSelenium

Contribute to ForestBird1/TestSelenium development by creating an account on GitHub.

github.com

 


옵션?

우리가 셀레니움을 이용하는 대부분의 이유는 반복작업을 대체하기 위해 사용한다고 생각합니다.

단순한 반복작업을 하는데 굳이 브라우저가 눈에 보일 필요는 없겠죠

브라우저가 백그라운드에 존재하며 우리눈에 보이지 않더라도 작업은 계속 진행되게 하고 싶습니다

그리고 크롬드라이버프롬프트창도 숨기고 싶구요. gpu가속도 사용하기 싫고 안전한페이지검증절차도 거치기 싫을 수 있습니다. 이런 모든것들을 우리는 옵션설정을 통해 원하는 브라우저의 행동을 제어할 수 있습니다

 

 


너무 많은 옵션들

https://peter.sh/experiments/chromium-command-line-switches/

 

List of Chromium Command Line Switches « Peter Beverloo

 

peter.sh

해당 사이트에서 크롬의 명령문을 찾을 수 있습니다

하지만 너무 많죠? 자주사용되는 명령문만 다뤄보겠습니다

 

		_options = new ChromeOptions();

            //명령프롬프트 숨기기
            _driverService.HideCommandPromptWindow = true;

            //안전하지않은 페이지경고 무시하기
            _options.AddArgument("--ignore-certificate-errors");

            //gpu가속 비활성화
            _options.AddArgument("--disable-gpu");

            //브라우저 숨기기
            _options.AddArgument("--headless");

            //브라우저 사이즈조절. headless모드에서 사용하면 유용합니다
            _options.AddArgument("--window-size=1920,1080");

주석에 나와 있는대로 이해하시면 됩니다.

다만 추가 설명이 필요한곳이 window-size옵션인데 

특정 웹에서는 브라우저 사이즈가 작으면 웹 디자인이 변경되기도 합니다

나는 최대화된 브라우저를 기준으로 코드를 작성했는데

headless로 브라우저를 숨겨서 작동시키면 제대로 동작하지 않는 경우가 종종 생깁니다

이런 것을 방지하기 위해 브라우저를 숨겨서 작동시키지만 브라우저사이즈를 옵션으로 직접 늘려줍니다.

 

만약 명령프롬프트를 숨겼다면 해당코드를 프로그램종료전에 혹은 종료시 꼭 작동시켜주세요

_driver?.Quit();

프로그램을 종료한다고 크롬드라이버까지 자동으로 종료되지 않습니다.

드라이버를 종료하지 않았다면 프로그램을 종료해도 드라이버는 작업관리자에서 둥둥 떠다닙니다 조심하세요

참고로 코드에서 ?.는 null체크입니다

 

이제 코드를 실행해보시면 작업관리자에 크롬드라이버가 존재하지만 브라우저가 눈에 보이지 않을 것입니다.

지금은 연습단계이므로 이러한 옵션이 있다는 것을 염두하고

다음 포스트부터는 다시 옵션을 전부 제거하고 진행해보겠습니다

 

https://github.com/ForestBird1/TestSelenium.git

 

GitHub - ForestBird1/TestSelenium

Contribute to ForestBird1/TestSelenium development by creating an account on GitHub.

github.com

 


셀레니움?

웹 크롤링을 하기 위해 우리는 제공되는 라이브러리를 이용합니다

파이썬의 beutifulsoup처럼 말이죠

여기선 그중 Selenium, 언어는 C#, 크롬브라우저를 기준으로 다뤄보겠습니다

 

셀레니움은 웹 브라우저를 컨트롤하는 라이브러리라고 생각하시면 편합니다

이러한 셀레니움의 기능을 도움받아서 웹 크롤링이외에 비슷한 개념의 매크로를 만들수 있기 때문에

다룰줄안다면 나중에 웹 반복업무시 큰 도움이 될 것입니다

 


설치

비주얼 스튜디오의 Nuget으로 위의 3개의 패키지를 설치합니다

그리고 설치하면 안되는 패키지도 있는데요 아래 패키지는 주의해서 설치를 피하시면됩니다

구버전 크롬 웹드라이버

위의 패키지는 크롬85버전까지만 지원되고 2020년에 업데이트가 중단된 드라이버입니다

드라이버를 2개다 설치했다면 필히 버전에 맞게 하나 지워주세요

 


기본적인 작동법

//Selenium 
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;

namespace TestSelenium
{
    public partial class TestSelenium : Form
    {
        //Selenum_Chrome
        private ChromeDriverService _driverService = ChromeDriverService.CreateDefaultService();
        private ChromeOptions _options = new ChromeOptions();
        private ChromeDriver _driver = null;
        private IWebElement _web_elem = null;

        public TestSelenium()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            //브라우저 창 생성
            _driver = new ChromeDriver(_driverService, _options);
        }
    }
}

상단 using문에 셀레니움 라이브러리를 추가후

셀레니움변수를 생성하시면됩니다

 

여기까지 진행하셨다면 브라우저가 생성되는 것 까지 보실 수 있습니다

 

이제 셀레니움을 이용할 모든 준비가 되었습니다!

다음 포스트부터 차근차근 셀레니움의 여러작동법을 올리겠습니다

 

 

https://github.com/ForestBird1/TestSelenium.git

 

GitHub - ForestBird1/TestSelenium

Contribute to ForestBird1/TestSelenium development by creating an account on GitHub.

github.com

 

+ Recent posts