블로그 이미지
자유로운설탕

calendar

1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30

Notice

2017. 3. 26. 20:29 프로그래밍

  이번 시간은 웹 자동화에 대해서 얘기하려 한다. 자동화에 대한 몇 가지 생각들을 얘기 한 후, 셀레늄(selenium)의 구조를 설명 후, 구글에서 특정 키워드를 검색하여, 상위 5개의 결과 링크를 새로운 탭들에  로딩하는 예제를 구현 후, 이를 다시 보이지 않는 유령 웹브라우저인 PhantomJS를 이용해 호출하여, 몇 가지 생각해 볼 만한 주제들에 관해서 이야기를 하려한다. 이후 부록으로 IE 를 통해서 같은 구현을 하는 부분을 얘기한 후 마무리 하려 한다. 

 

 

[목차]

0. 왜 파이썬 공부에 구글을 이용하는게 좋은가?

1. 언어를 바라보는 방법. 파이썬을 어떻게 바라봐야 할까?

2. 파이썬 설치와 환경, 버전 선택 하기의 이유.

3. 만들고자 하는 기능을 모르는 조각으로 나눠 조사해 보기

4. 데이터 베이스에서 내용 가져와 출력하기

5. 암호화 모듈을 이용해 암복호화 해보기

6. 퍼즐 조각들을 합쳐보기

7. 엑셀 파일 사용해 보기 -> 부록 : fuction 을 이용해서, 코드 정리해 보기

8. 정규표현식을 왜 사용해야 할까? 언어속의 미니 언어 정규표현식 살펴보기

9. 입력과 결과를 GUI 화면과 연결해 보기

10. Whois API 이용해 보기

11. 웹페이지 호출해 내용 파싱 하기(BeautifulSoup 그리고 한계)

12. 자동화 - 웹 자동화(with Selenium)

13. 자동화 - 윈도우즈 GUI 자동화(with pywinauto)

14. 자동화 - 작업 자동화

15. 수학 라이브러리 살펴보기

16. 그래픽 라이브러리 살펴보기

17. 머신러닝에서의 파이썬의 역활

18. 웹 프로그래밍 - Legacy Web

19. 웹 프로그래밍 - Flask 살펴보기(feat. d3.js)

20. 웹 프로그래밍 - Django 살펴보기

21. 정리 - 이런저런 이야기

 

 

 

[들어가면서]

  '자동화'란 무엇일까? 자동화의 반대말에 해당하는 '수동화'를 먼저 생각해 보자. '수동화'란 결국 사람이 무언가를 하는 것, 또는 사물이 사람의 도움을 통해서 움직이는 것을 얘기한다고 볼 수 있다. 그럼 자동화는 사람의 손을 거치지 않고도 사람이 원하는 동작을 하는 것을 의미하는 단어라고 볼수 있다. 사실상 자동화는 우리가 사용하는 프로그래밍 언어나, 윈도우즈 같은 운영체제 자체도 자동화가 구현된 예라고 봐도 무방할 듯하다. 운영체제는 예전의 기계식 컴퓨터와 비교한다면 전원만 넣어주면, 사람의 행동에 따라서 반응하여 동작을 해주는 인터렉티브한 자동화 구현체라고 볼 수 있다. 더 확장하면 우리가 현실의 어떤 일을 하는데 사용하는 패턴이나 노하우 같은 부분도 실체화 되있지는 않지만 자동화라고 볼 수 있을것 같다.

 

  그렇다면 자동화를 구현하는데 있어서 가장 필수적이면서도 어려운 요소들는 무얼까? 일반적으로 자동화는 초기화 단계에서는 '스스로 움직이는 것'이 가장 중요하다. (잘은 모르지만) 처음 로봇이나 자율주행 자동차를 만든다면, 사람이 수동으로 운전하는 것처럼 자동으로 움직이고, 뛰고, 코너링을 돌고, 멈추고 하는 기능들이 필요할 것이다. 그런데 해당 부분의 요소가 어느정도 해결나게 되는 순간 새로운 차원의 어려운 문제가 발생하게 된다. 그것은 '환경'과의 상호동작 이라는 요소이다. 만약 자율주행 자동차 라면 도로의 커브라든지, 앞차와의 간격, 도로 노면 상태, 기후 상태, 보행자 상태, 교통 신호 등등 여러가지 주행 중 만날 수 있는 환경적인 부분의 차이를 인지하여, 해당 부분에 대해서 자동차 스스로 가지고 있는 여러가지 자동화 능력(전진, 후진, 회전, 브레이크 등등..)을 적용할지를 결정해야 한다(이런 부분에서 요즘 한참 유행하는 딥러닝 같은 요소들이 끼어 들수도 있을 듯하다). 그럼 그러한 환경을 인지하기 위해서는 어떻게 해야 할까? 우리가 현재 개발해 놓은 센서, 또는 그러한 환경을 인지하기 위한 새로운 타입의 센서가 있어서 원하는 환경 요소를 구조적인 소프트웨어적인 정보로 변환하여 제공하여야 할 것 이다.

 

  그럼 위의 가정에서 자동화를 위한 소프트웨어에서 필요한 부분을 유추해 보면, 스스로 동작하게 하는 기능들, 환경을 인지해서 필요한 동작을 판단하게 할 수 있는 센서들이 필요하다. 전자는 셀레늄 같은 자동화 모듈의 API 들과, 파이썬, 자바 같은 범용적인 언어의 프로그램 로직으로서 커버되고, 두번째는 순수하게 자동화 모듈에서 제공하는 여러가지 센서와 동작 API 로 이루어지게 된다. 아래는 지금까지 얘기한 내용을 도시한 것이다. 

 

 

 

 

