문제 상황
크롤링을 하다가 중간에 '페이지를 열기 위한 메모리가 충분하지 않음' 오류와 함께 오류 코드 : Out of Memory 오류가 발생하였다.
문제 원인
Chrome은 메모리를 많이 요구한다. 탭을 10개 이상 띄워놓게 된다면 메모리가 8GB 이상인 PC 환경에서 해야 할 정도이다.
"왜 Chrome은 다른 브라우저보다 메모리 사용량이 많은가?"
1. Chrome은 각 탭이 독립되어있는 응용프로그램처럼 작동하기 때문에 메모리 사용량이 많이 늘어나게 된다.
예를 들어 Chrome에서는 한 탭에서 오류가 나더라도 다른 탭에서 오류가 나지 않는다면 다른 탭에서는 잘 작동하지만 Internet Explorer는 도중에 오류가 나게 되면 전부 다 다시 실행시켜야 한다.
2. Chrome은 프리렌더링(사용자가 이전에 방문한 페이지를 메모리에 저장해 두었다가 다시 그 페이지로 들어가게되면 메모리에서 웹 페이지를 불러오는 기술)을 지원하기 때문이다.
특정 웹 페이지를 다시 방문하게 되면 웹 페이지가 뜨는 속도가 빨라지는 경험을 할 수 있는데 이런 프리렌더링 기술을 활용하기 때문이다.
즉, 메모리를 손해보고 속도 측면에서 이득을 본 것이다.
문제 해결 과정(시행 착오)
1) 처음에는 새로운 쿠키를 생성해서 각 쿠키에 분산해서 담으면 되지 않을까? 라고 생각하여 쿠키를 랜덤으로 만들어주는 코드를 넣었다.
결과는 똑같은 오류가 발생하였다.
2) 구글링을 해본 결과 disk-cache-size을 조정하면 해결이 된다고 하여 이를 조정하고 보안 기능 비활성화, 시크릿 모드 활성화 등 캐시와 쿠키를 비활성화하기 위해 아래와 같은 코드를 추가하였다.
결과는 똑같은 오류가 발생하였다.ㅠㅠ
#chromeOptions에 disk-cache-size를 0으로 지정한다. (크롤링 하다 중간에 메모리 문제로 끊기는 것을 방지)
chrome_options.add_argument("--disk-cache-size=0")
#chrome_options.add_argument("--incognito") # 시크릿 모드 (캐시, 쿠키 기능 비활성화)
# chrome_options.add_argument('headless')
chrome_options.add_argument('--no-sandbox') # 보안 기능 비활성화 (샌드박스라는 공간을 비활성화 시킨다는 뜻)
chrome_options.add_argument('--disable-dev-shm-usage') # /dev/shm(공유메모리) 디렉토리를 사용하지 않는다는 뜻
chrome_options.add_argument('--disable-gpu')
chrome_options.add_argument("window-size=1400,600")
3) 크롤링 하는 중간마다 구글에서 인터넷 사용 기록을 삭제하는 코드를 추가한다.
하지만 인터넷 사용 기록 삭제를 하려고 CSS 선택자를 확인해보니 실제로 선택이 되지 않도록 막아두었다.
방법이 없을까? 고민하다가 단축키를 이용하였다. 이 방법은 막히지 않은 것 같다. (코드는 아래 '문제 해결' 란에 있습니다.)
문제 해결
크롤링 하는 중간마다 구글에서 인터넷 사용 기록을 삭제하는 코드를 추가하여 주기적으로 delete_cash 함수를 호출해주었다.
주의사항 : 각 탭에 대해서 인터넷 사용 기록을 삭제해야한다. (탭이 3개라면 주기적으로 3개 탭에 대해서 모두 인터넷 사용 기록을 삭제하는 로직을 수행해주는 것이 안전하다.)
service = Service() # chrome-for-testing 버전에 맞춰 적용되는 코드
driver = webdriver.Chrome(service=service, options=chrome_options)
def delete_cash(driver):
driver.get("chrome://settings/clearBrowserData")
driver.implicitly_wait(10)
time.sleep(3)
element = driver.find_element(By.XPATH, "//settings-ui")
element.send_keys(mouse_button.MouseButton.LEFT)
element.send_keys(Keys.ENTER)
time.sleep(1)
delete_cash(driver)
이제 크롤링을 하다가 메모리가 뻗지 않고 잘 실행되었다.