06-인텔리제이+파이썬 크롤링 과정

jje·2026년 2월 12일

개인 프로젝트

목록 보기
6/18

일단 파이썬을 사용해본 적이 없지만...
데이터 크롤링을 해야하기 때문에 실행방법을 찾아보면서 시도하려고 한다
일단 가져오려는 자료가 몇백개는 가볍게 넘기 때문에 빨리 성공을 해봐야 할 것 같다


사용 IDE : 인텔리제이 ultimate
Build #IU-253.30387.90, built on January 22, 2026
Runtime version: 21.0.9+1-b1163.94 amd64 (JCEF 137.0.17)

혹시 이런 자세한 정보도 필요할까봐...


파이썬 프로젝트 생성

크롤링을 위해서는 당연히 파이썬 프로젝트가 필요하다

이름의 통일성을 위해 docent_crawling이라는 이름으로 만들어줬고
생성 폴더도 본 프로젝트(docentNote)가 생성된 워크스페이스 폴더에 만들어줬다

인터프리터 타입 : project venv
프로젝트 풀더 안에 가상환경을 세팅해놨다는 뜻이다
프로젝트마다 필요한 도구 버전이 다를 수 있어서 가상환경을 세팅해놓는 편이 좋다

크롤링을 위한 라이브러리 설치

requests: 웹사이트에 접속해서 데이터를 가져오는 도구
BeautifulSoup4: 가져온 HTML 데이터에서 원하는 정보만 골라내는 도구
Selenium (선택): 로그인이 필요하거나 화면을 아래로 내려야 나오는 이미지를 잡을 때 사용

각 라이브러리를 사용하는 이유
: 크롤링 과정에서 맡은 역할이 뚜렷하고 효율적이기 때문

  1. Requests
    requests는 파이썬에서 가장 빠르고 간결하게 웹 서버에 "데이터 좀 주세요"라고 요청을 보내는 도구

구체적인 사용 이유: 웹 브라우저(크롬, 엣지 등)를 띄우지 않고 오직 '텍스트 데이터'만 주고받기 때문에 속도가 압도적으로 빠르다
역할: 서버로부터 HTML 코드 덩어리나 이미지 바이너리(0과 1로 된 파일 데이터)를 내 컴퓨터로 실어 나르는 역할
한계: 단순히 데이터를 받아올 뿐이라서, 받아온 데이터 안에서 특정 정보를 찾는 기능은 없다

  1. BeautifulSoup4
    BeautifulSoup4requests가 가져온 지저분한 HTML 코드 뭉치를 파이썬이 이해할 수 있는 구조(Tree 구조)로 만들어주는 도구

구체적인 사용 이유: HTML은 복잡한 태그(<div, <a>, <img)로 얽혀 있는데, 여기서 "클래스 이름이 'wp-post-image'인 이미지 주소만 뽑아줘" 같은 복잡한 명령을 단 한 줄로 수행할 수 있게 해준다
역할: 고화질 이미지의 src(주소)나 작품의 제목, 화가 이름 같은 '핵심 정보'만 핀셋으로 뽑아내는 필터링을 담당
한계: 스스로 웹사이트에 접속하는 기능은 없기 때문에 반드시 requests가 가져온 데이터가 있어야 작동

  1. Selenium
    Selenium은 실제 웹 브라우저를 직접 띄워서 사람이 하는 행동(클릭, 스크롤, 타이핑)을 그대로 재현하는 자동화 도구

구체적인 사용 이유:
동적 로딩 해결: 최근 웹사이트들은 스크롤을 끝까지 내려야만 이미지가 로딩되는 '무한 스크롤'** 방식이 많아졌다. requests는 첫 화면만 가져오지만, Selenium은 스크롤을 직접 내려서 숨겨진 이미지를 나타나게 할 수 있다
인증 및 상호작용: 로그인 버튼을 누르거나, 팝업창을 닫거나, 특정 버튼을 눌러야 데이터가 나오는 사이트에서 유일한 해결책이 된다
역할: 사람이 직접 브라우저를 조작하듯 행동하며 숨겨진 데이터를 억지로 끌어내는 조작을 담당
한계: 브라우저를 직접 띄우고 실행하므로 메모리를 많이 차지하고 속도가 매우 느리다

내가 크롤링 해야하는 사이트는 무한스크롤이 아닌 정적인 사이트지만
많은 사람들이 선택한 방법 == 오류상황에 대한 대처법이 하나라도 있을 가능성이 높다
그리고 나중에 동적인 페이지를 크롤링 할 상황이 생기면 여기에서 조금만 수정해주면 되기 때문에
앞으로의 활용 가능성을 생각해서 셀레니움을 사용하기로 했다