[자동화의 종류]

  개인적인 의견으로 자동화는 몇가지 타입으로 나누어 볼수 있다.

 

  1 번째는 가장 원초적인 '화면 좌표를 기준으로 한 자동화' 이다. 온라인 RPG 게임의 레벨업, 스킬업 노가다 같은 부분을 macro express 나, auto mouse 같은 프로그램으로 자동화 하는 것 같은 작업이다. 특정 프로그램 창 내의 특정한 위치를 반복적으로 클릭한다든지, 키보드 이벤트 명령을 이용해서 원하는 키를 입력한다든지, 화면의 특정한 좌표의 칼라를 기준으로 판단하여 캐릭 및 몹들의 위치를 판단하여 특정 이벤트를 일으킨다는지 하는 부분 말이다. 이런 방식의 단점은 UI의 변경이 일어나면, 화면 배치상의 좌표와 칼라 등이 달라질 가능성이 높기 때문에, 그에 따라 전체적인 자동화 코드의 수정이 필요해 질 수도 있다는 것이다. 다만 많은 변경이 없거나 변경 주기가 긴 프로그램등을 대상으로 했을 때는 적절히 정확한 동작을 보여주게 된다. 게임 패킷 등을 직접 조작하여 자동화를 구성하는 경우도 있는 것은 같지만, 그 부분은 일반적인 자동화의 영역이라기 보다는 해킹 작업에 가까운것 같다.

 

 

  2 번째는 셀레늄과 같은 자동화 모듈을 이용하는 '웹 자동화' 이다. 웹 자동화의 구현은 이전에 소개한 html, xml 파싱 모듈인 beautifulsoup 의 구현 방식과 많이 유사하다. 해당 페이지 내의 태그를 인지하거나, 원하는 값을 가져오는 방식은 거의 비슷하고, 추가적으로 특정한 행동을 해당 태그 개체에 일으킬 수 있다(텍스트 박스에 검색어를 넣거나, 특정 버튼을 누른다든지 - 실제 사람이 해당 버튼을 누를때 브라우저에서 일어나는 이벤트와 비슷하게 구현한다고 보면 될듯 하다). 또한 브라우저와 상호 작용하여 움직이기 때문에, 사람이 실제 사용하는 것과 거의 비슷하게 돌아간다고 볼 수 있어, UI 자동화 테스팅 같은 영역에서 시작하게 되었다. 

 

  좌표 방식의 자동화와는 달리 UI 가 조금 달라진다고 해도, 내부 페이지의 속성 등만 잘 유지되면, 코드의 수정이 필요 없을 가능성이 높은 반면, 화면의 모양은 같아도 내부 코드 구조가 달라져 안 돌아 갈수도 있다. 또 웹의 특성상 네트워크나 시스템 부하에 따라 응답 속도 차이나, 타 어플리케이션 또는 사용자의 마우스, 키보드 사용에 의한 이벤트 방해 등으로 동작이 멈춰버리는 등 미묘하고 골치아픈 문제들을 종종 만날 수 있다. 또한 호환이 안되는 웹 확장 컴포넌트나 플래시 컴포넌트 등이 자동화의 대상일 경우에는 표준 적인 접근이 안되어 최후의 수단인 좌표로 해결할 수 밖에 없게되서, 좌표 기준 구현과 비슷한 레벨의 유지보수 문제가 생길수도 있다. 또 단순한 숫자 입력을 벗어나 점점 어려워지고 있는 캡챠 화면이나 은행에서 사용하는 랜덤한 번호키, 보안 프로그램 등과 연관되어 인식이 힘든 문제가 발생할 수 있다. 실제 화면을 봐야될 필요가 없는 경우는 뒤에 소개할 PhantomJS 같이 화면이 표시되지 않는 브라우저를 이용하는 방식도 있는 듯 하다.

 

 

  3 번째는 '윈도우즈 자동화' 이다. 윈도우즈 자동화도 어찌보면 웹 자동화와 거의 비슷한데, 근본적인 차이라면 웹 자동화에서 웹의 여러 요소(태그, 어트리뷰트, DOM 구조)를 기준으로 인식하던 부분을, 윈도우 창들의 여러 특성(타이틀, 캡션, 클래스 등등)을 기준으로 인식한다는 점만 틀리다. 아무래도 브라우저에 비해서 응답 지연에 의한 타이밍 문제가 생길 가능성이 낮으나, 비슷한 통신을 하는 FTP 프로그램의 자동화와 같이, 껍데기는 앱 형태긴 하지만 실제 뒷단에서서의 실제적인 동작은 서버 클라이언트 통신인 경우엔 비슷하거나 더 어려운 문제가 발생할 수도 있다.

 

 

  4 번째는 가장 범용적인 부분으로 '작업의 자동화' 이다. 소스를 특별한 주기로 백업 및 압축하여 FTP, NAS 등에 올린다든지 하는 수동으로 하다보면 잊어버릴 수도 있고, 귀찮은 작업들을 해결하는 부분이다. 공장 자동화도 넓게보면 같은 영역에 포함될 것이다. 해당 부분은 1, 2, 3 번의 자동화와 연동 하여 동작할 수도 있고, OS 명령어나, 7-zip 과 같은 외부 프로그램, 외부 API, 다른 여러가지 모듈들을 조합하여 동작 될 수 있다. 4번 영역은 만들때는 귀찮거나 난이도가 어려울 순 있지만, 한번 만들어 놓으면 특별한 변경 작업 없이 계속 잘 돌아가 자동화에 대한 ROI 가 잘 증명되는 영역인 것 같긴하다. 어찌보면 머신러닝이나 딥러닝 같은 분야도 약간 고급 버전의 데이터 기반의 행동 자동화라고 봐도 되지 않을까 싶다. 결론적으로 처음에 얘기한 것 같이 우리가 PC나 스마트폰 등의 OS 상에서 행하는 모든 작업은, 또 어쩌면 우리의 사고 조차도 자동화의 요소들을 어느 정도 담고 있다고 생각한다.

 

 

  뭐 이외에도 성능 테스트 툴 등에서 쓰는 URL 기준의 자동화나, 유닛 테스트 등 포함되지 않은 다른 영역이 있을진 모르지만, 나름 4가지 분류에 대충 일반적인 자동화는 언급 되었다고 생각한다. 괜찮은 느낌을 받은 아래 쿠키런 자동화 슬라이드를 봐보자.
https://speakerdeck.com/sgonv/pythoneuro-kukireon-unyeonghagi

 

 

 

 

[Selenium 개론]

  그럼 파이썬 웹 자동화 이야기에서 단골로 등장하는 셀레늄(selenium)이란 모듈은 뭘까? 개인적으로 7~8년 전 쯤에 테스트 자동화 업무에 조금 발 담그고 있는 편이였는데, 그때는 셀레늄 같은 오픈 소스 보다는 QTP(Quick Test Professional - 현재는 unified functional testing 으로 제품명 변경)나 SilkTest 같은 상용 자동화 솔루션을 사용해서 많이 진행 했었다. 그 당시의 셀레늄은 거의 java 베이스로 사용하는게 대세였고, 딱히 브라우저 호환성도 적절하진 않아서, 내장된 자바스크립트 베이스의 API 를 포기하고 webdriver 라는 현재 통합된 오픈소스 모듈과 통합을 진행 하던 시기였다. 그때는 사실 해당 모듈을 지금 같은 일반적인 웹 접근 용도로 쓴다는 것은 상상을 못했었고, 상용툴 만큼 잘 동작 하지 못해서 아쉬울 뿐 이였다. 그런 연유로 python 공부를 하면서 다시 selenium 이라는 툴을 만나게 되서 많이 반가웠다. 셀레늄 변화에 대한 자세한 히스토리는 아래의 링크를 참조한다.    

http://www.seleniumhq.org/docs/01_introducing_selenium.jsp#brief-history-of-the-selenium-project

 

  현재 셀레늄은 우리나라 에서 주로 테스트 자동화 툴로 소개되거나, 크롤링 중 특정 문제(로그인)에 대해서 PhantomJS 브라우저 등을 이용하여 해결하는 용도로 소개되는 듯 하지만, 조금 더 범용적으로 접근하면 브라우저에서 반복적으로 하는 일을 자동화 할 수 있는 킬러 툴이 되는것 같다. 윈도우즈 쪽 자동화 모듈은 아직 살펴 보지 못했지만, 괜찮은 윈도우즈 자동화 모듈이 있다면 셀레늄과 조합해서 파이썬을 통해 사용하면 좋은 시너지가 날 듯 싶다.

 

 

 

  그럼 셀레늄 코드를 만들기 전에, 셀레늄을 어떻게 바라보면 좋은지에 대해서 간단히 얘기하려 한다(개인적으로 이렇게 생각하는 거고, 각자의 뷰에 따라 다르게 해석해도 괜찮다). 셀레늄은 크게 3개의 부분으로 나눠진다고 보면 된다. 

 

  1 번째는 원래 의도된 용도였던 UI 자동화 테스트를 위해서 Testcase 및 Testsuite(테스트 케이스를 그룹 지은 것) 관리 기능이 있다. UI 자동화 테스트를 제작하는 사람들은 해당 라이브러리들을 이용하여 케이스를 관리하고 실행한다. NUnit 프레임윅과 같은 역활이라고 보면 된다.

 

 

  2 번째는 검증(verification) 부분이다. 실행 결과로 나온 값이 예상했던 값과 같은지 체크하는 것이다. 1, 2 번 기능은 테스팅 영역에 가까우며 관련된 문서 경로는 아래와 같다.(단 해당 기능을 잘 이해 한다면 자동화 작업들을 관리하고 검증하는데 잘 사용할 수도 있을 것이다)

