[인공지능사관학교: 자연어분석A반] JavaScript (6) / Crawling (5)

Suhyeon Lee·2025년 8월 22일

목차

Ⅰ. 오전 수업
	A. 1교시
		1. 지난 시간 복습
		2. 속성 조작: input
	B. 2교시
	    1. 속성 조작: input (cont.)
		2. 속성 조작: 이미지
		3. 속성 조작: 스타일
	C. 3교시
		1. 속성 조작: 스타일 (cont.)
        2. 다중 스타일 제어: 다크 모드
        3. 다음 주 학습할 내용 미리보기

Ⅱ. 오후 수업
	A. 4교시: Crawling
		1. 지난 시간 복습
		2. 실습: 포켓몬 이미지 수집
		3. 실습: 네이버 지도
	B. 5교시: Crawling
		1. 실습: 네이버 지도 (cont.)
	C. 6교시: Crawling
		1. 실습: 네이버 카페 글 수집
Ⅲ. CARRER UP
	A. 포트폴리오 첨삭
	B. 수업 내용 복습
Ⅳ. 하루 돌아보기




Ⅰ. 오전 수업

A. 1교시

1. 지난 시간 복습

  • 실습 POINT
    • 로직을 짜기 위해서는 함수를 먼저 만들고 미리 연결해두기
    • 속성 안 문자열 표시: a 태그
      • 밖이 큰따옴표면 안은 작은따옴표
      • 밖이 작은따옴표면 밖은 큰따옴표
    • 백틱 활용: ul 태그
      • ul은 안에 il가 꼭 필요한데 이걸 한 줄로 이어쓰면 가독성이 떨어짐
      • 태그 안 태그가 있는 문자열인 경우 변수로 생성해 변수 값을 넣는 걸 추천
    • 요소를 변수에 저장할 때는 값이 아닌 요소 자체를 저장 → 그래야 활용성이 올라감
      • 자바스크립트에서 요소를 가지고 오는 getElementBy… 함수 자체가 긴 명령어를 가지고 있어 대부분 요소의 명칭을 짧게 쓰기 위해 요소만 변수에 저장함 → 활용성 up
      • 반복되는 건 "요소"임: 요소 안의 색 꺼내기, 요소 안의 컨텐츠 꺼내기, 요소 안의 클래스 꺼내기. …
    • 숫자가 필요한 경우 숫자로 형변환
      • innerText로 가져오는 건 다 문자열임
  • 이벤트 처리 ★★★
    • 인라인 방식
      • 가장 직관적
      • 태그 안에 직접 JavaScript 로직 작성: onclick = "print()"
      • 단점: 함수명 노출 / 가독성 떨어짐 / HTML과 JS 영역이 겹치기 때문에 유지보수 불편
      • 장점: 빠른 함수 처리
      • 간단한 함수나 내장 함수 넣을 때는 괜찮음 BUT 대부분의 개발자는 숨기는 걸 원칙으로 함
    • 이벤트 리스너 방식
      • 가장 많이 쓰이는 방식
      • 연결도 JavaScript에서 처리 → HTML과 JS 분리
      • addEventListener → 사용자가 어떤 행위를 했을 때 알려주는 역할(이벤트가 동작했을 때 발동) cf. 웹 사이트의 포트(port)도 비슷한 역할을 함(듣는 역할)
      • addEventListener(어떤 이벤트?, 어떤 걸 동작?) → e.g., addEventListener("click", print)
      • 주의: 상수의 이름만 담아야 함 (자바스크립트 영역에서는 () 만나면 실행해버림 cf. HTML은 실행 개념이 없어서 onclick = "print()"로 써도 괜찮음)
      • 매개 변수를 전달할 수 없는 문제 → 익명 함수로 해결
    • 익명 함수
      • 특징: 이름이 정의되지 않았음(이름이 없음) → 부를 수 없음 → 다른 데서 부를 필요가 없는 경우(e.g., 하나의 버튼에만 이벤트 연결하고 싶을 때) 사용
      • 특정 함수를 한 번만 연결할 때 → 배열 관련 함수에서 해봤었음
      • 또는 매개 변수를 포함한 함수를 호출할 때(콜백 함수)
    • 콜백 함수 원리
      • 매개변수로 함수 객체를 전달해서 호출 함수 내에서 매개변수 함수를 실행하는 것
      • 함수를 실행할 때 다른 함수를 통해 안쪽에서 동작하는 로직(그 함수 내부에서 다른 함수를 불러서 동작하는 메커니즘)
      • 코드를 실행할 때 외부에서 전달받은 함수(콜백)가 내부 로직 중에 실행되는 방식