프레임워크는 내장된 터미널로 설치했다
제일 속 편하게 설치할 수 있는 방법이다

(.venv)
== 가상환경이 활성화 된 상태인걸 확인해야 한다

pip install requests beautifulsoup4 selenium webdriver-manager
== 프레임워크를 한번에 설치하는 명령어

requests: 웹 페이지의 데이터를 요청하고 가져오는 역할
beautifulsoup4: 가져온 HTML 코드에서 이미지 주소만 추출
selenium: 브라우저를 직접 조작해 스크롤을 내리거나 버튼을 누름
webdriver-manager: 셀레니움을 쓸 때 브라우저 드라이버를 자동으로 관리해줘서 편리

엔터를 누르면 뭐가 쭉 설치가 되는데 오른쪽 하단의 로딩바까지 다 사라지면 설치 확인을 해야한다

pip list
명령어를 입력했을 때 나오는 목록에서 네가지 라이브러리 이름이 다 떠있으면 설치 성공


크롤링 테스트

프로젝트 이름에 우클릭 - new - 파이썬 파일
main 이라는 이름으로 파일을 생성해준다

import requests # 접속 도구 로드 #
from bs4 import BeautifulSoup # 추출 도구 로드 #
import os # 폴더 관리 도구 로드 #

# 1. 저장 폴더 준비 #
folder_name = 'crawled_images' # 이름 설정 #
if not os.path.exists(folder_name): # 폴더 존재 확인 #
    os.makedirs(folder_name) # 폴더 생성 #

# 2. 대상 사이트 설정 #
url = 'https://www.google.com/search?q=dog&tbm=isch' # 주소 설정 #
headers = {'User-Agent': 'Mozilla/5.0'} # 브라우저 정보 설정 #

# 3. 데이터 수집 #
response = requests.get(url, headers=headers) # 사이트 데이터 가져옴 #
soup = BeautifulSoup(response.text, 'html.parser') # HTML 분석 설정 #

# 4. 이미지 추출 및 저장 #
images = soup.find_all('img') # 이미지 태그 추출 #

for i, img in enumerate(images[:10]): # 10개만 반복 설정 #
    img_url = img.get('src') # 이미지 주소 가져옴 #
    
    if img_url and img_url.startswith('http'): # 유효 주소 확인 #
        try:
            img_data = requests.get(img_url).content # 바이너리 가져옴 #
            file_path = f'{folder_name}/dog_{i}.jpg' # 파일 경로 설정 #
            
            with open(file_path, 'wb') as f: # 파일 생성 #
                f.write(img_data) # 데이터 저장 #
                print(f'{i}번 이미지 저장 완료') # 진행 상태 출력 #
        except:
            print(f'{i}번 이미지 저장 실패') # 에러 차단 #
  1. 준비 단계: 이미지를 담을 바구니(폴더)를 먼저 만든다 (최상위 폴더에 생긴다)
  2. 접속 단계: 구글 서버에 "강아지 사진 주세요"라고 요청
  3. 추출 단계: 받아온 코드 뭉치에서 사진 주소만 골라낸다
  4. 저장 단계: 주소를 따라가서 실제 사진 파일을 내 컴퓨터에 옮긴다

해당 코드를 실행하면

설정해놓은 이름대로 폴더가 생기고

9번 이미지까지 저장이 됐다
테스트는 성공

그리고 놀랍게도 이 과정동안 오류가 없었다
너무 놀랍게도...


여기서 하나 고려할 점이 생겼다

지금 나는 그림만 몇백장을 저장해야하는데
이걸 한 폴더에 관리하는건 분명히 비효율적일 것 같다
../crawling_image/movement/painter 구조대로 관리를 하면 좋을 것 같은데

시각화 한다면 이런 식으로?
DB는 이미지를 파일 경로로 저장하는 방식을 사용한다 == 즉 텍스트 데이터
이렇게 폴더 구조를 세분화 한다면 어쩌다가 같은 이름의 그림이 있어도
url 즉 경로가 다르기 때문에 DB가 헷갈릴 일이 없다


크롤링 자료 정리

일단 미술사조/화가 범위를 좁히자
크롤링 할 사이트의 카테고리에서

  1. 유명한 화가들
  2. 선정성, 폭력성이 낮은 그림들
  3. 호불호가 없을만한 정물화, 풍경화

위주로 찾았다