http://www.seleniumhq.org/docs/

 

 

  3 번째인 웹 드라이버(webdriver) 부분이 파이썬 자동화에서 보통 관심을 가지고 이용하는 부분이다. 웹 드라이버는 위에서 설명한 센서 API 및 기능 API 의 집합이라고 보면 되며, 전반적인 사용법은 이전 시간에 소개했던 beautifulsoup 과 유사하다(문법이 같다라기 보다 개념이 같다). 이 얘기는 결국 웹 드라이브를 잘 쓰려면, 웹 구조에 대해서 전반적으로 잘 이해하고 있어야 한다는 말이다. 웹 드라이버는 윈도우 장치 드라이버랑 유사하게, 브라우저에 상관없이 같은 코드로 돌아가게 되어있다. 즉 최초 웹 드라이버를 불러오는 부분에서 특정 브라우저를 지정하면 이후 나머지 코드는 (이상적으로는) 공통 적으로 사용할 수 있다. 윈도우에서 웹 드라이브는 보통 exe 형태의 실행파일로 제공되어 셀레늄과 브라우저를 연결 해주는 역활을 한다. 아래의 코드와 같이 초기 브라우저 지정만 선언하면 뒤의 코드는 모두 공통되게 돌아갈 수 있다는 개념이다. 그래서 web 'driver' 라고 하나 보다.

1
2
3
4
5
6
7
8
9
#-*- coding: utf-8 -*-
from selenium import webdriver
browser = webdriver.Firefox()
#browser = webdriver.PhantomJS()
#browser = webdriver.Ie()
 
type(browser)
 
browser.get("https://www.google.com")
cs

 

 

  4 번째로 이러한 자동화 라이브러리 들을 사용해 호출하는 언어가 파이썬 이다. 파이썬은 문법 요소를 기술할 뿐만 아니라, 자동화 모듈 이외의 다른 여러가지 유틸 모듈들을 조합하여, 자동화 구성을 풍성하게 만들어주는 기능 API 역활도 지원하게 된다(이건 나중에 얘기하겠지만 머신러닝에서도 같은 역활을 한다). 결국 셀레늄이라는 것은 자동화를 위한 라이브러리 묶음이다. 조금 더 공식적으로 얘기하면 UI 자동화 테스팅용 프레임윅이라고 볼수 있다. firefox 에서 selenium IDE 를 설치하게 되면 사용자의 액션을 자동으로 레코딩 하는 기능도 가지고 있는데, 큰 기대는 하지말고, 초기 코드를 만드는 용도로 사용해도 된다. 이번 시간에는 수동으로 코드를 만들며 레코딩 기능은 아래의 블로그를 참조하자. 

http://jeen.tistory.com/entry/Selenium-%EC%9B%B9%EC%84%9C%EB%B9%84%EC%8A%A4-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%9E%90%EB%8F%99%ED%99%94%EC%99%80%EB%8A%94-%EB%B3%84-%EC%83%81%EA%B4%80%EC%97%86%EB%8A%94-%EC%9E%91%EC%97%85%EC%9D%84-%EC%9C%84%ED%95%B4

 

 

  위에서 얘기한 그림을 정리해 보면 아래와 같다.

 

 

 

 

 

[Firefox 를 이용한 구글 검색 후 Top 5 링크 새 탭에 열기]

 

  먼저 가장 셀레늄하고 궁합이 잘 맞는다고 여겨지는 파이어폭스 브라우저를 설치해서 자동화 코드를 만들어 보려 한다.  만들려는 기능은 첨에도 얘기했지만, "구글에서 특정 키워드를 검색하여, 상위 5개의 검색 결과의 링크를 새로운 탭들을 열어 각각 로딩" 하려고 한다. 먼저 우리가 해야하거나 모르는 작업들을 나열해 보자

  1) 파이어 폭스 설치

  2) 셀레늄 설치

  3) 파이썬에서 셀레늄을 이용해 동작하는 샘플 검증

  4) 구글을 열어 검색어 날리기

  5) 검색 결과 링크 중 상위 5개를 얻기

  6) 브라우저에 탭을 열어서 얻은 링크 5개를 각각 로딩하기

 

 

 

[Firefox 설치하기]

  먼저 firefox 를 설치해 보자. 구글에서 'firefox' 라고 검색해 본다. 제일 위의 파이어 폭스 링크로 이동한다.

https://www.mozilla.org/ko/firefox/new/

 

 

  '무료 다운로드' 버튼를 클릭하여 설치를 진행한다. 설치 진행 중 아래의 '가져오기 마법사' 항목이 나오면 굳이 IE 설정을 가져올 필요는 없으니 '아무 것도 가져오기 않기' 를 선택한다.

 

 

  다음에 "기본 브라우저" 설정도 굳이 취향이 아님 firefox 를 기본 브라우저로 쓸 필요는 없으니, 시작할 때마다 확인 체크 박스를 풀고, '나중에' 버튼을 누른다. 그럼 firefox 설치는 완료됬다.

 

 

 

[셀레늄 설치하기]

  다음은 셀레늄 설치 이다. pip 설치가 가능한듯 하니 아래와 같이 pip 설치를 시도해 본다. 정상적으로 설치가 된다.

c:\Python\code>pip install selenium
Collecting selenium
  Downloading selenium-3.3.1-py2.py3-none-any.whl (930kB)
    100% |################################| 931kB 297kB/s
Installing collected packages: selenium
Successfully installed selenium-3.3.1

 

 

 

[샘플 코드 구현]

  그럼 셀레늄을 이용해서 firefox 를 한번 호출해 보자. 구글에서 'python selenium firefox' 라고 검색해서, 맨 위의 공식 문서의 샘플을 참고하여 본다.

http://selenium-python.readthedocs.io/getting-started.html 

 

  해당 예제는 firefox 로 python.org 페이지를 열고 검색어를 입력해서 페이지가 잘 열렸는지 검증을 한다. 전체 코드는 굳이 필요 없으니 python.org 페이지를 여는 부분만 잘라서 가져오자. 호출하는 사이트도 구글로 바꾼다.

1
2
3
4
5
6
#-*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
 
driver = webdriver.Firefox()
driver.get("https://www.google.com")
cs

 

  c:\python\code 폴더에 selenium_1st.py 라고 저장하고 실행을 해본다(2교시 참고)

