인프런 영문 브랜드 로고
인프런 영문 브랜드 로고

인프런 커뮤니티 질문&답변

최지훈님의 프로필 이미지
최지훈

작성한 질문수

[2024 개정판] 이것이 진짜 크롤링이다 - 실전편 (인공지능 수익화)

마지막 페이지 오류 + class 명 오류

작성

·

869

1

안녕하십니까 선생님

최근에 마지막 페이지 오류를 해결하는 방법을 질문드렸는데, 친절히 답변주셔서 감사합니다.

수정해주신 코드로는 크롤링이 정상적으로 작동이 되는 것을 확인했습니다.

강사님께서 수정해주신 버튼 html 코드는 다음과 같습니다.

isLastPage = soup.select_one('a.btn_next').attrs['aria-disabled']

그러나,

1 of 3. 네이버에 "미옹이"를 검색한 다음,
페이지를 이동하는 버튼의 html을 보면, btn_next라는 class는 사진에 보이듯이 하나밖에 없고,
하나밖에 없는 해당 태그는 a가 아니라 button으로 나옵니다.
어느 부분에서 강사님께서는 해당 태그가 a태그로 판단하신 것인지 궁금합니다.
button 위에 있는 a태그는 옆으로 이동하는 버튼의 태그가 아니라, 1페이지, 2페이지의 a태그여서 버튼과는 관계없다고 생각합니다.

# 네이버 "미옹이" 검색결과의 html

 

2 of 3. class 명을 일부만 적는 것이 이해되지 않습니다.

강사님께서 미옹이의 코드를 작성하실 때, class가 btn_next dimmed임에도 불구하고, btn_next만 명령어에 입력하셨습니다.

마찬가지로,

위 사진에 나와 있는 네이버 지도 크롤링 과정에서도 class 태그 4개 중에 input_search만 명령어에 입력하셨는데,

search = browser.find_element(By.CSS_SELECTOR, "input.input_search")

전체 class인 input.input_search.ng-valid.ng-dirty.ng-touched를 입력하면 오류가 발생합니다.

search = browser.find_element(By.CSS_SELECTOR, "input.input_search.ng-valid.ng-dirty.ng-touched")

input.input_search와 input.input_search.ng-valid.ng-dirty.ng-touched 모두

html 상에서 검색했을 때 동일하게 1 of 1이 뜨는것을 확인했는데도 왜 전체 class태그를 입력한 것에서 오류가 발생하는지 궁금합니다.

정리하자면, class 태그를 full로 완전히 입력할때는 오류가 뜨더니(input.input_search.ng-valid.ng-dirty.ng-touched),

class의 일부인 input.input_search를 넣으면 오류가 뜨지 않습니다.

따라서 어느 상황에서 class를 일부만 사용하고, 또는 전체를 사용해야 하는지 판단하는데 어려움이 있습니다.

아래는 발생하는 오류입니다.

>>> search = browser.find_element(By.CSS_SELECTOR, "input.input_search.ng-valid.ng-dirty.ng-touched") Traceback (most recent call last): File "<stdin>", line 1, in <module> File "C:\Users\rnrxh\AppData\Local\Programs\Python\Python310\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 857, in find_element return self.execute(Command.FIND_ELEMENT, { File "C:\Users\rnrxh\AppData\Local\Programs\Python\Python310\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 435, in execute self.error_handler.check_response(response) File "C:\Users\rnrxh\AppData\Local\Programs\Python\Python310\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 247, in check_response raise exception_class(message, screen, stacktrace) selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":"input.input_search.ng-valid.ng-dirty.ng-touched"} (Session info: chrome=103.0.5060.134) Stacktrace: Backtrace: Ordinal0 [0x00575FD3+2187219] Ordinal0 [0x0050E6D1+1763025] Ordinal0 [0x00423E78+802424] Ordinal0 [0x00451C10+990224] Ordinal0 [0x00451EAB+990891] Ordinal0 [0x0047EC92+1174674] Ordinal0 [0x0046CBD4+1100756] Ordinal0 [0x0047CFC2+1167298] Ordinal0 [0x0046C9A6+1100198] Ordinal0 [0x00446F80+946048] Ordinal0 [0x00447E76+949878] GetHandleVerifier [0x008190C2+2721218] GetHandleVerifier [0x0080AAF0+2662384] GetHandleVerifier [0x0060137A+526458] GetHandleVerifier [0x00600416+522518] Ordinal0 [0x00514EAB+1789611] Ordinal0 [0x005197A8+1808296] Ordinal0 [0x00519895+1808533] Ordinal0 [0x005226C1+1844929] BaseThreadInitThunk [0x75C96739+25] RtlGetFullPathName_UEx [0x77A28FEF+1215] RtlGetFullPathName_UEx [0x77A28FBD+1165] >>> search.click() Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'search' is not defined >>> time.sleep(1) >>> # 검색어 입력 >>> >>> search.send_keys("강남역 맛집") Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'search' is not defined >>> search.send_keys(Keys.ENTER) # 검색어를 입력하고, 엔터를 치라는 명령어 Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'search' is not defined >>> time.sleep(2)