르네상스 (2)
다빈치 https://artvee.com/artist/leonardo-da-vinci/
미켈란젤로 https://artvee.com/artist/michelangelo/
르네상스는 이름부터 유명하지만...
이 시대 그림은 사람들이 대부분 헐벗었고 종교화가 컨셉이었던 시대라서 예수의 고난이나 피흘리고 다친 상태의 그림들이 많아서 부득이하게 유명한 화가 두명만 선정했다

바로크 (7)
루벤스 https://artvee.com/artist/peter-paul-rubens/page/1/
렘브란트 https://artvee.com/artist/rembrandt-van-rijn/
요하네스 https://artvee.com/artist/johannes-vermeer/
벨라스케스 https://artvee.com/artist/diego-velazquez/
브뤼헐 https://artvee.com/artist/jan-brueghel-the-elder/
칼데이 https://artvee.com/artist/francesco-caldei/
혼토르스트 https://artvee.com/artist/gerard-van-honthorst/

로코코 (7)
와토 https://artvee.com/artist/jean-antoine-watteau/
부셰 https://artvee.com/artist/francois-boucher/
프라고나르 https://artvee.com/artist/jean-honore-fragonard/
티에폴로 https://artvee.com/artist/giovanni-battista-tiepolo/
브룅 https://artvee.com/artist/elisabeth-louise-vigee-le-brun/
카날레토 https://artvee.com/artist/canaletto/
게인즈버러 https://artvee.com/artist/thomas-gainsborough/

낭만주의 (4)
프리드리히 https://artvee.com/artist/caspar-david-friedrich/
터너 https://artvee.com/artist/joseph-mallord-william-turner/
콘스터블 https://artvee.com/artist/john-constable/
고야 https://artvee.com/artist/francisco-de-goya/


리얼리즘 (7)
쿠르베 https://artvee.com/artist/gustave-courbet/
밀레 https://artvee.com/artist/jean-francois-millet/
도미에 https://artvee.com/artist/honore-daumier/
레핀 https://artvee.com/artist/ilya-efimovich-repin/
닐슨 https://artvee.com/artist/amaldus-nielsen/
소른 https://artvee.com/artist/anders-zorn/
로비 https://artvee.com/artist/jean-baptiste-robie/

인상주의 (7)
모네 https://artvee.com/artist/claude-monet/
르누아르 https://artvee.com/artist/pierre-auguste-renoir/page/1/
드가 https://artvee.com/artist/edgar-degas/
피사로 https://artvee.com/artist/camille-pissarro/
마네 https://artvee.com/artist/edouard-manet/
모리조 https://artvee.com/artist/berthe-morisot/
소로야 https://artvee.com/artist/joaquin-sorolla/

탈인상주의 (7)
고흐 https://artvee.com/artist/vincent-van-gogh/
고갱 https://artvee.com/artist/paul-gauguin/page/3/
세잔 https://artvee.com/artist/paul-cezanne/
쇠라 https://artvee.com/artist/georges-seurat/
루아조 https://artvee.com/artist/gustave-loiseau/
뤼셀베르그 https://artvee.com/artist/theo-van-rysselberghe/
시다네르 https://artvee.com/artist/henri-le-sidaner/

아르누보 (6)
무하 https://artvee.com/artist/alphonse-mucha/
세레 https://artvee.com/artist/jules-cheret/
프리바리브몽 https://artvee.com/artist/henri-privat-livemont/
뮈니에 https://artvee.com/artist/georges-meunier/
닐슨 https://artvee.com/artist/kay-rasmus-nielsen/
클림트 https://artvee.com/artist/gustav-klimt/

삽화
레이엔데커 https://artvee.com/artist/joseph-christian-leyendecker/

양이 어마어마해서 크롤링 하는데 시간이 걸릴 것 같다
그래서 일단 두번으로 나눠서 처리하기로

여기서 한번 더 선택을 해야하는데
1. 원본 파일을 다운로드 받을까
2. 사이즈가 줄어든 썸네일을 다운받을까

일단 지금은 용량이 적은 썸네일로 다운받고 혹시나 나중에 내가 이 프로젝트를 더 발전시킨다면 그 때 원본파일을 다운로드 받아야겠다


크롤링 + 실제 자료 수집

import os # 폴더 관리 도구 로드 #
import requests # 접속 도구 로드 #
from bs4 import BeautifulSoup # 데이터 추출 도구 로드 #
import time # 차단 방지 지연 도구 로드 #

# 1. 경로 설정 #
base_path = r'C:\jje_works\sts5.0.1_workspace\docentNote\crawling_images' # 루트 경로 설정 #