콜백 함수는 다른 함수의 매개변수로 전달되는 함수로서, 외부 함수가 실행되는 도중 또는 특정 이벤트나 상황에 따라 그 내부에서 호출됩니다.
즉, 함수 A가 함수 B(콜백 함수)를 인자로 받아서 함수 A의 로직 중 적절한 시점에 함수 B를 실행하는 구조입니다.
실행 흐름을 외부에서 조정할 수 있고, 원하는 동작을 외부에서 지정할 수 있습니다. 이런 특성은 동기/비동기 처리(예: 이벤트, 데이터 호출 등)와 코드의 재사용이 필요한 다양한 상황에서 활용할 수 있습니다.

2. 속성 조작: 인풋

지난 시간까지는 컨텐츠를 다루었다면 이번 시간은 속성(attribute) 다루는 법을 배울 것

  • 속성이란?
    • 예: 이미지 태그 → src라는 공간이 존재(src는 source의 약자): 이미지의 경로를 담당하는 곳 → 속성에 해당
    • 즉, 태그 안에 존재하는 안의 내용들이 속성임

실습 1: 버튼을 클릭했을 때 input에 적힌 값을 alert로 출력

  1. input 태그, button 태그 만들기
    • input 태그는 '데이터를 넘기는 태그'기 때문에 반드시 name이 필요함
      • 인풋 태그에서 가장 중요한 두 가지 속성: name, value
      • name이 있어야 꺼낼 때 데이터를 조회할 수 있음
      • name이 없으면 오류 발생함
  2. script 열고 함수와 button 연결하기
  3. 로직 작성
    • 이렇게 쓰면 되나요? → 🙅
      • 오류를 찾아 나갈 때 항상 뭐가 안 되는지 파악하기: 알림창은 떴는데 내용이 없음 → 알림창이 떴다는 건 함수 연결 및 동작은 정상이라는 것 → 즉, text가 비어있다는 의미
      • innerText는 "컨텐츠"에 접근해 값을 꺼내오는데 input 태그는 컨텐츠 영역이 없고 모든 값을 value라는 공간에 저장함 → 따라서 이렇게 쓰면 빈 내용만 출력됨
      • innerText, innerHTML은 열린 태그와 닫는 태그 사이에 있는 컨텐츠를 조작함
      • input 태그는 컨텐츠 영역 자체가 없음 → 우리 눈에는 보이지 않지만 value라는 속성이 존재하고 입력받은 값을 value 속성에 저장함 (value에 저장된 값이 들어가는 개념)
    • 그럼 어떻게 처리해야 하죠? → innerText 키 값을 다른 걸로 바꾸기 → 속성에 접근해야 함
  • DOM에서 특정 값을 접근할 때는 key 값으로 접근한다
    • 모두 객체 기반이기 때문에 모든 데이터는 key 값으로 접근할 수 있음(사실 안 되는 애도 있음😅: class)
    • 객체 기반?
      • 우리 눈에는 한 줄이지만 컴퓨터는 실제로 한 줄을 불러온 다음 쪼개서 관리함 → 모든 데이터가 객체로 쪼개져서 관리되고 있다!
  • 자바스크립트에서 속성 접근할 때: 함수가 없음. 모든 걸 다 key 값 베이스로 접근하면 됨 (class 제외)
    • class는 왜? → 여기에서 이미 이야기했었음: class 객체를 만들 때 쓰는 class 예약어 keyword가 이미 있기 때문
    • 대신 className, classList를 사용
      • 왜 class만 특별하게 name과 list가 있을까? class는 여러 개를 가질 수 있기 때문에 복수개의 class인 경우 리스트로 반환 가능(하나면 name 쓰면 됨)
  • 정리
    • input 태그는 모든 값을 value 속성에 저장한다. → innerText로 접근이 불가능
      • innerText, innerHTML은 컨텐츠에 접근/조작하는 용도
    • 중요! 자바스크립트로 특정 속성에 접근할 때는 속성명은 키 값으로 활용 ✨ → 속성 제어
      • 모든 속성이 다 되는 건 아님
      • class는 예약어라서 사용 불가 → classList, className 활용해 조회 가능
  • 심화: attributes, properties 구분하기

