셀레니움 창 여러개 - sellenium chang yeoleogae

  Selenium을 주로 크롤링 할 때 사용하다보니 따로 다뤄볼 일이 없었다. 그러다가,PC를 켜면 항상 실행하고 로그인해야 하는 웹이 몇개 있는데 이 과정들을 자동화하기로 하였다. 가장 먼저 해야할 일은 웹을 한번에 여러개 실행할 수 있도록 탭을 여러개 띄우는 것이었다. 구글링을 하여 방법을 찾아봤는데 꽤 많은 방법들이 있었다. 그런데, 링크를 가진 요소를 찾아서 [ Ctrl ] + [ Mouse Click ] 이벤트를 발생시키거나, 버전이 안맞아서 그런지 정상적으로 동작하지 않는게 대부분이었다. 그래서, 좀 더 찾다가 가장 깔끔하다고 생각하는 방법을 소개하려고 한다.

1. 예제 소스

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

import selenium.webdriver.support.ui as ui

from selenium import webdriver

from selenium.webdriver.chrome.options import Options

from selenium.webdriver.remote.webelement import WebElement

from selenium.webdriver.common.keys import Keys

from selenium.webdriver import ActionChains

#chromedriver 경로 설정

CHROMEDRIVER_PATH = './chromedriver.exe'

chrome_options = Options()

chrome_options.add_argument('--start-maximized')

#브라우저 실행 및 탭 추가

driver = webdriver.Chrome( executable_path=CHROMEDRIVER_PATH, chrome_options=chrome_options )

driver.execute_script('window.open("about:blank", "_blank");')

driver.execute_script('window.open("about:blank", "_blank");')

tabs = driver.window_handles

# TAB_1

driver.switch_to_window(tabs[0])

driver.get('http://www.naver.com/')

# TAB_2

driver.switch_to_window(tabs[1])

driver.get('http://www.google.com/')

# TAB_3

driver.switch_to_window(tabs[2])

driver.get('https://heodolf.tistory.com/')

cs

     - 18~19 ln: driver.execute_script()를 이용하면 javascript를 실행할 수 있음.

        *여기서 새 탭을 생성하는 Javascript를 작성.  window.open("about:blank", "_blank");

     - 21 ln: driver.window_handles에는 현재 열려있는 탭 리스트가 있음.

     - 24 ln: driver.switch_to_window() 함수로 탭을 자유롭게 전환할 수 있음.

2. 실행 결과.

셀레니움 창 여러개 - sellenium chang yeoleogae

3. 마치며

   - 새로운 탭을 여러개 사용하는 방법에 대해서 찾아보다가, selenium으로 javascript 까지도 실행할 수 있다는 것을 알게되었다. 이걸 잘만 활용하면 유용하게 쓸 수 있을것 같다.

   - 그리고 Seleninum을 실행했을 때, 'Chrome이 자동화된 테스트 소프트웨어에 의해 제어되고 있습니다.' 이 문구가 거슬린다. 다음 포스트에서는 이 문구를 지우는 방법에 대해서 알아보도록 하겠다.

파이썬 multiprocessing 이용해서 selenium 창 여러개 띄워서 하는방법도 괜찬을거같아요 ㅋㅋㅋ 근데 여러 프로세스에서 동시에 하나의 엑셀파일에 접근할순 없을거 같으니, 엑셀파일 내용을 csv로 옮긴 다음에 각 셀에 index를 메기고 각각의 프로세스에서 index범위에 따라서 파싱을 해오는 방법도 괜찬을거같아요 그리고 selenium쓰면 브라우저 열고, 검색하고 이런과정을 직접 사용자가 하는 방식이랑 똑같이 하기때문에 resource도 많이 잡아먹고 속도가 상당히 느려요!! beautifulSoup+ request쓰면 바로 http request요청으로 (client-side 렌더링 제외 왠만한 경우면) 더빠르게 가져올수있는데 그것도 속도를 올릴수있는 괜찬은 방법인거같아요 ^^

크롤링을 병렬로 처리하고 싶어요. 셀레니움으로 webdriver를 사용하는데 멀티쓰레딩이 안 돼요!
pathos를 써도 안 돼요!

멀티프로세싱과 멀티쓰레딩이 다른 의미긴 하지만 multiprocessing 모듈을 사용하기 떄문에 제목에 멀티프로세싱이라고 적어두었다..

웹드라이버는 멀티쓰레딩을 지원하지 않기 때문에 안 된다. multiprocessing 모듈을 사용할수가 없다.

이 원인을 찾으려고 얼마나 뒤졌는지..ㅠㅠ

https://stackoverflow.com/questions/30808606/can-selenium-use-multi-threading-in-one-browser

Can Selenium use multi threading in one browser?

I want to test a web in multi threading but when I open too many chromedrivers they use too much memory. Can I use multi threading in one browser?

stackoverflow.com

셀레니움 창 여러개 - sellenium chang yeoleogae

해결법


프로세스마다 webdriver를 각각 생성해서 만들어준다.

한 브라우저에 탭을 이용하는게 아니고, 브라우저 여러개를 띄우면 된다.

from pathos.multiprocessing import ProcessingPool as Pool #pip install pathos


class Parser(Subject):
    def __init__(self):
        self.pool = Pool(processes=3)
        """웹드라이버가 여기에 있으면 오류가 난다! 웹드라이버는 싱글스레드라서!"""
		# self.driver = webdriver.Chrome('./chromedriver.exe')
        
	def open_browser(self, site):
    	""" process별로 브라우저를 따로 열어주면 오류가 안 난다 """
    	driver = webdriver.Chrome('./chromedriver.exe')
        driver.get(site)
        
    def multi_processing(self):
    	sites = ['https://www.naver.com', 'https://www.daum.net', 'https://www.tistory.com']
    	pool.map(open_browser, browsers)
        
        
        
 parser = Parser()
 parser.multi_processing()

pathos multiprocess를 쓰는 게 정신건강에 좋다.

일반 pool은 top-level method에만 쓸 수 있기 때문이다.

pathos는 다른 클래스 내 메소드, 같은 클래스 내 다른 메소드에도 쓸 수 있다!

안 되는 걸 되게 하는 pathos ↓

https://stackoverflow.com/questions/3288595/multiprocessing-how-to-use-pool-map-on-a-function-defined-in-a-class

Multiprocessing: How to use Pool.map on a function defined in a class?

When I run something like: from multiprocessing import Pool p = Pool(5) def f(x): return x*x p.map(f, [1,2,3]) it works fine. However, putting this as a function of a class: class calculate(

stackoverflow.com

셀레니움 창 여러개 - sellenium chang yeoleogae