크롤링을 하면서 화면을 캡쳐해야 할 일이 왕왕 있는데,
구글에 검색해보면 주로 Selenium으로 직접 scroll을 움직이면서 캡쳐하는 방법만 잔뜩 있다.
해당 코드를 보면 좀 하드코딩 하는 느낌이 난달까?
코드가 지저분해지고 길어지고.. 좀 꼴보기 싫어져서
라이브러리를 설치하든 어쨌든 간결하게 만들수 없을까 해서 열심히 조사를 해봤다.
역시, 없는건 없다.
꽤 괜찮은 방법들을 찾아서 소개해 주도록 하겠다.
[시간이 없으신 분들은 방법 3을 바로 읽어보세요!!!]
관련 출처 :
Take screenshot of full page with Selenium Python with chromedriver
After trying out various approaches... I have stumbled upon this page to take full-page screenshot with chromedriver, selenium and python. The original code is here. (and I copy the code in this p...
stackoverflow.com
위 StackoverFlow의 답변에서 해답을 얻었음을 알린다!
방법 1. Html의 Body tag를 이용하기
1-1. headless X
여러 답변들 중 가장 간결해보이는 코드이다.
일단 딱보기에도 scroll 관련된 코드가 없어서 아주 깔끔하다.
코드에서도 그 의미를 직접적으로 알 수 있다.
웹페이지의 Body부분을 가져온다는 뜻으로 즉 현재 화면 전체를 캡쳐한다는 말이 된다.
(Body = 웹페이지에 보이는 내용물 대부분)
(Head= script, style, link 등 메타데이터들. 웹페이지에 보이지 않는 부분들을 담고있음.)
위 코드의 find_element_by_tag_name을 보니, 구버전 selenium 코드인 것으로 보인다.
따라서 현재 사용할 수 있는 셀레니움 4 기준으로 작성을 해보도록 하겠다.
FullScreenshot-useTagName.py
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
# 셀레니움 드라이버 실행
CHROME_DRIVER_PATH = 'C:/your/chromedriver/path/chromedriver.exe'
options = webdriver.ChromeOptions()
driver = webdriver.Chrome(CHROME_DRIVER_PATH, options=options)
driver.maximize_window()
# url, 경로 설정
url = 'https://www.naver.com/'
path = 'TagName_Body.png'
#실행
driver.get(url)
time.sleep(1)
el = driver.find_element(By.TAG_NAME,'body')
el.screenshot(path)
위 답변 내용을 그대로 옮겨 보았다.
결과 - 실패
내가 원하는 FullScreenshot이 나오지 않았다.
답변을 보니 headless 옵션이 필수라고 한다.
내 경우에는 헤드리스를 사용할 수 없기 때문에 내 기준에는 적당하지 않은 코드이다.
1-2. headless O
혹시 headless를 하면 잘 될까 해서 headless 옵션을 넣고 다시 해봤다.
FullScreenshot-useTagName-2.py
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
# 셀레니움 드라이버 실행
CHROME_DRIVER_PATH = 'C:/your/chromedriver/path/chromedriver.exe'
options = webdriver.ChromeOptions()
options.add_argument('--headless')
options.add_argument('--start-maximized')
driver = webdriver.Chrome(CHROME_DRIVER_PATH, options=options)
# driver.maximize_window()
# url, 경로 설정
url = 'https://www.naver.com/'
path = 'TagName_Body-headlessOptions.png'
#실행
driver.get(url)
time.sleep(1)
el = driver.find_element(By.TAG_NAME,'body')
el.screenshot(path)
결과 - 실패
더 이상하게 나왔다ㅋㅋㅋㅋ.
FullScreenshot을 노리는게 아니라면 headless 옵션을 사용하지 않는 상태에서
캡쳐할 때 사용할 수 있을듯 하다.
하지만 전체 캡쳐에는 적합하지 않는것으로 보인다.
방법 2. Selenium-Screenshot 라이브러리를 이용하기
역시 같은 게시물의 다른 답변을 통해서 접근할 수 있었다.
라이브러리만 설치하면 아주 깔끔하게 짤 수 있었다.
관련 링크를 타고 들어가서 참고해가며 현재 버전에 맞게끔 수정해보았다.
FullScreenshot-useScreenshotLibrary.py
# 라이브러리 설치
pip install Selenium-Screenshot
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
from Screenshot import Screenshot
# 셀레니움 드라이버 실행
CHROME_DRIVER_PATH = 'C:/your/chromedriver/path/chromedriver.exe'
options = webdriver.ChromeOptions()
driver = webdriver.Chrome(CHROME_DRIVER_PATH, options=options)
driver.maximize_window()
# url 설정
url = 'https://www.naver.com/'
# 실행
ob = Screenshot.Screenshot()
driver.get(url)
time.sleep(1)
img_url = ob.full_screenshot(driver, save_path=r'.', image_name='Selenium-Screenshot.png', is_load_at_runtime=True, load_wait_time=3)
결과 - 애매함
작동하는걸 보고있으면
로딩이 다 됐는지 확인하고 페이지의 크기에 맞게 직접 자동으로 스크롤을 내려가면서 캡쳐하고
여러 사진을 합친 다음에 경로에 저장하는것 같다.
스크롤 할 때 그냥 그대로 쭉 내려가는 페이지이면 나쁘지 않을 것 같은데,
내가 캡쳐한 네이버 홈페이지처럼 웹페이지 상단부에 오브젝트가 함께 움직이는 경우
결과물이 좀 아쉽다.
근본적으로 전체를 캡쳐하는게 아니라,
스크롤을 조작하면서 캡쳐하는것이기 때문에 나타나는 현상으로 보인다.
좀 애매하긴 하지만 웹페이지에 스크롤시 따라다니는 오브젝트가 없다면 써볼법 한 라이브러리인것 같다.
방법 3. Chrome의 devtools protocol을 이용하기
결론적으로 말하면 내가 가장 만족한 방법이다.
추천 수는 좀 적지만, comment에 "non-headless" 상황에 동작한다는 말을 보고
재빨리 시도해보았다.
코드 윗부분이 스킵된듯이? 저렇게 ... 처리를 해놓기도 했고
다른 코드들처럼 selenium 옵션을 사용하지 않는거같아서 감이 잘 안왔었다.
그래서 이 사이트를 참고했다.
https://dev.to/gdledsan/selenium-4-and-chrome-driver-take-full-page-screenthos-2j8d
Selenium 4 and chrome driver, take full page screenshots
I spent some time playing with Selenium 4 trying to get a full page screenshot, this is what I found....
dev.to
이 홈페이지에서도 완벽히 다 써주지는 않았지만,
주석이 써져있어서 바로 작성해 볼 수 있었다.
현재 버전에 맞추어 작성한 코드는 다음과 같다.
FullScreenshot-useChromeDevProtocol.py
import base64
# I am assuming we already have a driver object.
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
# 셀레니움 드라이버 실행
CHROME_DRIVER_PATH = 'C:/your/chromedriver/path/chromedriver.exe'
options = webdriver.ChromeOptions()
driver = webdriver.Chrome(CHROME_DRIVER_PATH, options=options)
driver.maximize_window()
# url 설정
url = 'https://www.naver.com/'
# 실행
driver.get(url)
time.sleep(1)
# We need the dimensions of the content
page_rect = driver.execute_cdp_cmd('Page.getLayoutMetrics', {})
# parameters needed for ful page screenshot
# note we are setting the width and height of the viewport to screenshot, same as the site's content size
screenshot_config = {'captureBeyondViewport': True,
'fromSurface': True,
'clip': {'width': page_rect['cssContentSize']['width'],
'height': page_rect['cssContentSize']['height'], #contentSize -> cssContentSize
'x': 0,
'y': 0,
'scale': 1},
}
# Dictionary with 1 key: data
base_64_png = driver.execute_cdp_cmd('Page.captureScreenshot', screenshot_config)
# driver.execute_script("document.body.style.zoom='80%'")
# Write img to file
with open("chrome-devtools-protocol.png", "wb") as fh:
fh.write(base64.urlsafe_b64decode(base_64_png['data']))
결과 - 성공
문제 없는 FullScreenshot이다.
완벽한 캡쳐다.
headless든지 non-headless든지 사용가능하다고 하니까 혹시 headless로 해야하는 경우
Chrome devtools protocol의 Cature Screenshot을 검색해 보길 바란다.
다만 주의점으로는 페이지가 다 로딩될때까지 time.sleep을 해줘야한다.
아니면 로딩 되기도 전에 캡쳐를 해버리기 때문에! 그 점만 조심한다면 전혀 문제 없을것이다.
예전부터 전체 스샷 저장하는걸 좀 찾고싶었는데
이번기회에 제대로 파보고 적당한 방법을 찾아서 속 시원하다.
혹시 코드 실행시 안되거나 게시물에 문제가 있는 경우 댓글 달아주시면
최대한 빠르게 답변할수 있도록 하겠습니다!!
#영어태그
full screen capture python selenium
how to capture full screeen in python using selenuim
'IT > Web' 카테고리의 다른 글
[Java] Jsch로 SSH 연결 시 Algorithm DH not available 오류 해결 (1) | 2024.06.12 |
---|---|
[Ajax] 반복문 안에 Ajax 실행 시 차례대로 수행하게 하는 법 (0) | 2024.04.25 |
[에러] Spring에 MySQL 연결하기 (Gradle) (2) | 2024.01.03 |
[에러]Html, Javascript에서 ajax로 이미지 안열림 해결(2/2) - for문을 이용해 여러 이미지 동시 출력하기 (1) | 2023.12.13 |
[에러] Html, Javascript에서 ajax로 이미지 안열림 해결(1/2) - 파일 경로를 이용해서 이미지 열기 (1) | 2023.12.13 |