자바스크립트에서 attributes(어트리뷰트)와 properties(프로퍼티)는 비슷하게 보일 수 있지만, 아래와 같이 중요한 차이가 있습니다.

  1. 정의와 위치

    • Attribute(어트리뷰트): HTML 문서에 직접 정의된 정적 속성
      • 예시: <input value="기본값" /> → 여기서 value="기본값"이 attribute입니다.
    • Property(프로퍼티): 자바스크립트에서 DOM 객체의 속성으로 직접 접근하거나 변경할 수 있는 동적 속성
      • 브라우저가 HTML을 읽어 DOM(Document Object Model) 객체를 만들 때, 해당 attribute가 DOM 객체의 property로 변환됩니다.
      • 예시: input.value → DOM의 property
  2. 값의 변화

    • Attribute는 HTML 문서에 정의된 초기값만을 가집니다.

      • 문서를 다시 읽지 않는 한 값이 변하지 않습니다.
    • Property는 자바스크립트, 사용자 이벤트(입력 등)에 의해 실시간으로 값이 변할 수 있습니다.

      • 입력값, 선택상태, 스타일 등 DOM 객체 상태가 바뀌면 property값도 바뀝니다.
    • 예시:

      <input value="hello" />
      const input = document.querySelector('input');
      input.value = "world"; // Property 값 변경 가능
      
      input.getAttribute('value'); // "hello"
      input.value; // "world"
      • 여기서, HTML attribute 값은 그대로 "hello"이고, property인 value는 "world"로 변경됩니다.
  3. 접근 방법

    • Attribute 접근
      • element.getAttribute('속성명'), element.setAttribute('속성명', 값)
    • Property 접근
      • element.속성명 (예: input.value, input.checked)
  4. 사용자 정의 속성의 차이

    • HTML에 직접 설정하지 않은 사용자 정의 속성(custom attribute)은 property로 바로 접근할 수 없습니다.
    • 예시:
      <input custom="data" />
      input.custom; // undefined
      input.getAttribute("custom"); // "data"
      • 사용자 정의 속성은 getAttribute로만 접근 가능하며, DOM property로 바로 접근할 수 없습니다.
  • 정리
    • attribute: HTML 태그에 직접 작성된 정적인
    • property: DOM 객체의 상태를 나타내는 동적인
    • 자바스크립트로 요소를 조작할 때는 property를 주로 사용하지만, 실제 HTML 문서의 값을 확인/변경하려면 attribute를 사용하는 것이 적합합니다.
  • 실제 프로젝트에서는
    • 값이 변하지 않는 HTML 설정, 초기값 확인 → attribute
    • 값이 실시간으로 변하고, 스크립트로 조작해야 할 때 → property

실습 2: 버튼을 클릭하면 패스워드가 눈에 보이게 작성

  • 패스워드 숨겼다가 보였다가
    • input 태그 기본 attribute에 있긴 하지만 로직 파악을 위해 아래와 같이 실습 진행
  1. 패스워드 입력 창 만들기
    • 실행 결과

B. 2교시

1. 속성 조작: input (cont.)

  1. 함수 껍데기 만들고 버튼과 연결시키기
  2. 로직 작성: 조건문
    • key 값 베이스라 바꾸고자 하는 값을 대입(=)만 하면 됨
  • Tip: 비교 시 피연산자의 값과 타입 모두 비교하는 ===(일치 비교, 엄격한 비교) 사용을 권장하지만(표준 작성법이지만) 보통 개발자들은 보통 ==(동등 비교, 느슨한 비교)를 씀 → 대부분이 텍스트이기 때문이라고 함(pw.type은 어차피 텍스트니까 굳이 ===를 쓸 필요가 없음)
    • 그래도 자바스크립트에서는 ===를 기본적으로 사용하는 것이 좋은 습관!