아래는 제가 작성한 코드입니다.

# 셀레니움 기본설정
import imp
from lib2to3.pgen2 import driver
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time

# 크롬 드라이버 자동 업데이트
from webdriver_manager.chrome import ChromeDriverManager

# 브라우저 자동으로 꺼짐 방지
chrome_options = Options()
chrome_options.add_experimental_option("detach", True)

# 불필요한 에러 메시지 삭제
chrome_options.add_experimental_option("excludeSwitches", ["enable-logging"])

# 셀레니움으로 웹브라우저 자동으로 띄우기
# ChromeDriverManager를 통해서 ChromeDriver를 설치하고, Service라는 객체를 만든 뒤, service라는 변수에 저장한다
service = Service(executable_path=ChromeDriverManager().install())
browser = webdriver.Chrome(service=service, options=chrome_options)

# 특정 웹페이지 주소로 이동
browser.implicitly_wait(5) # 웹페이지가 로딩 될때까지 5초는 기다려준다
browser.maximize_window() # 화면 최대화
browser.get('https://map.naver.com/v5/?c=14141023.2182021,4522932.8702864,15,0,0,0,dh')

# 검색 배너 클릭
search = browser.find_element(By.CSS_SELECTOR, "input.input_search.ng-valid.ng-dirty.ng-touched")
search.click()
time.sleep(1)

# 검색어 입력
search.send_keys("강남역 맛집")
search.send_keys(Keys.ENTER) # 검색어를 입력하고, 엔터를 치라는 명령어
time.sleep(2)
 

3 of 3. 왜 id를 사용하지 않으셨는지 궁금합니다.

사진에는 class 뿐만 아니라 해당 태그의 고유값인 id도 있습니다.

그러나, id를 사용하기 위해 아래와 같이 copy selector를 입력했음에도 불구하고 오류가 발생합니다.

search = browser.find_element(By.CSS_SELECTOR, "#input_search1658797242706")

긴 질문 읽어주셔서 정말 감사합니다.

답변 1

0

스타트코딩님의 프로필 이미지
스타트코딩
지식공유자

안녕하세요.

 

우선 1번과 2번은 접근하신 페이지의 URL이 다를 것으로 판단 됩니다.

 

제가 강의에서 사용한 페이지는

https://search.naver.com/search.naver?where=news&sm=tab_pge&query=%EB%AF%B8%EC%98%B9%EC%9D%B4&sort=0&photo=0&field=0&pd=0&ds=&de=&cluster_rank=27&mynews=0&office_type=0&office_section_code=0&news_office_checked=&nso=so:r,p:all,a:all&start=21

입니다.

 

button이 아닌 a태그가 있는 것을 확인할 수 있습니다.

(해당 강의를 다시 천천히 돌려 보면서 따라 해보세요.)

 

3번 질문은 저도 확인해 보았는데

굉장히 특이한 케이스 인 것 같습니다.

 

검색창에 서버 내부적으로 클래스를 부여할 때, selenium이 감지하기 힘든 방식으로

부여하는 것으로 추정됩니다.

 

이런 것은 예외적인 경우기 때문에 머리아파 하지 마시고,

보통의 경우 클래스명을 한개를 하던지 세개를 하던지 정상적으로 가져옵니다.

 

 

 

 

 

최지훈님의 프로필 이미지
최지훈

작성한 질문수

질문하기