# 2. 화가 데이터 정리 (1탄: 르네상스 ~ 낭만주의) #
art_data = {
    'Renaissance': {
        'Da_Vinci': 'https://artvee.com/artist/leonardo-da-vinci/',
        'Michelangelo': 'https://artvee.com/artist/michelangelo/'
    },
    'Baroque': {
        'Rubens': 'https://artvee.com/artist/peter-paul-rubens/',
        'Rembrandt': 'https://artvee.com/artist/rembrandt-van-rijn/',
        'Vermeer': 'https://artvee.com/artist/johannes-vermeer/',
        'Velazquez': 'https://artvee.com/artist/diego-velazquez/',
        'Brueghel': 'https://artvee.com/artist/jan-brueghel-the-elder/',
        'Caldei': 'https://artvee.com/artist/francesco-caldei/',
        'Honthorst': 'https://artvee.com/artist/gerard-van-honthorst/'
    },
    'Rococo': {
        'Watteau': 'https://artvee.com/artist/jean-antoine-watteau/',
        'Boucher': 'https://artvee.com/artist/francois-boucher/',
        'Fragonard': 'https://artvee.com/artist/jean-honore-fragonard/',
        'Tiepolo': 'https://artvee.com/artist/giovanni-battista-tiepolo/',
        'Le_Brun': 'https://artvee.com/artist/elisabeth-louise-vigee-le-brun/',
        'Canaletto': 'https://artvee.com/artist/canaletto/',
        'Gainsborough': 'https://artvee.com/artist/thomas-gainsborough/'
    },
    'Romanticism': {
        'Delacroix': 'https://artvee.com/artist/eugene-delacroix/',
        'Friedrich': 'https://artvee.com/artist/caspar-david-friedrich/',
        'Turner': 'https://artvee.com/artist/joseph-mallord-william-turner/',
        'Constable': 'https://artvee.com/artist/john-constable/',
        'Goya': 'https://artvee.com/artist/francisco-de-goya/'
    }
} # 설정 #

headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'} # 브라우저 정보 설정 #

# 3. 크롤링 실행 #
for movement, painters in art_data.items(): # 사조별 반복 #
    for painter, url in painters.items(): # 화가별 반복 #
        
        # 폴더 생성 #
        save_dir = os.path.join(base_path, movement, painter) # 계층 경로 설정 #
        if not os.path.exists(save_dir): # 폴더 확인 #
            os.makedirs(save_dir) # 폴더 생성 #
            
        try:
            response = requests.get(url, headers=headers) # 데이터 가져옴 #
            soup = BeautifulSoup(response.text, 'html.parser') # HTML 분석 설정 #
            
            # 수정한 부분: 따옴표 짝을 맞추고 클래스 선택 방식을 변경했습니다. #
            items = soup.find_all('div', class_='product-element-top') # 컨테이너 추출 #
            
            for i, item in enumerate(items[:20]): # 20개 반복 설정 #
                img_tag = item.find('img') # 이미지 태그 가져옴 #
                if img_tag:
                    # lazy 로딩용 주소 또는 일반 주소 가져옴 #
                    img_url = img_tag.get('data-lazy-src') or img_tag.get('src') # 주소 가져옴 #
                    
                    if img_url:
                        img_data = requests.get(img_url).content # 바이너리 가져옴 #
                        file_name = f"{painter.lower()}_{i}.jpg" # 이름 설정 #
                        full_path = os.path.join(save_dir, file_name) # 경로 설정 #
                        
                        with open(full_path, 'wb') as f: # 파일 생성 #
                            f.write(img_data) # 데이터 저장 #
                        print(f"[{movement}] {painter} - {i}번 저장 완료") # 상태 출력 #
                        
                        time.sleep(1.0) # 지연 시간 설정 #
                    
        except Exception as e:
            print(f"{painter} 크롤링 중 에러 발생: {e}") # 에러 차단 #

print("1탄 작업이 모두 끝났습니다!") # 완료 #

한꺼번에 너무 빨리 크롤링하면 차단먹을까봐 일부러 1초로 설정했는데
진짜 너무 오래걸린다... 그래도 차단만 안 당하면 좋겠다

그리고 여기까지도 아직 오류가 없다
감개무량이다


그리고 두번째 크롤링

import os # 폴더 관리 도구 로드 #
import requests # 접속 도구 로드 #
from bs4 import BeautifulSoup # 데이터 추출 도구 로드 #
import time # 지연 시간 설정 #