2. 속성 조작: 이미지

  • 실습 목표: 버튼을 클릭했을 때 src값이 img1이라면 img2로 변경, img2인 경우에는 img1으로 변경
  • 순서: 함수 제작 → 버튼 연결 → 조건문 로직 작성
  1. img 태그, button 태그 만들기
    • Tip: div 안 쓰고 버튼과 이미지 줄 바꾸고 싶으면 둘 중 하나의 display 속성을 'block'으로 바꾸면 됨
  2. 함수 껍데기 만들고 버튼과 연결시키기
  3. 로직 작성: 조건문
    • 위 코드보다 더 좋은 코드도 있고 한 줄로 작성할 수도 있음
      • 삼항연산자(ternary operator): 조건에 따라 두 가지 값 중 하나를 간단하게 선택 → 조건식 ? 참일 때 값 : 거짓일 때 값
    • 중요한 건 로직을 익히는 것

3. 속성 조작: 스타일 (CSS)

  • 표준 변경(현업에서 쓰는 방식의 기술)은 아니지만 직관적으로 바꿀 수 있는 방법을 소개
    • CSS 인라인 방식 → 스타일 속성에 접근하기

실습: 버튼을 누르면 span 태그의 글자 색은 파란색으로, 글자 크기는 30px로 변경

  1. 껍데기 만들고 연결
  2. 속성에 접근하기
    • style 태그가 없는데요? → span 태그에 접근해서 style attribute를 꺼내올 것: 'style' key에 접근하기
    • 주의: style attribute는 "객체" 구조임 → 우리는 style 중 color에 blue를 넣어야 함 → key로 'color'만 꺼내기: 여기에서 이미 다룬 개념!
      • console.log(span.style);로 구조 확인
      • 주의: style에서는 'font-size'이지만 DOM properties에서는 'fontSize'임 (객체에서 꺼낼 때 글자에 hyphen을 못 넣음)
방법가능 여부예시
점 표기법Xobj.my-name (오류)
대괄호+문자열 표기법Oobj["my-name"]
리터럴(따옴표포함)O{"my-name": "value"}

점(.) 표기법에서는 하이픈 사용 불가 → 하이픈(-)은 연산자(뺄셈)로 인식되기 때문
obj.my-name → 이 문장은 obj.my에서 name을 빼는 연산이 되어버립니다
따옴표(" "), (' ')로 감싸면 가능 → 리터럴 선언할 때, 키를 문자열로 작성하면 하이픈 포함 가능(let obj = { "my-name": "홍길동" };)
하이픈이 들어간 key는 반드시 대괄호 표기법과 문자열로 접근해야 함(obj["my-name"] = "홍길동2";)
일반적으로 하이픈 대신 카멜케이스(myName) 혹은 언더스코어(my_name)를 사용하는 것이 권장되지만, API 응답 처리 혹은 외부 데이터(JSON 등)에는 하이픈이 흔히 쓰이므로 위 규칙을 기억하면 됩니다.

  1. 코드 작성
    • 불편한 점 찾아보기
      • 코드를 완성한 뒤 항상 어떻게 줄일까를 고민하는 습관 만들기
        → span.style이 반복되고 있음!
    • 한 문장으로 쓸 수 있는 key가 따로 있음: cssText
  • 위 방식은 실제 현업에서는 많이 사용하지 않음
    • 인라인 방식이라 단점 많음
    • 짧고 간단한 한두 개 스타일에는 괜찮지만 많은 양을 처리할 때는 합리적이지 않음

C. 3교시