c:\Python\code>python selenium_1st.py
Traceback (most recent call last):
... 생략

os.path.basename(self.path), self.start_error_message)
selenium.common.exceptions.WebDriverException: Message: 'geckodriver' executable needs to be in PATH.

 

 

  그런데 불행하게도 에러가 난다. geckodriver 라는 파일이 패스 경로에 없다고 한다. 해당 파일은 위에서 얘기한 webdriver 기능을 사용하기 위한 exe 형태의 실행 파일이 없어서이다. 구글에 'Message: 'geckodriver' executable needs to be in PATH.' 라고 검색한다. 아래 스택 오버 플로우 링크를 보면 파일을 다운받아 path 가 지정된 경로에 복사하라는 안내가 있다. 리눅스 기준 설명이지만, 해당 파일이 결국 path 경로 안에만 들어있음 셀레늄에서 알아서 참조해 실행 하나 보다.

http://stackoverflow.com/questions/40208051/selenium-using-python-geckodriver-executable-needs-to-be-in-path

 

this steps SOLVED for me on ubuntu firefox 50.

1.Download geckodriver
2.Copy geckodriver in /usr/local/bin

 

 

  링크된 깃허브 경로로 이동하여(https://github.com/mozilla/geckodriver/releases) 제공된 여러 파일 중 현재 환경(윈도우10, 64bit)에 맞는 'geckodriver-v0.15.0-win64.zip' 파일을 다운 받는다. 해당 압축 파일안에는 'geckodriver.exe' 파일이 하나 달랑 들어 있는데, 뭐 특정 폴더를 만들어 넣고 시스템 path 등을(path 에 대해선 파이썬 설치하는 부분에서 설명했다) 걸거나, 기존 패스가 지정되 있는 c:\windows\system32 폴더 같은데 복사해도 되지만, 일을 단순하게 만들기 위해서 파이썬 소스가 있는 경로인 c:\python\code 폴더에다 압축을 푼다.

 

  이후 아까 에러난 코드를 다시 실행해 본다.

c:\Python\code>python selenium_1st.py

 

  조금은 신기하게도 아래와 같이 파이어 폭스가 뜬 후 구글 페이지를 로딩해 준다.

 

 

 

 

 

[구글 검색해 상위 5개 링크 가져오기]

  그럼 구글에서 상위 5개 링크를 가져오는 코드를 만들어 보자. 검색 결과에서 링크를 얻기 위해서 구글에 'selenium get google results python' 라고 검색 하자. 아래의 스택 오버플로우 코드를 참고하면, 결과 중 첫번째 항목(results[0])을 참고하여 링크를 가져온다.

http://stackoverflow.com/questions/35241230/how-to-extract-a-google-links-href-from-search-results-with-selenium
1
2
3
4
5
6
7
8
9
10
11
from selenium import webdriver
 
driver = webdriver.PhantomJS()
driver.get("https://www.google.com/search?q=test")
 
results = driver.find_elements_by_css_selector('div.g')
link = results[0].find_element_by_tag_name("a")
href = link.get_attribute("href")
 
import urlparse
print(urlparse.parse_qs(urlparse.urlparse(href).query)["q"])
cs

 

 

 

  해당 코드를 firefox 웹 드라이브를 사용하도록 앞에서 만든, selenium_1st.py 파일과 적절히 합치면, 아래의 코드가 나온다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#-*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
 
# 파이어 폭스 드라이버 로딩
driver = webdriver.Firefox()
 
# 구글 페이지에서 '파이썬 공부' 라고 검색해 옴.
driver.get("https://www.google.com/search?q=파이썬 공부")
 
# 검색 된 div 태그 안에 들은 링크 들을 가져옴
results = driver.find_elements_by_css_selector('div.g')
 
# 그중 첫번째 링크 안에서 a 태그를 찾음
link = results[0].find_element_by_tag_name("a")
 
# a태그 안에서 href 라는 인자를 가져옴 
href = link.get_attribute("href")
 
# 인자안에서 q 에 해당되는 값을 가져옴.
import urlparse
print(urlparse.parse_qs(urlparse.urlparse(href).query)["url"])
cs

 

 

  위의 링크를 가져오는 코드의 내용을 이해하기 위해서, IE의 개발자 도구 기능을 한번 사용해 보도록 하자(선호하는 다른 브라우저의 개발자 도구를 이용해도 된다). 아래의 화면에 있는 대로 구글에서 '파이썬 공부' 라고 검색 후,  첫번째 링크를 기준으로 요소 검사를 해서, 위에 있는 python 코드와 개발자 도구에서 보여주는 소스코드 내용을 비교해 놓았다. 자세히 보면 class 속성이 'g' 인 'div' 태그들을 찾은 후(페이지 전체를 분석해 보면 이 'g' 속성을 가진 'div' 태그가 각 링크 결과를 하나씩 감싸고 있을 것이다), 이후 해당 'div' 태그 안에서 다시 'a' 태그를 찾은 후, 이후 찾은 'a' 태그 안에서 다시 'href' 속성을 찾은 후 해당 url 형태의 결과 값에 대해 'urlparse' 라는 라이브러리를 이용하여, 'url' 이라는 인자 요소만 추출하게 되는 구조이다. 그 추출된 값이 바로 실제 링크된 페이지인 'http://ngee.tistory.com/263' 일 것이다. (개발자 도구를 사용하는 방법은 11교시에 설명했다)

 

  그럼 해당 파일을 c:\python\code 폴더에 selenium_2nd.py 로 저장하여 실행해 본다.

c:\Python\code>python selenium_2nd.py
  File "selenium_2nd.py", line 8
SyntaxError: (unicode error) 'utf-8' codec can't decode byte 0xc6 in position 0: invalid continuation byte

 

 

  근데 예전에 whois api 구현하면서 한번 본 적이 있는 유니코드 에러가 난다. 구글 호출 인자에 한글 검색어('파이썬 공부')를 명시한 부분 때문에 그런가 보다. 그 때 해결했던 것과 동일하게 파일을 다른 이름으로 저장 하면서 utf-8 형식으로 저장한다. 이후 다시 실행 한다.  

c:\Python\code>python selenium_2nd.py
Traceback (most recent call last):
  File "selenium_2nd.py", line 16, in <module>
    import urlparse
ImportError: No module named 'urlparse'

 

 

  이젠 구글 페이지까진 잘 뜨는데 urlparse 라는 모듈이 없다고 에러가 난다. 아마도 코드가 파이썬 2.x 대 기준이라 그런 듯 싶다. 파이썬 3 에서 사용하는 url parsing 방식을 찾기 위해서 구글에 'get some argument from url urlparse python' 이라고 찾는다. 첫번째 스택오버 플로우 페이지를 보는데, 아래와 같은 눈에 띄는 항목이 있다.

http://stackoverflow.com/questions/5074803/retrieving-parameters-from-a-url

 

There is a new library called furl. I find this library to be most pythonic for doing url algebra. To install:
pip install furl

 

Code:
from furl import furl
f = furl("/abc?def='ghi'")
print f.args['def']

 

 

  코드를 보니 전체 적인 기능은 어떨지 모르지만 현재 사용하려는 목적으로는 urlparse 보다 간략해 보인다. 그럼 위에 적힌 데로, 모듈을 설치한다.

c:\Python\code>pip install furl
Collecting furl
  Downloading furl-0.5.7.tar.gz
  Running setup.py install for furl ... done
Successfully installed furl-0.5.7 orderedmultidict-0.7.11

 

  정상적으로 설치가 됬다. 그럼 해당 코드를 이용해서 아래와 같이 기존 코드를 수정하여 다시 실행해 본다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#-*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
 
# 파이어 폭스 드라이버 로딩
driver = webdriver.Firefox()
 
# 구글 페이지에서 '파이썬 공부' 라고 검색해 옴.
driver.get("https://www.google.com/search?q=파이썬 공부")
 
# 검색 된 div 태그 안에 들은 링크 들을 가져옴
results = driver.find_elements_by_css_selector('div.g')
 
# 그중 첫번째 링크 안에서 a 태그를 찾음
link = results[0].find_element_by_tag_name("a")
 
# a태그 안에서 href 라는 인자를 가져옴 
href = link.get_attribute("href")
 
# 인자안에서 q 에 해당되는 값을 가져옴.
from furl import furl
= furl(href) 
print (f.args['url'])
cs

 

c:\Python\code>python selenium_2nd.py
Traceback (most recent call last):
... 생략
    raise KeyError(key)
KeyError: 'url'

 

 

  그런데 이상하게도, 아까 개발자 도구상으로는 분명이 존재했던 'url' 인자가 없다고 나온다. 왜 그런지 원인을 찾기 위해서 아래와 같이 소스를 수정해 href 값을 화면에 출력해 본다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#-*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
 
# 파이어 폭스 드라이버 로딩
driver = webdriver.Firefox()
 
# 구글 페이지에서 '파이썬 공부' 라고 검색해 옴.
driver.get("https://www.google.com/search?q=파이썬 공부")
 
# 검색 된 div 태그 안에 들은 링크 들을 가져옴
results = driver.find_elements_by_css_selector('div.g')
 
# 그중 첫번째 링크 안에서 a 태그를 찾음
link = results[0].find_element_by_tag_name("a")
 
# a태그 안에서 href 라는 인자를 가져옴 
href = link.get_attribute("href")
 
# 프린트 해봄
print(href)
cs

 

c:\Python\code>python selenium_2nd.py
http://ngee.tistory.com/263

 

 

  헐 근데 특이 하게도, 개발자 도구로 소스상에서 본 href 는 분명 '/url?.........' 하는 구글을 기준으로 한 긴 링크 문장이였는데, 실제 firefox 웹드라이브에서 참조해 온 값은 이미 해당 url 이 실행되어 해석된, 최종 url 경로를 가져오게 된다(나중에 PhantomJS 에서도 보겠지만 거기는 또 가져오는 값이 조금 틀리다). 이 부분이 예전의 beautifulsoup 을 사용할 때와 틀린 부분 중 하나이다. 해당 beautifulsoup은 정적인 페이지를 대상으로 했기 때문에, 항상 소스에 기반해 일정한 값이 나오지만, 웹 드라이브의 경우 드라이브를 만든 곳에 따라서(ie, firefox, chrome, phantomjs) 구현 방식이 틀릴 수 있기 때문에, 각각 다른 동적인 시점에 따라 상이한 결과 값을 리턴해 줄수도 있다. 이게 어찌보면 'one source multi use' 를 지향하는 셀레늄의 현실적인 한계 부분 일지도 모른다(웹 드라이버는 만드는 단체가 각각 다를 수 있기 때문에 입출력 인터페이스는 동일 하지만 실제 얻어오는 값이나, 동작에는 조금씩 차이가 날 수 있다. 브라우저 특성도 영향을 줄 것이고 말이다). 여튼 이렇게 되면 굳이 설치한 furl 을 이용해서 한번더 주소를 가져올 필요가 없어진다. (나중에 PhantomJS 에 사용할 테니 굳이 삭제 하진 말길...)

 

 

 

[새로운 탭에 링크 URL 로딩하기]

  여튼 그럼 마지막으로 새로운 탭을 열어 특정 웹 주소를 여는 방식을 알아보자. 구글에 'selenium open link in new tab python' 이라고 검색한다. 아래 스택오버플로우 페이지를 보면 ctrl+t 키를 눌러서 새탭을 열고, 웹 주소를 해당 탭에 로딩하는 코드가 있는데, 마지막에 간단한 코드가 하나 눈에 뜨인다. 

http://stackoverflow.com/questions/28431765/open-web-in-new-tab-selenium-python

 

browser.execute_script('''window.open("http://bings.com","_blank");''')

 

  위의 코드가 정상적으로 돌아 간다면 좋을 것 같다(확인 결과 다행히 firefox 는 기본 동작 옵션이 _blank 로 새창을 열면 새 탭으로 열어준다. 넘 시행 착오 코드가 많아서 줄임 차원에서...). 실제 최종 확인 결과 어떤 사유인지는 모르지만 최근 브라우저 들에서는 아래 제시된 예제들과 같이 웹드라이버에서 ctrl + t 키를 body 태그에 날려도 새 탭이 안 생기는 거 같다. 그래서 결국 위의 코드를 쓰게 되었다.

https://gist.github.com/lrhache/7686903

 

 

 

[최종 코드 완성하기]

   앞에서 해결된 코드들을 합쳐, 제어 로직을 추가하여 최종 완성된 코드는 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#-*- coding: utf-8 -*-
from selenium import webdriver
 
# firefox 웹 드라이버를 연다 
browser = webdriver.Firefox()
 
# 구글에 '파이썬 공부'로 검색어 조회
browser.get("https://www.google.com/search?q=파이썬 공부")
 
# 결과 div 태그를 클래스 기준으로 검색해 다 가져옴.
results = browser.find_elements_by_css_selector('div.g')
 
# 인자를 담을 리스트
hrefs = []
 
# div 중 최초 5개를 가져와서, 그 안에서 a 태그를 찾고, a 태그 안의 href 속성을 찾는다. 
for i in range(05):
   link = results[i].find_element_by_tag_name("a")
   hrefs.append(link.get_attribute("href"))
 
# 화면에 그냥 프린트 해봄
for href in hrefs:
   print(href)
 
# 각 링크에 대해서 새 탭에서 연다
for href in hrefs:
   browser.execute_script('window.open("' + href + '","_blank");')
cs

 

  그럼 해당 파일을 c:\python\code 폴더에 selenium_firefox.py 로 저장하여 실행해 본다.

c:\Python\code>python selenium_firefox.py
http://ngee.tistory.com/263
https://nolboo.kim/blog/2014/08/10/the-best-way-to-learn-python/
http://analyticsstory.com/1
http://www.boxnwhis.kr/2016/03/25/how_to_be_a_developer_as_a_statistician.html
https://wikidocs.net/43

 

  아래와 같이 파이어 폭스 브라우저가 뜨면서 5개의 결과가 탭들에 담긴다. 

 

 

 

 

 

 

 

[팬텀JS(PhantomJS) 브라우저로 같은 작업 하기]

  위에 firefox 웹 드라이버로 했던 부분을 비교해보는 의미가 있을 것 같아서, 이번엔 PhantomJS 브라우저를 이용해 해보려고 한다. PhantomJS 는 화면에는 없고 메모리 상에만 존재하는 브라우저 같다. 웃긴건 스크린 캡쳐는 가능하다(다행히 이거라도 있어서 에러가 발생할 때 확인이 수월하다). 대신에 실제로 브라우저가 화면 상에 표시되어 움직이지 않기 때문에, 자동화 코드를 실행하면서도 비교적 해당 컴퓨터에서 다른 작업을 자유롭게 할수 있을 것 같고(기존 브라우저 베이스로 자동화 코드를 실행해 본 사람들은, 해당 실행이 끝나기 전에 컴퓨터를 사용 못하는 경험을 겪어봤을 것이다), 실제 화면에 모든 개체를 표시되지 않기 때문에 부하나 외부 환경에 대한 에러 요소도 좀 적을 것 같다. 단점은 해당 브라우저로 만든 코드가 다른 브라우저와 호환성을 100% 보장한다고는 할수 없을 것 같다. webkit 베이스 이기 때문에 사파리나 스마트폰 용 브라우저와는 어느정도 호환성이 있다고 볼 수 있을 거 같다.

 

 

[샘플 코드 구현]

  그럼 구글에 'python use PhantomJS' 라고 검색한다. 아래의 스택 오버플로우 페이지를 확인해 보면, 다른 부분은 동일하고, 처음에 웹 드라이브 정의 하는 부분만 아래와 같이 바꾸면 되는 것 같아 보인다.

http://stackoverflow.com/questions/13287490/is-there-a-way-to-use-phantomjs-in-python

 

1
driver = webdriver.PhantomJS()
cs

 

 

  그럼 해당 방식으로 아까 만든 코드를 가져와 실행해 본다. 탭을 띄우는 부분만 굳이 의미 없을것 같으니 제외 시키고, url 만 프린트 하고 종료되게 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#-*- coding: utf-8 -*-
from selenium import webdriver
 
# firefox 웹 드라이버를 연다 
browser = webdriver.PhantomJS()
 
# 구글에 '파이썬 공부'로 검색어 조회
browser.get("https://www.google.com/search?q=파이썬 공부")
 
# 결과 div 태그를 클래스 기준으로 검색해 다 가져옴.
results = browser.find_elements_by_css_selector('div.g')
 
# 인자를 담을 리스트
hrefs = []
 
# div 중 최초 5개를 가져와서, 그 안에서 a 태그를 찾고, a 태그 안의 href 속성을 찾는다. 
for i in range(05):
   link = results[i].find_element_by_tag_name("a")
   hrefs.append(link.get_attribute("href"))
 
# 화면에 그냥 프린트 해봄
for href in hrefs:
   print(href)
cs

 

  그럼 해당 파일을 c:\python\code 폴더에 selenium_phantom.py 로 저장하여 실행해 본다.

c:\Python\code>python selenium_phantom.py

Traceback (most recent call last):  .....
      os.path.basename(self.path), self.start_error_message)
