본문 바로가기
잡지식 저장고/크롤링

셀레니움 없이 웹페이지 스크린샷 뜨기

by Slate_Knowledge 2023. 3. 6.
728x90

셀레니움 

셀레니움(Selenium)은 웹 드라이버 기반의 강력한 툴로써 크롤링 등 여러가지 용도로 널리 쓰이고 있는 라이브러리이다. 셀레니움의 강점은 그 쉬운 사용법에 있는데, 예를 들어 특정 url에 접근하여 페이지 스크린샷을 찍고싶다고 한다면 아래와 같이 간단한 코드로도 수행할 수 있다.

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
# options.add_argument("--headless")
options.add_argument('log-level=3')
options.add_argument(f"--window-size=1920,1080")
options.add_argument("--hide-scrollbars")
driver = webdriver.Chrome(options=options, executable_path='E://chromedriver.exe')
driver.get('https://google.com')
driver.save_screenshot('E://screenshot_test.png')

이렇게 한줄로 스크린샷이 가능한가 하면, 해상도를 정해두었을 경우 headless로 백그라운드 실행을 하여도 그대로 스크린샷이 가능하다.

headless 옵션이 없을때의 스크린샷
headless 옵션을 써서 백그라운드에서 찍은 스크린샷. 동일하게 잘 찍힌다.

 

셀레니움 없이 PDF로 스크린샷 저장하기

그러나 크롤링을 하다보면 아무래도 웹드라이버를 기반으로 하는 셀레니움은 웹드라이버도 지속적으로 업데이트 해줘야하고 불편할때가 있다. 웹드라이버를 모종의 이유로 인해서 설치 및 사용하지 못하는 환경의 경우에는 더욱 난감하다. 셀레니움이 없이는 그렇다면 어떻게 크롤링 스샷을 찍을 수 있을까?

좀 불편하고 실제 웹드라이버보다 렌더링 품질이 떨어지긴 하지만 방법이 있다. imgkit, pdfkit와 같이 wkhtmltopdf 패키지를 이용하는 라이브러리들도 있는데, 에러가 많고 안되는 페이지도 많아서 찾다보니 정착한게 weasyprint다. 링크를 따라들어가서 필요 패키지들을 설치한 다음 python pip로 weasyprint를 설치해주면 사용 가능한데, 아래와 같이 beautifulsoup와 엮어서도 사용할 수 있다.

response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
HTML(string=soup.prettify()).write_pdf('output.pdf'))

독립적으로도 url을 타고 들어가서 변환해주지만, 아무래도 크롤링을 하려면 beautifulsoup도 쓰게 되니까 예시코드에서는 그렇게 사용하였다.

그럼 얼마나 오래걸릴까? 안타깝게도 실험 결과 셀레니움으로 했을때는 총 14개 url 페이지에 대한 스크린샷을 찍는데에 총 32.18초(5회 반복 평균)가 걸렸고,

셀레니움을 이용했을 때의 스크린샷 속도

weasyprint 및 beautifulsoup을 이용했을 때는 같은 url list에 대해서 무려 438.36(!!)초로 매우매우 느리면서 한번쯤 에러도 나주는 모습을 확인할 수 있다. 

weasyprint와 beautifulsoup 사용시 스크린샷 속도

다만, 결과물이 셀레니움의 경우 정말로 딱 창에서 보이는 부분에 국한되어있다면,

셀레니움 스크린샷 결과물

html을 pdf로 저장하는 경우 html로 구성되는 페이지를 전부 저장하기 때문에, 아래와 같이 여러장의 pdf로 저장할 수 있다.

상기 전체 테스트를 위해 사용한 코드 전문은 아래와 같다.

import os
from functools import wraps

def timer(n=1):
    def decorator(func):
        import time
        @wraps(func)
        def wrapper(*args, **kwargs):
            start = time.time()
            print('Start with 1 iteration to warm up')
            func(*args, **kwargs)
            for _ in range(n):
                func(*args, **kwargs)
            end = time.time()
            print(f'{func.__name__} - Average Time taken: {(end-start)/n} seconds')
        return wrapper
    return decorator

def use_selenium(url_list, output_dir):
    from selenium import webdriver
    from selenium.webdriver.chrome.options import Options
    options = Options()
    options.add_argument("--headless")
    options.add_argument('log-level=3')
    options.add_argument(f"--window-size=1920,1080")
    options.add_argument("--hide-scrollbars")
    driver = webdriver.Chrome(options=options, executable_path='E://chromedriver.exe')
    os.makedirs(os.path.join(output_dir,'selenium'), exist_ok=True)
    @timer(n=5)
    def crawl(url_list):
        for url in url_list:
            driver.get(url)
            driver.save_screenshot(os.path.join(output_dir,'selenium',url.split('//')[1]+'.png'))
    crawl(url_list)

def use_beautifulsoup(url_list, output_dir):
    from weasyprint import HTML
    
    from bs4 import BeautifulSoup
    import requests
    os.makedirs(os.path.join(output_dir,'beautifulsoup'), exist_ok=True)
    
    @timer(n=5)
    def crawl(url_list):
        for url in url_list:
            try:
                response = requests.get(url)
                soup = BeautifulSoup(response.text, 'html.parser')
                HTML(string=soup.prettify()).write_pdf(os.path.join(output_dir,'beautifulsoup',url.split('//')[1]+'.pdf'))
            except Exception as e:
                print(f'{e}: {url}')
    crawl(url_list)

import argparse
argparser = argparse.ArgumentParser()
argparser.add_argument('-s', '--selenium', help='Use selenium', action='store_true')
argparser.add_argument('-b', '--beautifulsoup', help='Use beautifulsoup', action='store_true')

if __name__ == '__main__':
    args = argparser.parse_args()
    url_list = ['https://google.com', 'https://yahoo.com', 'https://bing.com',
                'https://duckduckgo.com', 'https://yandex.com', 'https://ask.com',
                'https://aol.com', 'https://baidu.com', 'https://wikipedia.org',
                'https://reddit.com', 'https://instagram.com', 'https://twitter.com','https://facebook.com','https://youtube.com']
    output_dir = 'E://kclee/블로그/crawling/results'
    
    if args.selenium:
        use_selenium(url_list, output_dir)
    
    if args.beautifulsoup:
        use_beautifulsoup(url_list, output_dir)
    
    if not args.selenium and not args.beautifulsoup:
        print('Please select a method')

실행은 아래와 같이 하면 된다.

python <PYTHON_FILE_NAME>.py -b -s

이때, beautifulsoup만 테스트 해보고 싶으면 -b 옵션만, 셀레니움만 테스트 해보고 싶으면 -s 옵션만 넣어주면 된다.

728x90
반응형

댓글