1. 속성 조작: 스타일 (cont.)

  • '인라인 방식' 제어의 단점
    • 여러 개의 스타일을 제어할 때는 가독성 X
    • 유지 보수 X
    • 재사용성 X
  • 그럼 안 쓰나요?
    • 단순 스타일(간단한 1~2개 스타일) 제어에는 효율적 → 간단한 속성을 지정할 때는 괜찮아요!
      • 예시 1: div 태그에 style이 인라인 방식으로 들어가 있음 (한솥도시락 메뉴에 마우스 올리면 세부 메뉴 보이는 곳임)
      • 마우스를 올리면 아래와 같이 display 속성이 바뀜
  • 현업에서는 다른 방식을 사용

2. 다중 스타일 제어: 다크 모드

  • 다크 모드의 원리
    • css의 원리를 이용 → css에 스타일을 만들어 놓고 갈아끼우기
      • 우리가 앞서 진행한 인라인 방식은 필요할 때마다 매번 옷을 만들어 입는 것과 같음
      • css는 미리 옷을 만들어 놓고 갈아입는 것: 디자인을 매번 새롭게 만드는 것이 아니라 미리 만들어 놓고 필요할 때마다 갈아 끼움

원래는 외부 방식을 사용해야 맞지만 원리만 파악하기 위해 내부 방식으로 실습 진행

  1. 미리 스타일 두 개 만들어 놓기: class명 이용
  2. body 태그에 class 값 부여: 기본 상태 만들기
    • 실행 결과
  3. button 태그 만들고 script에서 함수 껍데기 만들고 연결
  4. 로직 작성
    • body 태그에 id가 없는데 어떻게 데려오죠? → ID 안 적고 데려오는 방법이 있음
      • 주의: document.getElementsByTagName("body")는 리턴 타입 자체가 복수개의 배열(HTMLcollection)이기 때문에 [0]으로 하나만 꺼내야 함
  5. 사용자가 내 페이지를 볼 때 이상하게 느낄만한 점 찾아서 고치기
    • 현재 작성한 코드는 다크 모드일 때도 버튼 이름이 '다크 모드 변경'이라 불편 → 버튼 글자가 일반 모드일 때는 '다크 모드 변경', 다크 모드일 때는 '일반 모드 변경'으로 표시되게 하기

body 태그 가져오는 방법은 사실 여러 개
1. document.getElementsByTagName("body")[0]
2. document.querySelector("body")
3. document.body
→ 프로그래밍에서 가장 중요한 건 "직관성"이라 대부분 1 아니면 2 씀
→ form 태그 접근 시에는 3 같은 방식을 많이 쓴다고 함

3. 다음 주 학습할 내용 미리보기

  • 페이지
  • 코드
    • 태그를 만들 수도 있음
    • 새로운 개념도 배울 예정: appendChild



Ⅱ. 오후 수업

A. 4교시: Crawling

1. 지난 시간 복습

  • 자동화 도구를 이용한 데이터 수집
    • Gmarket
      • 로봇 동작 회피: undetected-chromedirver
    • Pokemon
      • os 라이브러리를 통하여 바탕 화면에 "포켓몬" 폴더 생성: os.mkdir("경로")
      • 예외 처리하는 법: if-else
      • 스크롤하는 법
      • 속성명 추출 방법: 요소.get_attribute("속성명")
      • 이미지 경로를 활용해 로컬 폴더에 이미지 파일 저장: urlretrieve(이미지 주소, 저장 경로/파일이름.jpg)

2. 실습: 포켓몬 이미지 수집

  • 포켓몬 도감 생성을 위하여 여러 마리의 포켓몬 수집
    • 반복문 활용
# 브라우저 열기
driver = wb.Chrome()
url = "https://www.pokemonkorea.co.kr/pokedex#pokedex_1"
driver.get(url)
time.sleep(1)

# end 키 입력 반복
body = driver.find_element(By.CSS_SELECTOR,"body")
for i in range(20):
    body.send_keys(Keys.END)
    time.sleep(1)

# 이미지 데이터 태그 수집
img = driver.find_elements(By.CSS_SELECTOR, "div.img > div > img")

# img_list에 순수한 src 정보만 담기
img_list = []
for i in img:
    img_list.append(i.get_attribute("src"))