selenium.common.exceptions.WebDriverException: Message: 'phantomjs.exe' executable needs to be in PATH.

 

 

  역시 한번에 될리가 없고 무언가 에러가 난다. 하지만 이미 firefox 일때 웹드라이버 파일이 경로에 필요했던 부분을 겪어 봤으니, 이번엔 좀더 쉬울듯 하다. 구글에서 "Message: 'phantomjs' executable needs to be in PATH windows" 로 검색한다.

http://stackoverflow.com/questions/37903536/phantomjs-with-selenium-error-message-phantomjs-executable-needs-to-be-in-pa

 

  아래의 내용이 눈에 띈다

you need to download the DRIVER

after that session = webdriver.PhantomJS("c:\driverPath")

 

 

  해당 글에 명시된 http://phantomjs.org/ 페이지로 이동하여 'Download Ver 2.1(버전은 틀려질 수 있을 듯) 버튼을 클릭 하여, 'phantomjs-2.1.1-windows.zip' 파일을 다운 받는다. 이전같이 같은 폴더에 복사하거나, 또는 path가 지정되 있는 폴더에 복사하거나, 또는 특정 폴더에 복사후 시스템 path를 지정해도 좋지만, 조금 다른 방식을 보여주기 위해서 c:\python 폴더에 압축을 풀고(phantomjs-2.1.1-windows 폴더로 풀린다), Phantomjs 호출하는 코드 부분을 아래와 같이 조금 수정한다(어느 페이지에서 본 코드 인지는 잘 생각 안난다;).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#-*- coding: utf-8 -*-
