강사 약력
유튜브 스타트코딩 채널운영 (구독자 3만명)
삼성전자 메인 강사
에스오일, KT, 한국능률협회 등 출강
'파이썬을 제대로 활용해보려고 해' 저자
알파코(Alpaco) K-디지털트레이닝 강사
동신대, 대구한의대, 초등학교,중학교 코딩 특강
프로그래머스 파이썬 PCCE 자격증 강의
스타트코딩 유튜브 채널
https://www.youtube.com/channel/UCHwhZ7HPBhUh2IscPSL0pHA
스타트코딩 교육 센터
강의
수강평
- [신규 개정판] 이것이 진짜 크롤링이다 - 기본편
- [신규 개정판] 이것이 진짜 크롤링이다 - 기본편
게시글
질문&답변
원하는 값이 없을 때
저는 보통 try except 나 if 를 이용해서 간단하게 처리해서 pydantic 한 처리방법에 대해서는 잘모르겠네요 🤔 대신 제미나이 답변을 첨부해 드립니다!안녕하세요, 수강생님. 질문 감사합니다. 코드까지 깔끔하게 첨부해주셔서 질문의 의도를 정확히 파악하는 데 큰 도움이 되었습니다. Pydantic을 활용한 접근 방식, 아주 훌륭합니다.결론부터 말씀드리면, 수강생님이 작성하신 코드는 '오버 엔지니어링'이 전혀 아닙니다. 오히려 실무에서 적극 권장되는 '견고한(robust) 엔지니어링'에 가깝습니다. 제가 강의에서 (아마도) 간단한 딕셔너리(dict)로 처리한 것은 강의의 핵심 주제(크롤링)에 집중하기 위함이었을 겁니다.수강생님의 접근 방식(Pydantic)이 왜 좋은지, 그리고 '유연한 처리'란 무엇인지 설명해 드릴게요. 🧐 Pydantic을 쓰는 것이 왜 좋은가? 명확한 데이터 구조 (Schema):NewsItem 모델을 보는 것만으로도 "아, 이 크롤러는 5개의 필드를 수집하고, 각 필드는 이런 타입(str, HttpUrl)이구나"라고 즉시 알 수 있습니다.관심사의 분리 (SoC):crawl_... 함수는 HTML에서 데이터를 '추출(Extract)'하는 책임만 집니다.NewsItem 모델은 추출된 데이터가 '올바른지 검증(Validate)'하는 책임을 집니다.코드가 훨씬 깨끗해지고 유지보수가 쉬워집니다.조기 발견 (Fail-fast): 크롤링 도중 subject가 비어있거나 detail_article_url이 잘못된 형식이라면, Pydantic이 즉시 ValidationError를 발생시킵니다. 데이터가 한참 뒤에 DB에 저장되거나 다른 API로 전송될 때 에러가 나는 것보다 훨씬 좋습니다. 🎛 "유연한 처리" vs "견고한 처리" '유연하게 처리한다'는 것이 에러를 무시하고 대충 넘어가는 것을 의미하지는 않습니다.1. "간단하지만 지저분해지기 쉬운" 유연한 처리 (강의에서 쓸 법한 방식)Pydantic 없이 crawl_news_per_page 함수 내에서 모든 것을 처리하는 방식입니다.Pythondef crawl_news_per_page(soup: BeautifulSoup): result = [] news_per_page = soup.select(".block1") for article_card in news_per_page: try: subject = article_card.select_one(".articleSubject > a").text detail_article_url = get_detail_news_url(article_card.select_one(".articleSubject > a").get("href")) content_tag = article_card.select_one(".articleSummary") content = content_tag.contents[0].strip() press = content_tag.select_one(".press").text.strip() article_date = content_tag.select_one(".wdate").text.strip() # 여기서 직접 유효성 검사 (점점 지저분해짐) if not subject or not content: print("경고: 빈 제목이나 내용이 있습니다. 이 항목은 건너뜁니다.") continue # 이 항목을 스킵 result.append({ "subject": subject, "detail_article_url": detail_article_url, "content": content, "press": press, "article_date": article_date }) except AttributeError as e: # .text나 .get('href')를 시도할 때 태그가 None이면 발생하는 에러 print(f"파싱 에러 발생: {e}. 이 항목은 건너뜁니다.") continue # 이 항목을 스킵 except Exception as e: # 기타 예외 print(f"알 수 없는 에러: {e}") return result 문제점:try...except 블록이 거대해지고, 파싱 로직과 검증 로직, 에러 처리 로직이 뒤섞여 '스파게티 코드'가 되기 쉽습니다.2. 수강생님이 선택한 "견고한" 처리 (Pydantic)이 방식이 훨씬 낫습니다. 다만, 수강생님의 코드에서 두 가지 치명적인 문제를 수정해야 합니다. 💡 Pydantic 접근법 실전 꿀팁 (코드 개선) 수강생님의 코드는 90% 훌륭합니다. 하지만 valid_url 검증자와 파싱 부분에 실무에서 문제가 될 수 있는 부분이 있습니다.1. Validator는 I/O 작업을 해선 안 됩니다.문제:valid_url 함수 내부에서 requests.get(url)를 호출하고 계십니다.이유: Pydantic 모델을 생성(초기화)할 때마다 매번 HTTP 요청이 발생합니다. 뉴스 100개를 크롤링하면, 모델을 만들 때 100번, 나중에 실제 데이터를 쓸 때 100번, 총 200번의 요청이 나갈 수 있습니다. Validator는 데이터의 형식(format)이 유효한지만 빠르고 가볍게 검사해야 합니다. URL이 실제로 연결 가능한지(reachability)는 나중에 그 URL을 사용할 때 검사하는 것이 맞습니다.해결: Pydantic이 제공하는 HttpUrl 타입을 사용하면 requests 호출 없이도 URL 형식을 검증해 줍니다.2. 파싱(Parsing) 단계는 AttributeError에 방어적이어야 합니다.문제:subject = article_card.select_one(".articleSubject > a").text이유: 만약 .articleSubject > a 셀렉터에 해당하는 태그가 없다면select_one은 None을 반환합니다. None.text를 호출하는 순간 AttributeError가 발생하고, Pydantic 모델은 구경도 못하고 프로그램이 죽습니다.해결: 파싱 단계에서는 None을 안전하게 처리하고, None 또는 빈 문자열을 Pydantic 모델에 전달해야 합니다. 그 뒤는 Pydantic의 AfterValidator(non_empty_str)가 알아서 처리해 줄 것입니다. 🔧 개선된 코드 예시 제가 수강생님의 코드를 개선해 보겠습니다.Python# ... (requests, BeautifulSoup, datetime 등 import는 동일) from typing import Annotated, Optional from pydantic import BaseModel, HttpUrl, field_validator, ValidationInfo # --- Pydantic 모델 정의 --- def non_empty_str(v: str) -> str: """공백 제거 후 빈 문자열인지 검사하는 Validator""" if not v or not v.strip(): raise ValueError("필수 필드가 비어있습니다.") return v.strip() def valid_date_format(v: str) -> str: """날짜 형식이 'YYYY-MM-DD HH:MM:SS'인지 검사하는 Validator""" v = v.strip() try: datetime.strptime(v, "%Y-%m-%d %H:%M:%S") return v except ValueError: raise ValueError("날짜 형식이 'YYYY-MM-DD HH:MM:SS'와 다릅니다.") class NewsItem(BaseModel): # str 타입에 non_empty_str 검증을 적용합니다. subject: Annotated[str, AfterValidator(non_empty_str)] # HttpUrl 타입을 사용해 URL 형식을 검증합니다. (I/O 없음!) detail_article_url: HttpUrl content: Annotated[str, AfterValidator(non_empty_str)] press: Annotated[str, AfterValidator(non_empty_str)] article_date: Annotated[str, AfterValidator(valid_date_format)] # --- 크롤링 함수 정의 --- ROOT = "https://finance.naver.com/" PATH = "news/mainnews.naver" def get_detail_news_url(path): return urljoin(ROOT, path) def safe_get_text(tag, selector): """안전하게 .text를 추출하는 헬퍼 함수""" selected = tag.select_one(selector) return selected.text.strip() if selected else "" # None 대신 빈 문자열 반환 def safe_get_content(tag, selector): """본문 내용(press, wdate 제외)을 안전하게 추출하는 헬퍼 함수""" selected = tag.select_one(selector) if not selected: return "" # .press, .wdate 태그를 제외한 순수 텍스트 노드를 찾습니다. content_text = "" for child in selected.contents: if child.name not in ['span', 'a']: # press/wdate가 span/a 태그일 경우 content_text += str(child).strip() return content_text.strip() if content_text else "" def crawl_news_per_page(soup: BeautifulSoup): result = [] news_per_page = soup.select(".block1") for article_card in news_per_page: # 1. 안전하게 데이터 추출 (AttributeError 방지) # 태그가 없으면 None이나 ""이 subject_raw 등에 할당됩니다. subject_raw = safe_get_text(article_card, ".articleSubject > a") url_raw = article_card.select_one(".articleSubject > a") detail_article_url_raw = get_detail_news_url(url_raw.get("href")) if url_raw else "" content_raw = safe_get_content(article_card, ".articleSummary") press_raw = safe_get_text(article_card, ".articleSummary .press") article_date_raw = safe_get_text(article_card, ".articleSummary .wdate") try: # 2. Pydantic 모델로 검증 # 여기서 non_empty_str, HttpUrl, valid_date_format 등이 작동합니다. news_item = NewsItem( subject=subject_raw, detail_article_url=detail_article_url_raw, content=content_raw, press=press_raw, article_date=article_date_raw ) # 검증 성공 시 결과에 추가 result.append(news_item.model_dump()) except Exception as e: # 3. Pydantic 검증 실패 시 에러 처리 # (예: subject가 비어있거나, URL 형식이 아닐 때) print(f"--- 데이터 검증 실패 (항목 스킵) ---") print(f"에러: {e}") print(f"원본 데이터: [제목: {subject_raw[:20]}...], [URL: {detail_article_url_raw}]") print("---------------------------------") return result # ... (crawl_all_news, get_news_page_url 등 나머지 코드는 동일) 요약 수강생님의 Pydantic 접근은 '오버 엔지니어링'이 아니라 '베스트 프랙티스'입니다.'유연한 처리'란 try...except로 대충 넘기는 것이 아니라, 파싱(추출)과 검증(Validation)의 책임을 분리하고, Pydantic 같은 도구를 사용해 데이터 무결성을 '견고하게' 보장하는 것입니다.Validator 내부에서 requests.get 같은 I/O 작업은 절대 금물입니다. HttpUrl 타입을 활용하세요.파싱 로직은 AttributeError가 나지 않도록 방어적으로 작성하고 (예: safe_get_text), 검증은 Pydantic에 맡기세요.
- 0
- 2
- 40
질문&답변
44강 제목, 링크
여행다녀오느라 답변이 조금 늦었습니다.똑같이 not 선택자를 사용하면 되어서, 강의에 추가로 업데이트하진 않았는데요!아래와 같이 선택자를 만드시면 잘 동작할겁니다 ㅎㅎ posts = driver.find_elements(By.CSS_SELECTOR, "tbody > tr:not(.board-notice) .article") for post in posts: title = post.text link = post.get_attribute('href') print(title, link) html 구조를 정확히 파악하고선택자를 이용하면 됩니다 선택자 만드는게 까다로울 때는, AI에게 HTML 구조 (ex. table 태그)를 복사한다음 질문해보시는 것도 좋아요! (사진)
- 0
- 1
- 36
질문&답변
5번 강의 실습문제에서 질문있습니다.
그렇게 하셔도 됩니다!다만 print("이름-",name) 이렇게 썼을때 띄어쓰기가 하나 들어간다는 특징이 있어요 ㅎㅎ
- 0
- 1
- 34
질문&답변
크롤링한 링크가 엑셀로 들어가면 작동이 안되요
안녕하세요!저는 다시 결과물을 확인했을때 하이퍼링크가 잘 동작하네요! 우선 하이퍼링크에 오타가 없는지 잘한번 확인해 보시구요.오타가 없는대로 하이퍼링크가 활성화 되지 않는다면 아래 가이드를 따라서 해결해 보는 것을 추천드립니다 🙂 from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from bs4 import BeautifulSoup import time import pandas as pd # 크롬 드라이버 생성 driver = webdriver.Chrome() # 페이지 이동 driver.maximize_window() driver.get("https://shoppinghow.kakao.com/siso/p/sale/mall/talkstore") time.sleep(1) for i in range(1, 6): if i == 1: driver.execute_script("window.scrollTo(0, document.body.scrollHeight)") else: driver.execute_script("window.scrollTo(0, document.body.scrollHeight - 1000)") time.sleep(1.5) # 상품명, 상세페이지링크, 판매가격, 판매처, 배송정보 html = driver.page_source soup = BeautifulSoup(html, 'html.parser') data = [] items = soup.select(".list_spsale.__tiara_container_ > li") for item in items: name = item.select_one(".info_name").text link = 'https://shoppinghow.kakao.com/' + item.select_one(".link_product").attrs['href'] price = int(item.select_one(".info_sales").text.replace(',', '').replace('원', '')) shop = item.select_one(".txt_shop").text shipping = item.select_one(".info_etc").contents[-1].strip() # print(name, link, price, shop, shipping) data.append([name, link, price, shop, shipping]) df = pd.DataFrame(data, columns=['상품명', '상세페이지링크', '판매가격', '판매처', '배송정보']) df.to_excel('kakao_shopping_crawling.xlsx') driver.quit() 💡 가장 확실한 해결 방법: 코드 수정하기 가장 좋은 해결책은 파이썬 코드 자체를 수정해서, 엑셀 파일에 '이건 하이퍼링크입니다'라고 명확하게 알려주는 것입니다. 엑셀의 HYPERLINK 함수를 사용하도록 코드를 살짝만 바꿔주면 됩니다.아래와 같이 to_excel 실행 전, 데이터프레임의 링크 부분을 수정하는 코드 한 줄을 추가해 보세요. # ... (데이터 수집 코드 생략) ... df = pd.DataFrame(data, columns=['상품명', '상세페이지링크', '판매가격', '판매처', '배송정보']) # 아래 한 줄을 추가해주세요! # '상세페이지링크' 열의 모든 URL을 엑셀의 HYPERLINK 함수 형태로 바꿔줍니다. df['상세페이지링크'] = df['상세페이지링크'].apply(lambda url: f'=HYPERLINK("{url}")') df.to_excel('kakao_shopping_crawling_hyperlink.xlsx', index=False)✅ 추가적으로 확인해볼 사항 (엑셀 설정) 만약 위 방법으로도 해결이 안 되거나, 코드 수정이 어려운 상황이라면 엑셀 설정을 확인해볼 수 있습니다.엑셀 옵션 확인파일 > 옵션 > 언어 교정 > 자동 고침 옵션으로 들어갑니다.입력할 때 자동 서식 탭에서 '인터넷과 네트워크 경로를 하이퍼링크로 설정' 항목이 체크되어 있는지 확인해주세요. (아마 이미 체크되어 있을 가능성이 높습니다.)보안 센터 설정 확인파일 > 옵션 > 보안 센터 > 보안 센터 설정으로 들어갑니다.신뢰할 수 있는 위치나 외부 콘텐츠 설정이 너무 엄격하게 되어있어 링크 실행을 막고 있을 수 있습니다. 하지만 이 부분은 보안과 관련된 설정이므로 신중하게 변경하시는 것이 좋습니다.
- 0
- 2
- 53
질문&답변
선생님 셀에서 전체 실행은 어떻게하나요?
ctrl + shift + f8 이 전체 실행 단축키 입니다 ㅎㅎ
- 0
- 2
- 32
질문&답변
셀레니움 PDF자료는 받을 수 있나요
AI인턴이 잘 얘기 해줬네요... 이건 어떻게 알고 있는거지?
- 0
- 2
- 48
질문&답변
글목록 추출하기
네이버 카페 HTML 구조가 살짝 바뀌어서 기존 강의대로는 동작하지 않을겁니다 웹크롤링을 할때는 이런경우가 종종 있어서연습삼아 일부러 강의 업데이트를 하지 않은 것도 있어요 ㅎㅎ 거의 다 하셨는데 나무태그 만드실때 tbody 아래 tr 까지 선택해야해요~!.article-table > tbody:nth-of-type(2) > tr
- 0
- 2
- 53
질문&답변
메일 자동화 로그인 중복방지문자해결 오류 및 명시적 대기 질문
네이버 로그인 잘 되고 있어요~pw.send_keys(user_pw)보시면 패스워드를 2번 입력하도록 코드가 구성되어 있네요 그리고 메일자동화 합칠 때,명시적 대기 걸어줄 필요 없이 time.sleep 만 적절하게 1, 2초 씩 줘도 잘 동작합니다.감사합니다. 평안한 하루 되세요 :)
- 0
- 2
- 37
질문&답변
강의 노트가 어디에 있는건가요?
영상 플레이어에서 조금만 아래로 스크롤을 내려보면 나옵니다!
- 0
- 2
- 38
질문&답변
강의 커리큘럼 질문
예제가 갑자기 변경되어서 조금 당황스러우셨을텐데요.. ㅠ 크롤링 강의 특성상 예제 사이트가 업데이트 되면강의를 수정하거나 예제를 바꿔줘야 합니다. 네이버 쇼핑의 경우는 이번에 셀레니움을 완전히 막아 버렸어요.이를갈고 있다가 보안팀에서 원천 봉쇄해버린거 같은데이런 경우 어쩔 수 없습니다.. ㅎㅎ셀레니움을 사용할 수 없으니 강의에서 제외하도록 결정했어요. (고급 크롤링 기술인 패킷스니핑을 통해 데이터를 수집할 수는 있습니다만, 실전편강의에서는 다루지 않습니다)
- 0
- 1
- 53





![Thumbnail image of the [신규 개정판] 이것이 진짜 엑셀자동화다 - 기본편](https://cdn.inflearn.com/public/courses/330111/cover/d1d1754d-deea-4b3d-b14b-7e20cf602e5e/330111.png?w=148)
![Thumbnail image of the [신규 개정판] 이것이 진짜 크롤링이다 - 기본편](https://cdn.inflearn.com/public/courses/327774/cover/e25c692a-f39a-41ec-9da7-bdd7922e6553/327774.png?w=148)
![Thumbnail image of the [신규 개정판] 이것이 진짜 크롤링이다 - 실전편 (인공지능 수익화)](https://cdn.inflearn.com/public/courses/328045/cover/aec69a0c-6a3c-4602-b4a0-62d492398c12/328045.png?w=148)