# 전체 사진 jpg 파일로 저장하기
for i in range(len(img)):
    urlretrieve(img_list[i],f"C:/Users/내 PC/Desktop/pokemon/{i+1}.jpg")

# 브라우저 닫기
driver.quit()
  • 응용: 포켓몬 도감 번호와 이름으로 파일명 만들어 저장하기
# 브라우저 열기
driver = wb.Chrome()
url = "https://www.pokemonkorea.co.kr/pokedex#pokedex_1"
driver.get(url)
time.sleep(1)

# end 키 입력 반복
body = driver.find_element(By.CSS_SELECTOR,"body")
for i in range(20):
    body.send_keys(Keys.END)
    time.sleep(1)
    
# 태그 수집: 도감 번호, 이름
pokename = driver.find_elements(By.CSS_SELECTOR, "div.bx-txt > h3")
num_list = [i.text.split()[0] for i in pokename]
name_list = [i.text.split()[1] for i in pokename]

# 이미지 데이터
img = driver.find_elements(By.CSS_SELECTOR, "div.img > div > img")

# img_list에 순수한 src 정보만 담기
img_list = []
for i in img:
    img_list.append(i.get_attribute("src"))

# 전체 사진 jpg 파일로 저장하기
for i in range(len(img)):
    urlretrieve(img_list[i],f"C:/Users/내 PC/Desktop/pokemon/{num_list[i]}_{name_list[i]}.jpg")

# 브라우저 닫기
driver.quit()

3. 실습: 네이버 지도

  • 학습 목표
    • 네이버 지도를 통해 맛집 리스트, 맛집 리뷰 데이터, 맛집 메뉴 데이터를 수집할 수 있다.
# 라이브러리 불러오기
from selenium import webdriver as wb
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time

# 웹페이지 열기
url = "https://map.naver.com/p/"
driver = wb.Chrome()
driver.get(url)

# 웹 페이지 최대화
driver.maximize_window()

# '동명동 맛집' 검색 후 나오는 첫 번째 음식점 이름 추출
search = driver.find_element(By.CLASS_NAME,"input_search")
search.send_keys("동명동 맛집")
search.send_keys(Keys.ENTER)


# 첫 번째 음식점 태그 가져오기
# copy selector → #_pcmap_list_scroll_container > ul > li:nth-child(1) > div.CHC5F > div.bSoi3 > a > span.TYaxT
shop = driver.find_elements(By.CSS_SELECTOR,"div.bSoi3 > a > span.TYaxT")
shop
[]

→ 으악 빈 리스트다! iframe인가?

url = "https://pcmap.place.naver.com/restaurant/list?query=%EB%8F%99%EB%AA%85%EB%8F%99%20%EB%A7%9B%EC%A7%91&x=126.92312494606728&y=35.1449489999999&clientX=126.923125&clientY=35.144949&bounds=126.91841498704025%3B35.14014128045146%3B126.9280494818218%3B35.14958098670637&display=70&ts=1755837614019&additionalHeight=76&locale=ko&mapUrl=https%3A%2F%2Fmap.naver.com%2Fp%2Fsearch%2F%EB%8F%99%EB%AA%85%EB%8F%99%20%EB%A7%9B%EC%A7%91#"
driver = wb.Chrome()
driver.get(url)
driver.maximize_window()
shop = driver.find_elements(By.CSS_SELECTOR,"div.bSoi3 > a > span.TYaxT") # driver.find_elements(By.CLASS_NAME,"TYaxT")도 됨
shop[0].text
'쭈꾸미잡스'

일단 requests & beautifulsoup에서 했던 것처럼 실제 주소를 가져와 재요청했는데 이렇게 url을 직접 바꿔서 들어가는 건 너무 비효율적이다. 다른 방법은 없을까? → 있다고 함!

  • find_element 해 보면 iframe이라는 걸 금방 알 수 있음
    • NoSuchElementException 오류가 뜨기 때문
  • 올바르게 데이터 경로를 작성했음에도 "NoSuchElementException" 에러가 뜰 경우
    • 화면 구성 → iframe 의심!
    • 개발자 모드에서 ctrl+f 후 'iframe' 검색해 확인해보기