from selenium import webdriver
 
# 수정된 코드 
phantomjs_path = r'c:\Python\phantomjs-2.1.1-windows\bin\phantomjs.exe'
browser = webdriver.PhantomJS(phantomjs_path)    
 
# 구글에 '파이썬 공부'로 검색어 조회
browser.get("https://www.google.com/search?q=파이썬 공부")
 
# 결과 div 태그를 클래스 기준으로 검색해 다 가져옴.
results = browser.find_elements_by_css_selector('div.g')
 
# 인자를 담을 리스트
hrefs = []
 
# div 중 최초 5개를 가져와서, 그 안에서 a 태그를 찾고, a 태그 안의 href 속성을 찾는다. 
for i in range(05):
   link = results[i].find_element_by_tag_name("a")
   hrefs.append(link.get_attribute("href"))
 
# 화면에 그냥 프린트 해봄
for href in hrefs:
   print(href)
cs

 

  해당 내용을 selenium_phantom.py 에 엎어쓰고 다시 실행해 본다.

c:\Python\code>python selenium_phantom.py
Traceback (most recent call last):
  File "selenium_phantom.py", line 19, in <module>
    link = results[i].find_element_by_tag_name("a")
IndexError: list index out of range

 

 

  ※ 아마 실행 전에 아래와 같은 윈도우즈 방화벽 허용 창이 뜰것이다. 허가되지 않은 프로그램이 외부랑 통신을 할까봐 그러는 것으로 같다. 실행시 필수이니 '엑세스 허용'하고, 다시 프로그램을 실행 시켜야 한다.  

 

 

 

[에러 원인 확인 하기]

  근데 브라우저 실행까진 잘 됬는데 엉뚱하게 'a' 태그가 없다고 나온다. 화면이 보이지도 않으니 어떤 일이 일어났는지 알수 없으니 에러가 나기 전 코드 위치에서 스크린 샷을 찍어 본다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#-*- coding: utf-8 -*-
from selenium import webdriver
 
# firefox 웹 드라이버를 연다 
phantomjs_path = r'c:\Python\phantomjs-2.1.1-windows\bin\phantomjs.exe'
browser = webdriver.PhantomJS(phantomjs_path)    
 
# 구글에 '파이썬 공부'로 검색어 조회
browser.get("https://www.google.com/search?q=파이썬 공부")
 
# 결과 div 태그를 클래스 기준으로 검색해 다 가져옴.
results = browser.find_elements_by_css_selector('div.g')
 
# 인자를 담을 리스트
hrefs = []
 
# 디버그용 스크린 샷
browser.save_screenshot('screen.png')
 
