📚 웹 스크레이핑 문제 해결 (403 오류 및 데이터 누락) 마스터 청사진
💡 상황 해독
- 현재 상태: 웹사이트에서 필요한 정보(배우 이름, 이메일, 인스타그램 주소 등)를 자동으로 싹 긁어오려고 파이썬 코드를 만들었어요. 그런데 자꾸 웹사이트가 "너 들어오지 마!" 하고 막거나(403 Forbidden 오류), 분명히 웹사이트에는 정보가 있는데도 코드가 정보를 못 찾고 빈칸으로 가져오는 일이 생기고 있습니다. 신기하게도 그냥 인터넷 창(브라우저)으로 보면 다 잘 보이는데, 제 코드만 실행하면 말썽이네요.
- 핵심 쟁점:
- 만든 프로그램(스크립트)이 웹사이트 서버로부터 접근 거부당함 (403 오류).
- 웹페이지마다 생김새(HTML 구조)가 조금씩 달라서, 원하는 정보(특히 이름, 인스타그램)를 놓침.
- 데이터 긁어오는 데 시간이 너무 오래 걸림 (특히 많은 페이지를 처리할 때).
- 예상 vs 현실: 코드로 돌리면 클릭 몇 번보다 훨씬 빠르고 편하게 데이터가 모일 줄 알았는데, 실제로는 오류 때문에 자꾸 멈추고 빠진 데이터 메꾸느라 오히려 더 귀찮고 시간이 걸리는 상황입니다.
- 영향 범위: 원하는 배우 정보를 제때 못 모으게 되고, 문제 해결하느라 시간만 버리고 스트레스받습니다. 만약 웹사이트가 내 컴퓨터(IP 주소)를 완전히 차단하면, 앞으로 그 사이트 접속 자체가 어려워질 수도 있습니다.
🔍 원인 투시
- 근본 원인: 웹사이트 주인 입장에서는 정상적인 방문객(사람)들이 서비스를 잘 이용하는 게 중요하지, 정체 모를 로봇(스크립트)이 와서 순식간에 정보를 대량으로 퍼가는 걸 좋아하지 않아요. 그래서 "어? 얘는 사람 아닌 것 같은데?" 싶으면 문을 잠그거나(접근 차단), 내부 구조를 살짝 바꿔서 로봇이 헤매게 만듭니다.
- 연결 고리:
- (원인) 너무 빠르게 접속하거나, 요청 정보가 부실해서 사람처럼 안 보임 → (결과) 서버가 "너 로봇이지?" 의심 → 접근 차단 (403 오류 발생).
- (원인) 웹사이트 디자인이 바뀌거나, 페이지마다 정보 배치 방식이 조금씩 다름 → (결과) "여기서 이름 찾아와!" 하고 고정된 위치만 보도록 만든 코드가 헤맴 → 정보 못 찾고 누락됨.
- 일상 비유:
- 비유 1 (403 오류): 내가 만든 로봇을 시켜서 인기 빵집에 1초마다 가서 빵 100개씩 사 오라고 시키는 거예요. 빵집 주인은 로봇이 수상하기도 하고 다른 손님들한테 피해가 가니까 "더 이상 안 팔아!" 하고 로봇을 쫓아내는 거죠.
- 비유 2 (데이터 누락): 친구 집 주소를 'OO 아파트 101동 101호'로 외우고 찾아갔는데, 가보니 친구가 'OO 빌라 가동 101호'로 이사 갔거나, 어떤 집은 'A동'이라고 써 붙여놨어요. 내가 외운 주소랑 다르니 친구 집을 못 찾는 거죠.
- 비유 3 (자동화 패턴 감지): 매일 새벽 3시에 똑같은 옷을 입고 편의점에 가서 특정 음료수만 싹 쓸어가는 손님이 있다고 상상해보세요. 편의점 주인은 뭔가 이상하다고 느끼고 그 손님에게는 물건을 안 팔려고 할 수 있겠죠. 내 스크립트도 너무 티 나게 행동하면 서버가 감지하고 막는 거예요.
- 숨겨진 요소: 웹사이트는 그냥 화면만 보여주는 게 아니라, 접속하는 컴퓨터가 남기는 여러 흔적(IP 주소, 어떤 종류의 브라우저/운영체제인지 알리는 정보(User-Agent), 로그인 상태 정보(쿠키), 얼마나 자주 접속하는지 등)을 보고 사람인지 로봇인지 나름대로 판단하고 있어요. 그리고 모든 배우 프로필 페이지가 완전히 똑같은 디자인 틀을 사용하지 않을 수도 있다는 점도 중요해요.
🛠️ 해결 설계도
- 사람인 척 위장하기 (403 오류 피하기)
- 핵심 행동: 내 프로그램이 웹사이트에 접속할 때, 최대한 '진짜 사람'이 '일반적인 웹 브라우저'로 접속한 것처럼 보이게 꾸민다.
- 실행 가이드:
- 내가 쓰는 웹 브라우저(크롬, 엣지 등)의 '신분증' 정보(
User-Agent
값)를 알아내서, 내 코드가 웹사이트에 요청 보낼 때 같이 보낸다. (개발자 도구 Network 탭에서 확인 가능) - 로그인이 필요한 사이트라면, 먼저 브라우저로 로그인한 뒤, 개발자 도구(Application 또는 Storage 탭)에서 '쿠키(Cookie)' 값을 복사해서 내 코드에 넣어준다. (로그인 상태 유지)
- 너무 빠르게 요청하지 않도록 코드 중간에 잠깐 쉬는 시간(
time.sleep
)을 주거나, 동시에 너무 많은 요청을 보내지 않도록 조절한다(asyncio.Semaphore
사용).
- 성공 지표: 이전보다 403 Forbidden 오류가 눈에 띄게 줄어든다.
- 예시/코드 (
3.py
또는4.py
참고):
# 변경 전 (기본, 아무 정보 없음) # response = httpx.get(url) # 변경 후 (사람 흉내내기) cookies = { 'PHPSESSID': '최신값...', 'rx_login_status': '최신값...', # ... (로그인 관련 쿠키들) } headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)...', # 내 브라우저 정보 'Accept-Language': 'ko,en;q=0.9...', # ... (다른 헤더들) } # 비동기 클라이언트에 쿠키와 헤더 설정 async with httpx.AsyncClient(cookies=cookies, headers=headers) as client: response = await client.get(url) # 핵심 변화 설명: 웹사이트에 접속할 때, '나 이런 브라우저 쓰는 사람이야'(User-Agent)라는 명찰과 '나 로그인한 회원이야'(Cookie)라는 출입증을 보여주는 것과 같음.
- 주의사항: 쿠키 값은 시간이 지나면 무효가 되니, 오류가 나면 최신 값으로 바꿔줘야 함. User-Agent도 너무 옛날 브라우저 정보면 이상하게 보일 수 있음.
- 정보 탐정처럼 찾기 (데이터 누락 줄이기)
- 핵심 행동: 웹페이지 구조가 조금씩 달라도 원하는 정보를 찾을 수 있도록, 한 가지 방법이 실패하면 다른 방법으로 찾는 '플랜 B'를 준비한다.
- 실행 가이드:
- 가장 확실해 보이는 방법(예: 아주 구체적인 CSS 선택자)으로 정보를 먼저 찾아본다.
- 만약 못 찾으면, 좀 더 넓은 범위나 다른 특징(예: '홈페이지'라는 글자 옆, 특정 영역 안의 첫 번째 제목 태그
h2
)을 이용해서 다시 찾아본다. - 이름이나 링크 주소처럼 특정 패턴이 있는 정보는 정규 표현식(RegEx)으로 텍스트 안에서 찾아본다. (단, HTML 구조를 무시하고 찾으므로 엉뚱한 걸 찾을 수도 있음)
- 찾아낸 정보가 지저분하면(예: 이름 뒤에 "(클릭)" 같은 글자가 붙음, 인스타 링크에
embed.js
같은 불필요한 게 섞임) 깔끔하게 다듬는 후처리 작업을 한다. 중복된 정보는 제거한다.
- 성공 지표: 전에는 비어있던 이름이나 인스타그램 칸에 데이터가 채워지는 경우가 늘어난다.
- 예시/코드 (
3.py
또는4.py
의extract_name...
,extract_instagram...
함수 참고):
# 변경 전 (한 가지 방법만 고집) # name_element = soup.select_one('#아주_구체적인_선택자') # name = name_element.get_text() if name_element else None # 변경 후 (여러 방법 시도 + 후처리) async def extract_name_from_profile_page(html_content: str): # ... (BeautifulSoup 준비) ... name = None # 방법 1: 구체적 선택자 시도 name_element = soup.select_one('#board_content ... h2 > a') if name_element: name = name_element.get_text(strip=True) # 방법 2: 실패 시 다른 방법 (h2 태그 찾기) if not name: board_content = soup.select_one('#board_content > div.board') if board_content: h2_element = board_content.find('h2') if h2_element: name = h2_element.get_text(strip=True) # 방법 3: 후처리 (괄호 내용 제거) if name and "(" in name: name = name.split('(').strip() return name # 핵심 변화 설명: 첫 번째 수색 방법(구체적 주소)이 실패하면, 두 번째 수색 방법(근처 큰 건물 찾기)을 쓰고, 찾은 정보도 깔끔하게 정리해서 최종 결과를 만듦.
- 주의사항: 아무리 여러 방법을 써도 정보 자체가 페이지에 없거나, 예상과 너무 다른 방식으로 숨겨져 있으면 못 찾을 수도 있음. 100% 완벽한 추출은 목표가 아닐 수 있음.
- 똑똑하게 작업 관리하기 (속도 개선 및 안정성 확보)
- 핵심 행동: 컴퓨터가 웹사이트 응답을 기다리며 노는 시간을 줄여서 작업 속도를 높이고, 중간에 멈추더라도 처음부터 다시 시작하지 않도록 한다. 동시에 웹사이트에 너무 피해를 주지 않도록 속도를 조절한다.
- 실행 가이드:
- 한 번에 하나씩 순서대로 처리하는 동기 방식 대신, 여러 작업을 동시에 처리하는 비동기 방식(
asyncio
,httpx
)을 사용한다. - 컴퓨터와 웹사이트 서버에 무리가 가지 않도록, 동시에 진행할 작업의 최대 개수(
asyncio.Semaphore
)를 적절히 설정한다. (너무 많으면 서버가 화내고, 너무 적으면 느림) - 작업 중간 결과를 계속 파일에 저장한다. 예를 들어, 찾아낸 프로필 URL 목록(
collected_profile_urls.txt
)과 이미 처리한 결과(actors_data.csv
)를 기록해두고, 프로그램이 다시 시작될 때 이 파일들을 읽어서 어디까지 작업했는지 확인하고 다음 작업부터 이어서 한다.
- 성공 지표: 수천 개의 프로필을 처리하는 시간이 이전보다 훨씬 단축된다. 실수로 프로그램을 껐다 켜도 처음부터 다시 시작할 필요가 없다.
- 예시/코드: (
3.py
또는4.py
의async def main...
,load_...
,save_...
함수들 참고) - 주의사항: 비동기 코드는 일반적인 순차 코드보다 조금 더 복잡하게 느껴질 수 있음. 동시 작업 개수를 너무 높게 잡으면 '사람인 척'하려는 노력이 무색하게 서버에 차단될 수 있음.
🧠 핵심 개념 해부
- 웹 스크레이핑 (Web Scraping): 일상적 재정의
- 5살에게 설명한다면: 인터넷이라는 큰 그림책에서 내가 원하는 그림(정보)만 골라서 내 스케치북(파일)에 자동으로 베껴 그리는 거야.
- 실생활 예시: 여러 항공사의 비행기 표 가격을 한눈에 비교해주는 사이트, 특정 연예인 관련 뉴스 기사만 매일 아침 모아서 보여주는 앱.
- 숨겨진 중요성: 단순 반복 작업을 컴퓨터에게 맡겨 내 시간을 아낄 수 있고, 사람이 일일이 보기 힘든 엄청난 양의 정보 속에서 패턴이나 의미를 찾아낼 수 있게 해줘.
- 오해와 진실: (오해) 모든 웹사이트는 긁어도 괜찮다. -> (진실) 웹사이트마다 주인이 정한 규칙(robots.txt, 이용 약관)이 있고, 함부로 긁으면 경고를 받거나 법적인 문제가 생길 수도 있어. / (오해) 한 번 만들어두면 평생 쓸 수 있다. -> (진실) 웹사이트는 수시로 디자인이나 구조가 바뀌기 때문에, 내 스크레이핑 코드도 계속 고쳐줘야 해.
- HTTP 요청/응답 (HTTP Request/Response): 일상적 재정의
- 5살에게 설명한다면: 내가 식당(웹사이트)에 가서 "메뉴판 보여주세요!"(요청)라고 말하면, 점원이 "네, 여기 있습니다."(응답) 하면서 메뉴판(웹페이지)을 가져다주는 것과 같아.
- 실생활 예시: 인터넷 주소창에 naver.com 치고 엔터(요청), 네이버 화면이 보이는 것(응답). 쇼핑몰에서 '구매하기' 버튼 클릭(요청), '주문 완료' 페이지가 뜨는 것(응답).
- 숨겨진 중요성: 내가 어떻게 요청하느냐(예: 로그인 정보 포함 여부)에 따라 웹사이트가 다른 응답(로그인된 화면, 에러 화면)을 줄 수 있어. 응답할 때 같이 오는 숫자 코드(200: 성공, 403: 접근 금지, 404: 없음 등)는 내 요청이 어땠는지 알려주는 중요한 신호야.
- 오해와 진실: (오해) 인터넷 접속은 다 똑같다. -> (진실) 요청에는 여러 종류(GET: 정보 보기, POST: 정보 제출 등)가 있고, 요청에 어떤 '짐'(헤더, 쿠키, 데이터)을 실어 보내느냐에 따라 결과가 달라져.
- HTML & CSS 선택자 (HTML & CSS Selectors): 일상적 재정의
- 5살에게 설명한다면: 웹페이지라는 커다란 장난감 상자(HTML)에서, 내가 딱 원하는 '빨간색 로봇 장난감'(정보)을 찾기 위한 '꼬리표'(CSS 선택자) 같은 거야. "상자 오른쪽 구석에 있는 세 번째 칸의 빨간 로봇!" 이렇게.
- 실생활 예시: 블로그 글에서 '작성자 이름' 부분 찾기, 쇼핑몰 페이지에서 '상품 가격' 숫자만 콕 집어내기.
- 숨겨진 중요성: 웹사이트 주인이 장난감 상자 정리를 다시 하면(HTML 구조 변경), 내가 붙여둔 꼬리표가 엉뚱한 걸 가리킬 수 있어. 너무 자세한 꼬리표보다는 좀 더 넓은 의미의 꼬리표(예: '로봇 장난감 칸')를 쓰는 게 더 오래갈 수 있어.
- 오해와 진실: (오해) 브라우저 개발자 도구가 알려주는 선택자 주소는 무조건 정답이다. -> (진실) 개발자 도구가 알려주는 주소는 너무 길고 복잡해서, 상자 정리(HTML 변경)에 매우 취약할 수 있어. 직접 보고 더 간단하고 핵심적인 주소를 찾는 연습이 필요해.
- 비동기 처리 (Asynchronous Processing): 일상적 재정의
- 5살에게 설명한다면: 숙제(작업)를 하는데, 수학 문제(네트워크 요청) 풀다가 답 기다리는 동안 국어 숙제(다른 작업) 먼저 하고, 답 오면 다시 수학 숙제하는 거야. 한 문제 답 기다리느라 멍하니 있지 않으니 숙제가 빨리 끝나!
- 실생활 예시: 여러 개의 큰 파일을 동시에 다운로드 걸어두는 것, 유튜브 영상 보면서 댓글을 스크롤해서 보는 것 (영상 재생과 댓글 로딩이 동시에 진행).
- 숨겨진 중요성: 특히 인터넷에서 정보를 기다리는 시간이 긴 작업(웹 스크레이핑, 파일 다운로드)에서 효과 만점이야. 컴퓨터가 노는 시간을 확 줄여줘. 하지만 코드 짜는 방식이 순서대로 하는 것보다 좀 더 복잡해질 수 있어.
- 오해와 진실: (오해) 비동기는 마법처럼 모든 걸 빠르게 한다. -> (진실) 컴퓨터 내부 계산만 잔뜩 하는 작업은 비동기로 해도 별로 안 빨라져. 뭔가를 '기다리는' 시간이 많은 종류의 작업에서 효과를 발휘해.
🔮 미래 전략 및 지혜
- 예방 전략:
- 긁기 전에 예의 지키기: 스크레이핑하려는 웹사이트의
robots.txt
파일(보통웹사이트주소/robots.txt
)을 확인해서 '긁지 말라'는 부분은 피하고, 웹사이트 이용 약관에 자동 수집 금지 조항이 있는지 살펴본다. - '착한 로봇' 되기: 요청 사이에 반드시 쉬는 시간(
time.sleep
또는 비동기 제어)을 넣고, '나 사람이에요'라고 알리는User-Agent
헤더를 꼭 포함한다. 동시 요청 수는 최소한으로 유지한다. - 실패에 대비하기: 정보를 찾는 코드는 항상 실패할 수 있다고 가정하고, '플랜 B' (다른 선택자, 다른 방식)를 준비하거나, 못 찾았을 때 어떻게 처리할지(예: 빈 값 저장, 로그 남기기) 미리 정해둔다. 오류 처리(
try-except
)는 필수!
- 장기적 고려사항: 웹사이트는 살아있는 생물처럼 계속 변한다는 것을 기억해야 한다. 오늘 잘 되던 코드도 내일은 오류를 낼 수 있으니, 꾸준한 관심과 유지보수가 필요하다. 무분별한 스크레이핑은 법적 문제나 서비스 차단으로 이어질 수 있으니 항상 책임감을 가져야 한다.
- 전문가 사고방식: "어떻게 하면 안 들키고 최대한 많이, 빨리 긁어올까?"가 아니라, "어떻게 하면 웹사이트에 피해를 주지 않으면서, 오랫동안 안정적으로 필요한 데이터를 얻을 수 있을까?"를 고민한다. 단순히 코드를 짜는 것을 넘어, 웹 기술과 데이터 윤리에 대한 이해를 넓히려 노력한다.
- 학습 로드맵:
- 기초 다지기: 파이썬 기본 문법 마스터하기.
- 문 열기:
requests
(동기) 또는httpx
(동기/비동기) 라이브러리로 웹 페이지 내용 가져오기 연습. - 지도 읽기: HTML과 CSS가 웹 페이지를 어떻게 구성하는지 기본 원리 이해하기 (태그, 속성, 구조).
- 보물찾기 도구:
BeautifulSoup
라이브러리로 HTML 속에서 원하는 정보(텍스트, 링크) 찾아내기 연습 (CSS 선택자 활용). - 패턴 인식: 정규 표현식(RegEx)으로 복잡한 텍스트 속에서 이메일, 전화번호 등 특정 패턴 찾아내기 연습.
- (선택) 속도 향상:
asyncio
와httpx
로 비동기 스크레이핑 구현해보기 (효율 증대). - (선택) 전문 장비:
Scrapy
같은 전문 스크레이핑 프레임워크 사용법 익히기 (대규모 작업). - (고급) 난관 극복: JavaScript로 동적으로 변하는 웹사이트 긁는 법 배우기 (
Selenium
등 브라우저 자동화 도구 활용).
🌟 실전 적용 청사진
- 즉시 적용:
- 지금 만들고 있는 스크레이핑 코드에
headers={'User-Agent': '내 브라우저 정보'}
추가하기. - 정보를 찾을 때
try-except
구문으로 감싸서 오류가 나도 프로그램이 멈추지 않게 하기. - 반복문 안에
time.sleep(0.5)
같은 코드를 넣어 최소한의 요청 간격 두기 (0.5초).
- 중기 프로젝트: 내가 자주 방문하는 웹사이트(단, 너무 복잡하지 않은 곳) 하나를 정해서, 첫 3페이지 정도의 특정 정보(예: 게시글 제목과 작성자, 상품 이름과 가격)를 자동으로 수집해서 CSV 파일로 저장하는 나만의 스크레이퍼 만들어보기. (403 오류, 데이터 누락 등 발생 시 해결 시도 포함)
- 숙련도 점검:
- 로그인해야만 볼 수 있는 페이지의 정보를 긁어올 수 있는가? (쿠키/세션 처리)
- 클릭해야 내용이 나타나는 페이지(JavaScript 사용)의 정보를 긁어올 수 있는가?
- 웹사이트에서 IP 차단을 시도할 때, 이를 우회하는 방법(예: 프록시 서버)을 알고 시도해볼 수 있는가? (주의: 기술적/윤리적 고려 필요)
- 긁어온 데이터를 단순히 저장하는 것을 넘어, 내가 원하는 형태로 가공하거나 분석할 수 있는가? (
pandas
라이브러리 활용 등) - 추가 리소스:
- (입문) 생활코딩 WEB2 - Python: https://opentutorials.org/course/3131 (파이썬으로 웹 다루기 기초)
- (초급) 점프 투 파이썬 - 웹 프로그래밍: https://wikidocs.net/book/1 (책 후반부 웹 관련 내용)
- (초중급) BeautifulSoup 공식 문서 (한글 번역도 있음): https://www.crummy.com/software/BeautifulSoup/bs4/doc.ko/
- (중급) 파이썬 공식
asyncio
문서: https://docs.python.org/ko/3/library/asyncio.html - (고급) Scrapy 튜토리얼: https://docs.scrapy.org/en/latest/intro/tutorial.html
- (실전) 구글에서 'python web scraping tutorial', 'python beautifulsoup examples', 'python httpx async example', '웹 크롤링 윤리' 등 검색
📝 지식 압축 요약
- 웹 스크레이핑 시 403 오류는 '로봇 의심' 때문이니, 최대한 '사람인 척'(헤더/쿠키 설정, 속도 조절)해서 접근해야 뚫릴 가능성이 높다.
- 페이지마다 생김새가 다르므로, 데이터 추출은 한 가지 방법만 고집하지 말고 여러 선택지와 후처리, 정규식을 동원해 유연하게 탐색해야 놓치는 정보를 줄일 수 있다.
- 느린 네트워크 작업을 효율화하려면 비동기 처리가 강력하지만, 서버 차단을 피하려면 동시 요청 수를 현명하게 조절하고, 장시간 작업 시에는 이어하기 기능을 구현하는 것이 필수다.
- 성공적인 스크레이핑은 단순히 코딩 기술을 넘어, 웹사이트를 존중하는 '착한 로봇'의 자세와 끊임없이 변화에 적응하는 유지보수 노력이 중요하다.
댓글
댓글 로딩 중...