B. 5교시: Crawling

1. 실습: 네이버 지도 (cont.)

  • selenium에서 iframe 해결하기
    • 선택자를 문제없이 추출했음에도 수집이 불가능하다면? → iframe 구조를 의심하기!
    • 핵심: selenium 라이브러리를 활용하면 iframe의 새로운 url에 직접 접근하는 것이 아니라 자동으로 화면 이동할 수 있음
      • requests처럼 페이지를 계속 여는 건 비효율적 → requests는 동적이 아니라서 iframe 주소를 직접 넣어 주었지만 selenium은 동적이라 url 입력 없이 화면을 옮겨갈 수 있음
    • 사용법: driver야 iframe 으로 접근해~ → switch_to.frame()
  1. 현재 html 페이지에서 id='searchiframe' <iframe> 요소를 찾음
  2. searchiframe 요소로 접근! → 전환 선언
# 현재 html페이지에서 동명동 맛집 데이터가 들어 있는 <iframe> 요소를 찾기 → id = 'searchIframe'
search_iframe = driver.find_element(By.CSS_SELECTOR, '#searchIframe')
# searchIframe 요소로 접근! -> 전환선언
driver.switch_to.frame(search_iframe)
# iframe 페이지로 driver 가 이동함

# 첫 번째 음식점 요소 추출
result = driver.find_element(By.CSS_SELECTOR, "#_pcmap_list_scroll_container > ul > li:nth-child(1) > div.CHC5F > div.bSoi3 > a > span.TYaxT")
result.text
'쭈꾸미잡스'
  • iframe 접근하여 데이터 수집할 때 주의점!
    • 하나의 iframe(1번)에서 또 다른 iframe(2번)으로 접근할 때 1번에서 2번으로 바로 이동 불가
      • 1번과 2번은 서로 모르는 사이니까 둘 사이를 연결해 주는 경유지를 거쳐야 함
    • 매개체 역할을 하는 원본 html(최상위 html)로 다시 이동 후 다른 iframe(2번)으로 이동
    • 흐름: 원본 html → 1번 iframe → 클릭 → 원본 html → 2번 iframe → 클릭
# 리뷰 수집을 위해 첫 번째 음식점 눌러서 들어가기
result.click()

# 원본 html로 돌아가기
driver.switch_to.default_content()
# 화면 전환을 하는 건 아니고 드라이버가 일하는 장소만 바뀜

# 두 번째 iframe(음식점 이름 누른 뒤 뜨는 곳) 이동
# 요소를 추출하여 공유 → 전환
entry_iframe = driver.find_element(By.CSS_SELECTOR,'#entryIframe')
driver.switch_to.frame(entry_iframe)

# 리뷰 데이터 수집을 위해 이동
# #app-root > div > div > div.place_fixed_maintab > div > div > div > div > a:nth-child(5)
review = driver.find_element(By.CSS_SELECTOR,"#app-root > div > div > div.place_fixed_maintab > div > div > div > div > a:nth-child(5)")
review.click()

# 1번 리뷰 절대 경로: #_review_list > li:nth-child(1) > div.pui__vn15t2 > a:nth-child(1)
# 2번 리뷰 절대 경로: #_review_list > li:nth-child(2) > div.pui__vn15t2 > a
# 3번 리뷰 절대 경로: #_review_list > li:nth-child(3) > div.pui__vn15t2 > a:nth-child(1)
# 2번만 다른 이유? "더보기" 있고 없고 (2번은 없음)
review_txt = driver.find_elements(By.CSS_SELECTOR,"#_review_list > li > div.pui__vn15t2 > a")