# div 중 최초 5개를 가져와서, 그 안에서 a 태그를 찾고, a 태그 안의 href 속성을 찾는다. 
for i in range(05):
   link = results[i].find_element_by_tag_name("a")
   hrefs.append(link.get_attribute("href"))
 
# 화면에 그냥 프린트 해봄
for href in hrefs:
   print(href)
cs

 

 

  소스와 같은 폴더에 저장된 screen.png 파일을 열어보니, 글자가 ??? ?? 나오는거 보면 PhantomJS 는 url 에 한글을 포함해 보내면 제대로 처리를 못해주는 것 같다. --;

 

 

  그럼 어떻게 내부에서 인코딩을 처리하는지 모르는 상태에서(물론 홈 페이지에서 전체 소스를 제공해 주긴 하지만) 뭔가 인코딩 부분을 건드리긴 무섭고, url 에 넣는 방식이 아닌 한글 검색어를 입력 후 한글 검색어를 넣고 검색버튼을 누르는 방향으로 전환해 보며 한글 처리를 잘 하길 기대해 보자. 구글에 'selenium google search python' 라고 검색한다. 처음 링크로 나오는 github 페이지를 참고한다.

https://gist.github.com/azam-a/32b89944b98a3fd79d44ebfdac16b63d#file-google-py

http://stackoverflow.com/questions/24598648/searching-google-with-selenium-and-python

 

 

  해당 움직이는? 방식으로 코드를 수정하면 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#-*- coding: utf-8 -*-
from selenium import webdriver
 
# firefox 웹 드라이버를 연다 
phantomjs_path = r'c:\Python\phantomjs-2.1.1-windows\bin\phantomjs.exe'
browser = webdriver.PhantomJS(phantomjs_path)    
 
