Ⅰ. 오전 수업
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. 수업 내용 복습
Ⅳ. 하루 돌아보기
onclick = "print()"addEventListener → 사용자가 어떤 행위를 했을 때 알려주는 역할(이벤트가 동작했을 때 발동) cf. 웹 사이트의 포트(port)도 비슷한 역할을 함(듣는 역할)() 만나면 실행해버림 cf. HTML은 실행 개념이 없어서 onclick = "print()"로 써도 괜찮음)콜백 함수는 다른 함수의 매개변수로 전달되는 함수로서, 외부 함수가 실행되는 도중 또는 특정 이벤트나 상황에 따라 그 내부에서 호출됩니다.
즉, 함수 A가 함수 B(콜백 함수)를 인자로 받아서 함수 A의 로직 중 적절한 시점에 함수 B를 실행하는 구조입니다.
실행 흐름을 외부에서 조정할 수 있고, 원하는 동작을 외부에서 지정할 수 있습니다. 이런 특성은 동기/비동기 처리(예: 이벤트, 데이터 호출 등)와 코드의 재사용이 필요한 다양한 상황에서 활용할 수 있습니다.
지난 시간까지는 컨텐츠를 다루었다면 이번 시간은 속성(attribute) 다루는 법을 배울 것






자바스크립트에서 attributes(어트리뷰트)와 properties(프로퍼티)는 비슷하게 보일 수 있지만, 아래와 같이 중요한 차이가 있습니다.
정의와 위치
<input value="기본값" /> → 여기서 value="기본값"이 attribute입니다. input.value → DOM의 property값의 변화
Attribute는 HTML 문서에 정의된 초기값만을 가집니다.
Property는 자바스크립트, 사용자 이벤트(입력 등)에 의해 실시간으로 값이 변할 수 있습니다.
예시:
<input value="hello" />
const input = document.querySelector('input');
input.value = "world"; // Property 값 변경 가능
input.getAttribute('value'); // "hello"
input.value; // "world"
접근 방법
element.getAttribute('속성명'), element.setAttribute('속성명', 값)element.속성명 (예: input.value, input.checked)사용자 정의 속성의 차이
<input custom="data" />input.custom; // undefined
input.getAttribute("custom"); // "data"getAttribute로만 접근 가능하며, DOM property로 바로 접근할 수 없습니다.



=)만 하면 됨===(일치 비교, 엄격한 비교) 사용을 권장하지만(표준 작성법이지만) 보통 개발자들은 보통 ==(동등 비교, 느슨한 비교)를 씀 → 대부분이 텍스트이기 때문이라고 함(pw.type은 어차피 텍스트니까 굳이 ===를 쓸 필요가 없음)===를 기본적으로 사용하는 것이 좋은 습관!



조건식 ? 참일 때 값 : 거짓일 때 값



| 방법 | 가능 여부 | 예시 |
|---|---|---|
| 점 표기법 | X | obj.my-name (오류) |
| 대괄호+문자열 표기법 | O | obj["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 등)에는 하이픈이 흔히 쓰이므로 위 규칙을 기억하면 됩니다.


cssText


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





document.getElementsByTagName("body")는 리턴 타입 자체가 복수개의 배열(HTMLcollection)이기 때문에 [0]으로 하나만 꺼내야 함
body 태그 가져오는 방법은 사실 여러 개
1.document.getElementsByTagName("body")[0]
2.document.querySelector("body")
3.document.body
→ 프로그래밍에서 가장 중요한 건 "직관성"이라 대부분 1 아니면 2 씀
→ form 태그 접근 시에는 3 같은 방식을 많이 쓴다고 함




os.mkdir("경로")요소.get_attribute("속성명")urlretrieve(이미지 주소, 저장 경로/파일이름.jpg)# 브라우저 열기
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()
# 라이브러리 불러오기
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을 직접 바꿔서 들어가는 건 너무 비효율적이다. 다른 방법은 없을까? → 있다고 함!

switch_to.frame()
<iframe> 요소를 찾음# 현재 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
'쭈꾸미잡스'
# 리뷰 수집을 위해 첫 번째 음식점 눌러서 들어가기
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비오는날 소주한잔에 쭈꾸미 볶음 한번만 먹어주세오🙏 ㅠㅠㅠㅋㅋㅋㅋㅋ 넘 맛잇고 찰떡궁합 ㅠㅠㅠ'
%20으로 대체하기# 라이브러리 불러오기
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()