review_txt[0].text
'어제 티비보다가 쭈꾸미볶음 나와서 검색하고 찾은 곳이예요✨\n후회없이 맛있게 먹고 왔습니다🙂\n매콤맛이 제일 맛잇게 먹을 수 있는 정도엿고\n많이 맵게는 남편이 좀 많이 매워하긴 햇지만 저는 맛잇게 먹었어요!\n양이 많은 편은 아니구요!\n저희는 항상 적당히 먹는 편이긴한데 3인분 시켜서 배부르게 잘 먹고 왔어요~\n주차는 불편할것 같지만 저는 늦은 점심시간에 방문해서 사장님의 배려로 주차도 편하게 하고 맛있게 먹고 왔어요!!\n비오는날 소주한잔에 쭈꾸미 볶음 한번만 먹어주세오🙏 ㅠㅠㅠㅋㅋㅋㅋㅋ 넘 맛잇고 찰떡궁합 ㅠㅠㅠ'

C. 6교시: Crawling

1. 실습: 네이버 카페 글 수집

  • 학습 목표
    • 네이버 카페 글 수집
      • 하나의 키워드에 대해 다양한 카페의 글을 수집
  • 키워드로 카페 글 수집
    • 키워드: '음식물 처리기 사용 후기'
    • 기간 설정: 3개월
  • url 패턴 분석을 통해 기간과 키워드를 url에 포함시켜 데이터 수집
    • 주의 사항: url에는 공백이 들어가면 안 됨 → 공백을 %20으로 대체하기
    • 기간패턴: 0(전체), 1(1시간), 2(1일), 3(1주), 4(1개월), 5(3개월), 6(6개월), 7(1년)
# 라이브러리 불러오기
from selenium import webdriver as wb
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time
from tqdm import tqdm

data_option = "5" # 기간의 패턴 값이 정해져 있음 → 기간패턴: 0(전체), 1(1시간), 2(1일), 3(1주), 4(1개월), 5(3개월), 6(6개월), 7(1년)
keyword = "음식물%20처리기%20사용%20후기" 
url = f"https://search.naver.com/search.naver?cafe_where=&date_option={data_option}&prdtype=0&query={keyword}&sm=mtb_opt&ssc=tab.cafe.all&st=rel&stnm=rel&opt_tab=0&nso=so%3Ar%2Cp%3A3m"

# 드라이버로 웹 페이지 열기
driver = wb.Chrome()
driver.get(url)
time.sleep(1)

# 웹 페이지 최대화
driver.maximize_window()

# 다양한 데이터 수집을 위해 스크롤 진행
for i in range(20):
    body = driver.find_element(By.TAG_NAME,"body")
    body.send_keys(Keys.END)
    time.sleep(1)

# 스크롤 후 수집한 데이터 수 확인
get_title = driver.find_elements(By.CSS_SELECTOR,".title_link")
len(get_title)
630
  • 전체 내용을 수집하기 위해 카페 글 제목에 포함되어 있는 게시글 링크를 통하여 원본 글 수집
# 게시글의 링크를 리스트에 누적
href_list = []
for i in get_title:
    href_list.append(i.get_attribute("href"))
    
# 한 개의 카페 글 수집(iframe)
# 카페 글로 이동: 드라이버에게 첫 번째 카페 주소로 이동하라고 말해주기
driver.get(href_list[0])

# iframe 화면 전환
driver.switch_to.frame("cafe_main")
# 글 수집
content = driver.find_element(By.CSS_SELECTOR,".se-main-container")
  • 반복을 통해여 전체 글 수집
cafe_contents = []
for i in tqdm(range(100)):
    driver.get(href_list[i])
    
    # 지정된 초만큼 웹 로딩을 기다리지만, 그 전에 로딩 완료 시 넘어가도록 하는 코드
    driver.implicitly_wait(3)

    # 화면 전환
    driver.switch_to.frame("cafe_main")
    # 글 수집
    content = driver.find_element(By.CSS_SELECTOR,".se-main-container")
    cafe_contents.append(content.text)

    time.sleep(1)

driver.quit()




Ⅲ. CARRER UP

A. 포트폴리오 첨삭

B. 복습





하루 돌아보기

👍 잘한 점

  • 수업 시간에 질문 많이 하고 대답도 많이 했음

👎 아쉬웠던 점

  • 포트폴리오, 미니 프로젝트 하느라 복습을 많이 못했음

🔬 개선점

  • 시간 분배 잘 하기
profile
2 B R 0 2 B

0개의 댓글