NOMO.asia

Selenium 사용 중 페이지에 JavaScript 를 삽입하려다가…

Selenium 을 사용하다가 웹페이지 내에 JavaScript 를 삽입하고 싶은 경우가 생겼다. 내 경우에는 어떤 웹사이트에서 '더보기' 버튼을 눌러 내용을 계속 읽어오며 배열에 저장했는데, 내용이 많~이 쌓이면 느려지기 때문에 이미 내용을 읽은 element 는 없애버리고 싶었다.

이 외에도 나는 Selenium 과 관련된 라이브러리보다는 JavaScript 를 더 잘 쓰기 때문에, 적당히 섞어쓰면 코드 작성이 훨씬 편해질 것 같았다.

Selenium의 execute_script 함수를 쓰면 JavaScript 를 사용할 수 있기는 하지만 순수한 JavaScript 만 써야했다.

그래서 jQuery 를 사용하려고 <head>와 </head> 사이에 jQuery 를 동적으로 로드하는 스크립트를 삽입했다.

from selenium import webdriver
import time
import os

url = "https://play.google.com/store/apps/details?id=com.google.android.googlequicksearchbox"
CHROME_EXE_PATH = r'크롬 드라이버 파일 경로\chromedriver.exe'

# 브라우저 설정
options = webdriver.ChromeOptions()
driver = webdriver.Chrome(executable_path=CHROME_EXE_PATH, chrome_options=options)
driver.implicitly_wait(1)

# 크롤링 대상 링크 접속
driver.get(url)

# jquery load
jScript = """
  var script = document.createElement('script');
  script.type = "text/javascript";
  script.src = "https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js";
  document.getElementsByTagName('head')[0].appendChild(script);
"""
jScript2 = "console.log('jquery:', typeof jQuery);"
driver.execute_script(jScript)
time.sleep(1)
driver.execute_script(jScript2)

os.system("PAUSE")
driver.quit()


문제 발생

그런데 특정 사이트에서 이러한 짓을 시도하면 아래와 같은 경고가 떴다.

Refused to load the script 'https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js' because it violates the following Content Security Policy directive: "script-src 'report-sample' 'nonce-krsKb/RLOrlIehvKJO965Q' 'unsafe-inline'". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.

Refused to load the script 'https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js' because it violates the following Content Security Policy directive: "script-src 'nonce-krsKb/RLOrlIehvKJO965Q' 'self' 'unsafe-eval' https://apis.google.com https://ssl.gstatic.com https://www.google.com https://www.gstatic.com https://market.android.com https://www.google-analytics.com/analytics.js https://www.googleapis.com/appsmarket/v2/installedApps/". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.

Contents Security Policy, 줄여서 CSP에 의해 동적으로 스크립트가 삽입되는 것이 막혔다.

참고로 이러한 보안 설정을 위해 웹사이트 제작자는 페이지 내에 몇줄의 Meta Tag만 간단히 삽입하면 된다.

여튼 스크립트 삽입 시도 시 콘솔창에 위 그림과 같은 에러가 뜨는 경우 브라우저 설정에서 CSP를 꺼줘야 하며,
몇가지 방법을 찾아 시도해보았으나 적어도 내 환경(Selenium, Chrome, Python 3.5) 에서 잘 안 되는 경우가 많았다.

그 중 아주 잘 먹히는 방법을 찾아서 본 포스트에 좀 더 상세하게 정리해본다. 출처는 아래의 링크이다.

https://stackoverflow.com/questions/53304222/relaxing-chromes-csp-while-running-tests-webdriver-content-security-policy

Selenium 에서 CSP를 disable 하는 확실한 방법

핵심은 CSP를 차단해주는 확장기능을 Selenium에서 사용하는 것이다.

  1. CSP 차단을 위한 Disable Content-Security-Policy 확장기능을 파일로 다운로드 받아야 한다.
    이를 위해 Chrome Extension Downloader 웹사이트에 접속한 후 extension 페이지 주소인
    https://chrome.google.com/webstore/detail/disable-content-security/ieelmcmcagommplceebfedjlakkhpden 을
    주소 입력란에 붙여넣기 하여 확장기능 파일을 다운로드 받는다.
  2. 다운로드 받은 파일의 확장자를 zip 로 바꾸고, 압축을 푼다.
  3. 압축 파일 내 background.js 파일을 텍스트 에디터로 연다.
  4. isCSPDisabled = false; 를 찾아서 isCSPDisabled = true; 로 바꾼다.
    이 작업을 해주는 이유는 해당 확장기능의 기본값이 'CSP 끄지 않음' 이고 확장기능 버튼을 클릭해줘야 CSP Disable이 되는데, 
    Selenium이 실행된 직후부터 바로 CSP Disable을 하기 위한 것이다.
  5. 다시 zip 로 압축한다.
  6. 확장기능을 불러오기 위한 코드를 작성한다.

테스트한 코드는 아래와 같다. 처음 코드에서 추가해준 것은 확장기능을 불러오는 라인 단 한 줄이다.

from selenium import webdriver
import time
import os

url = "https://play.google.com/store/apps/details?id=com.google.android.googlequicksearchbox"
CHROME_EXE_PATH = r'크롬 드라이버 파일 경로\chromedriver.exe'

# 브라우저 설정
options = webdriver.ChromeOptions()
options.add_extension('확장기능 ZIP 파일 경로\Disable-Content-Security-Policy_v1.0.6.zip')
driver = webdriver.Chrome(executable_path=CHROME_EXE_PATH, chrome_options=options)
driver.implicitly_wait(1)

# 크롤링 대상 링크 접속
driver.get(url)

# jquery load
jScript = """
  var script = document.createElement('script');
  script.type = "text/javascript";
  script.src = "https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js";
  document.getElementsByTagName('head')[0].appendChild(script);
"""
jScript2 = "console.log('jquery:', typeof jQuery);"
driver.execute_script(jScript)
time.sleep(1)
driver.execute_script(jScript2)

os.system("PAUSE")
driver.quit()

실행 결과는 아래와 같다.

이제 페이지가 이동되지 않았다는 전제하에 jQuery 를 Selenium의 execute_script 함수에서 자유롭게 쓸 수 있다.