# 구글에 '파이썬 공부'로 검색어 조회
browser.get("https://www.google.com")
input_element = browser.find_element_by_name("q"
input_element.send_keys("파이썬 공부"
input_element.submit() 
 
 
# 결과 div 태그를 클래스 기준으로 검색해 다 가져옴.
results = browser.find_elements_by_css_selector('div.g')
 
# 인자를 담을 리스트
hrefs = []
 
# 디버그용 스크린 샷
# browser.save_screenshot('screen.png')
 
# div 중 최초 5개를 가져와서, 그 안에서 a 태그를 찾고, a 태그 안의 href 속성을 찾는다. 
for i in range(05):
   link = results[i].find_element_by_tag_name("a")
   hrefs.append(link.get_attribute("href"))
 
# 화면에 그냥 프린트 해봄
for href in hrefs:
   print(href)
cs

 

c:\Python\code>python selenium_phantom.py
https://www.google.co.kr/url?q=http://ngee.tistory.com/263&sa=U&ved=0ahUKEwi8sJba-_bSAhVEzbwKHQfuD5oQFggZMAA&usg=AFQjCNFNP7xOQT4RepCgxW-lSoAIucugwA
https://www.google.co.kr/url?q=https://nolboo.kim/blog/2014/08/10/the-best-way-to-learn-python/&sa=U&ved=0ahUKEwi8sJba-_bSAhVEzbwKHQfuD5oQFgggMAE&usg=AFQjCNFtcAIvPG3wUHOowvFHpgBjf7XQsw
.. 뒤에 3개는 생략

 

 

[웹 자동화의 타이밍 문제]

  흠 그런데 PhantomJS 코드를 처음 만들어 실행 했을 때 났던 에러가 지금은 안 나서 조금 당황스러운 상태다. --;

 

  어떤 일이 발생 할  수 있냐면, 코드 중에서 처음 find_elements_by_css_selector('div.g') 찾는 부분에서 원래 에러가 났었다. 그 이유는 browser.get() 함수는 아마 페이지 전체가 로딩될때 까지 대기 후, 다음 코드가 실행되지만, 위와 같이 submit 버튼으로 전송한 경우는 버튼을 누른 순간 코드가 끝났기 때문에 다음 코드가 실행 됬을때, 페이지가 아직 로딩 중일 수 있기 때문에, find_elements_by_css_selector('div.g') 코드에서 아직 페이지에 div.g 태그가 로딩되지 않아서 에러가 발생할 수 있다. 그래서 그것을 보여주며 타이밍 이슈에 대해서 얘기하려 했는데 재현이 안 된다. 보통 사용의 UI 자동화 솔루션들이 이러한 태그나 속성 요소를 찾는 함수에 기본으로 지정가능한 '대기시간'이 있어서, 해당 부분의 요소가 없더라도 '대기시간'동안 계속 해당 요소를 반복해서 찾아 웹페이지에 로딩에 의한 타이밍 에러를 줄여준다.

 

 

  셀레늄에도 그러한 타이밍 이슈를 위한 기능들이 있는데, 보통 2가지 방식으로 해결한다. 1 번째는 쉬는 시간을 무조건 지정하는 것이다. 하지만 경험상 시간을 충분히 주더라도 특정 경우에 타이밍 에러가 날 수도 있도,  또 이미 해당 부분이 로딩 됬더라도 무조건 기다리게 되어 실행 시간이 많이 걸리기 때문에 그다지 바람직한 방식은 아니다.  

 

  2 번째로 페이지 로딩이 충분이 됬는지를 특정 엘리먼트를 계속 확인하여 기다리는 것이다. 이 경우 해당 엘리먼트를 찾게되면 바로 다음 단계로 넘어가고, 아니면 지정된 시간이 지나서 에러를 내게 된다. 이런 코드를 만들 경우 현재 참조 하려는 태그나, 페이지에서 가장 마지막에 로딩 될것 같은 태그를 페이지 구조를 분석하여 분석하여 지정해야 한다. 이것 역시 쉬운 작업은 아니지만, 이렇게 구현하면 빠르고, 정확한 코드가 된다. 그런 면에서 상용 솔루션이 이런 부분에서 조금 더 유연성은 있을 것 같다.  아마 7년전의 지식 기준이니 지금은 좀 더 해당 부분이 강화되었을 것 같긴 한다. 두 가지 방식에 대한 상세한 얘기는 밑의 링크를 참고하고, 여기서는 쉽게 가려고 sleep 함수를 2초간 사용하려 한다. (firefox 같은 실제 브라우저를 사용하는 코드에서는 조금 더 시간을 주는게 좋다)

http://stackoverflow.com/questions/26566799/selenium-python-how-to-wait-until-the-page-is-loaded 

1
2
import time
time.sleep(2)
cs

 

 

  또 해당 결과에서 하나 걸리는 것은 firefox 와 비교되는 최종 url 을 얻는 부분인데, firefox 에서는 'href' 에 '최종 url' 이 나왔는데, PhantomJS 에서는 아래와 같이 정적 소스와도 좀 상이한(원본 소스는 'url' 인자에 링크가 있었는데 여기 'q' 인자임) 최종 url 이전의 링크가 나온다.

https://www.google.co.kr/url?q=http://ngee.tistory.com/263&sa=U&ved=0ahUKEwi8sJba-_bSAhVEzbwKHQfuD5oQFggZMAA&usg=AFQjCNFNP7xOQT4RepCgxW-lSoAIucugwA

 

  이 링크를 띄워도 최종 url 이 표시되긴 하지만, 굳이 구글을 거쳐서 실행 되는게 찜찜해서 firefox 와 동일하게 나오게 하기 위해서 아까 사용하려다 만 furl 을 사용하려 한다.  

 

Code:
from furl import furl
f = furl("/abc?def='ghi'")
print f.args['def']

 

 

 

[최종 코드]

  모듈 설치는 이미 했으니 해당 코드를 반영하여 최종 PhantomJS 코드를 만들면 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#-*- coding: utf-8 -*-
from selenium import webdriver
 
# firefox 웹 드라이버를 연다 
phantomjs_path = r'c:\Python\phantomjs-2.1.1-windows\bin\phantomjs.exe'
browser = webdriver.PhantomJS(phantomjs_path)    
 
# 구글에 '파이썬 공부'로 검색어 조회
browser.get("https://www.google.com")
input_element = browser.find_element_by_name("q"
input_element.send_keys("파이썬 공부"
input_element.submit() 
 
import time
time.sleep(2)
 
# 결과 div 태그를 클래스 기준으로 검색해 다 가져옴.
results = browser.find_elements_by_css_selector('div.g')
 
# 인자를 담을 리스트
hrefs = []
 
# 디버그용 스크린 샷
# browser.save_screenshot('screen.png')
 
# div 중 최초 5개를 가져와서, 그 안에서 a 태그를 찾고, a 태그 안의 href 속성을 찾는다. 
for i in range(05):
   link = results[i].find_element_by_tag_name("a")
   hrefs.append(link.get_attribute("href"))
 
# href 에서 q 인자를 추출해 옴   
from furl import furl
for href in hrefs:
   f = furl(href) 
   print (f.args['q'])
cs

 

  해당 내용을 selenium_phantom.py 에 엎어쓰고 마지막으로 실행해 본다. 원하는 결과가 나온다.

c:\Python\code>python selenium_phantom.py
http://ngee.tistory.com/263
https://nolboo.kim/blog/2014/08/10/the-best-way-to-learn-python/
... 뒤의 3개 생략

 

 

 

 

 

[부록 - IE (Internet explorer) 로 같은 작업 하기]

  마지막으로 자주 사용하는 IE 브라우저를 무시하긴 그래서 IE 코드도 하나 넣으려 한다. 결론 부터 말하자면 생각보다 실행을 위해 해야 될 귀찮은 일들이 좀 많고, 실행 해 보면 firefox 보다 상당히 느리게 동작한다(물 안에서 움직이는 느낌이라고 할까?). 그래서 가능한 동작을 꼭 IE 브라우저에서 할 필요가 없으면 firefox 나 해보진 않았지만 크롬에서 해보는게 어떨까 싶다(구글에서 webdriver 를 만드는 것 같으니 최적화는 잘되지 않았을까 싶어서). 아니면 화면을 꼭 봐야될 필요가 없다면 PhantomJS 도 괜찮고 말이다. 앞에서 같이 경험 목적으로 반복해서 시행착오 과정을 굳이 보여줄 필요는 없을 것 같아서, 실행에 필요한 세팅들을 안내 후, 실행 결과를 보여주고 마무리를 하려 한다.

 

 

[웹드라이버 세팅]

http://stackoverflow.com/questions/24925095/selenium-python-internet-explorer

http://selenium-release.storage.googleapis.com/2.42/IEDriverServer_x64_2.42.0.zip

  다운로드 후 c:\python 폴더에 c:\Python\IEDriverServer_x64_2.42.0\ 로 압축 해제를 하면 된다. 코드내에 경로를 반영하기가 귀찮다면 c:\python\code 에 직접 exe 파일을 복사후, browser = webdriver.Ie() 로 소스를 바꾸어 호출해도 된다.  

 

 

[IE 보안 설정]

  Unexpected error launching Internet Explorer. Protected Mode settings are not the same for all zones 에러 발생함.

 

  인터넷 옵션 > 보안 탭 > 아래 4개 영역에 대해서 보호 모드 사용이 꺼져 있어야 한다. 테스트 완료 후 인터넷 쪽은 꼭 원래대로 복원하길 바란다.

 

 

[IE 확대/축소 사이즈 설정]

  Message: Unexpected error launching Internet Explorer. Browser zoom level was set to 125%. It should be set to 100% 에러 발생.

 

  확대 설정이 100% 여야만 동작한다(IE 브라우저는 왜 이런 설정까지 맞춰야 하는지 싶다 --;)

 

 

[방화벽 설정]

  PhantomJS 와 비슷하게 방화벽 혀용을 묻는 창이 뜨면 허용을 해주고 다시 코드를 실행 해주어야 한다.

 

 

 

[최종 코드]

  세팅은 좀 달랐지만 최종 정리한 코드는 브라우저 호출 부분만 바뀌었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#-*- coding: utf-8 -*-
from selenium import webdriver
 
# IE 웹 드라이버를 연다 
ie_path = r'c:\Python\IEDriverServer_x64_2.42.0\IEDriverServer.exe'
browser = webdriver.Ie(ie_path)    
 
# 구글에 '파이썬 공부'로 검색어 조회
browser.get("https://www.google.com")
input_element = browser.find_element_by_name("q"
input_element.send_keys("파이썬 공부"
input_element.submit() 
 
# 시간 좀 늘임
import time
time.sleep(5)
 
# 결과 div 태그를 클래스 기준으로 검색해 다 가져옴.
results = browser.find_elements_by_css_selector('div.g')
 
# 인자를 담을 리스트
hrefs = []
 
# 디버그용 스크린 샷
# browser.save_screenshot('screen.png')
 
# div 중 최초 5개를 가져와서, 그 안에서 a 태그를 찾고, a 태그 안의 href 속성을 찾는다. 
for i in range(05):
   link = results[i].find_element_by_tag_name("a")
   hrefs.append(link.get_attribute("href"))
 
# 링크 출력 해보기 
for href in hrefs:
   print(href)
 
# 새 창에 띄움
for href in hrefs:
   browser.execute_script('window.open("' + href + '","_blank");')
cs

 

  selenium_ie.py 로 저장하여 실행하면 아래와 같이 새창으로 뜬다. (firefox 처럼 탭으로 뜨게 하고 싶으면, 현재 버전의 브라우저 들은 코드가 지원하는지 불분명하니 아래 설명한 바와 같이 브라우저 옵션을 바꾸는게 현실적일 것 같다 -> 밑에 코맨트 단 것과 같이 IE 웹 드라이버에서는 ctrl+t, ctrl+click, 팝업 옵션을 바꾸어도 모두 탭으로 열리진 않는다. 현재로서는 새창으로 밖에 안 열리는 것 같다는 결론을 내렸다.)

http://meaningone.tistory.com/52

c:\Python\code>python selenium_ie.py

 

 

 

  마지막으로 웹 자동화 연습용 페이지 링크를 소개한다(사실 실제 사이트 들에서 하면 되서 효율성은 잘 모르겠지만, 구조가 쉬워 연습할때 도움이 되지 않을까도 싶다). 다음 시간에는 윈도우즈 UI 자동화 부분에 대해서 간단한 프로그램 대상으로 진행해 보려 한다.

http://www.techbeamers.com/websites-to-practice-selenium-webdriver-online/

 

 

 

 

2017.3.29 by 자유로운설탕
cs

 

posted by 자유로운설탕