# 1. 절대 경로 설정 #
base_path = r'C:\jje_works\sts5.0.1_workspace\docentNote\crawling_images' # 루트 경로 설정 #

# 2. 화가 데이터 정리 (2탄) #
art_data = {
    'Realism': {
        'Courbet': 'https://artvee.com/artist/gustave-courbet/',
        'Millet': 'https://artvee.com/artist/jean-francois-millet/',
        'Daumier': 'https://artvee.com/artist/honore-daumier/',
        'Repin': 'https://artvee.com/artist/ilya-efimovich-repin/',
        'Nielsen_A': 'https://artvee.com/artist/amaldus-nielsen/',
        'Zorn': 'https://artvee.com/artist/anders-zorn/',
        'Robie': 'https://artvee.com/artist/jean-baptiste-robie/'
    },
    'Impressionism': {
        'Monet': 'https://artvee.com/artist/claude-monet/',
        'Renoir': 'https://artvee.com/artist/pierre-auguste-renoir/',
        'Degas': 'https://artvee.com/artist/edgar-degas/',
        'Pissarro': 'https://artvee.com/artist/camille-pissarro/',
        'Manet': 'https://artvee.com/artist/edouard-manet/',
        'Morisot': 'https://artvee.com/artist/berthe-morisot/',
        'Sorolla': 'https://artvee.com/artist/joaquin-sorolla/'
    },
    'Post-Impressionism': {
        'Gogh': 'https://artvee.com/artist/vincent-van-gogh/',
        'Gauguin': 'https://artvee.com/artist/paul-gauguin/',
        'Cezanne': 'https://artvee.com/artist/paul-cezanne/',
        'Seurat': 'https://artvee.com/artist/georges-seurat/',
        'Loiseau': 'https://artvee.com/artist/gustave-loiseau/',
        'Rysselberghe': 'https://artvee.com/artist/theo-van-rysselberghe/',
        'Sidaner': 'https://artvee.com/artist/henri-le-sidaner/'
    },
    'Art_Nouveau': {
        'Mucha': 'https://artvee.com/artist/alphonse-mucha/',
        'Cheret': 'https://artvee.com/artist/jules-cheret/',
        'Livemont': 'https://artvee.com/artist/henri-privat-livemont/',
        'Meunier': 'https://artvee.com/artist/georges-meunier/',
        'Nielsen_K': 'https://artvee.com/artist/kay-rasmus-nielsen/',
        'Klimt': 'https://artvee.com/artist/gustav-klimt/'
    },
    'Illustration': {
        'Leyendecker': 'https://artvee.com/artist/joseph-christian-leyendecker/'
    }
} # 설정 #

headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'} # 접속 설정 #

# 3. 크롤링 실행 #
for movement, painters in art_data.items(): # 사조 반복 #
    for painter, url in painters.items(): # 화가 반복 #
        
        # 저장 폴더 생성 #
        save_dir = os.path.join(base_path, movement, painter) # 경로 설정 #
        if not os.path.exists(save_dir): # 확인 #
            os.makedirs(save_dir) # 생성 #
            
        try:
            response = requests.get(url, headers=headers) # 데이터 가져옴 #
            soup = BeautifulSoup(response.text, 'html.parser') # 분석 설정 #
            
            items = soup.find_all('div', class_='product-element-top') # 태그 추출 #
            
            for i, item in enumerate(items[:20]): # 20개 제한 설정 #
                img_tag = item.find('img') # 태그 가져옴 #
                if img_tag:
                    img_url = img_tag.get('data-lazy-src') or img_tag.get('src') # 주소 가져옴 #
                    
                    if img_url:
                        img_data = requests.get(img_url).content # 바이너리 가져옴 #
                        file_name = f"{painter.lower()}_{i}.jpg" # 이름 설정 #
                        full_path = os.path.join(save_dir, file_name) # 경로 설정 #
                        
                        with open(full_path, 'wb') as f: # 파일 생성 #
                            f.write(img_data) # 데이터 저장 #
                        print(f"[{movement}] {painter} - {i}번 완료") # 상태 출력 #
                        
                        time.sleep(1.0) # 지연 시간 설정 #
                        
        except Exception as e:
            print(f"{painter} 에러 발생: {e}") # 에러 차단 #

print("2탄 (리얼리즘~삽화) 수집 완료!") # 완료 #

여기까지가 두번째로 나눈 크롤링 코드
실시간으로 폴더가 생기니까 좀 신기하다

하위폴더로 화가폴더까지 다 만들어졌다

초기 데이터를 위한 크롤링은 여기서 종료

profile
공부기록

0개의 댓글