수달이네 기술 블로그

5. selenium을 이용한 크롤링 본문

AI공부/머신러닝

5. selenium을 이용한 크롤링

슬픈 수달이 2025. 11. 26. 23:52

셀레니움(selenium)

셀레니움은 셀레니움은 브라우저를 사람이 직접 조작하는 것처럼 자동으로 제어할 수 있게 해주는 웹 자동화 도구이다.

from selenium import webdriver

webdriver:웹 드라이버 관련 메서드들 포함

driver = webdriver.Chrome()
driver.get('<https://www.google.com>')

웹드라이버를 chrome으로 선택한 후 해당 드라이버에서 해당 url을 불러온다.

웹에서 내가 찾고 싶은 객체에 우클릭 > 검사

  • 그러면 해당 객체에 해당하는 html을 찾아준다.
  • 여기서 name, 즉, 해당 객체를 부르는 별명을 찾은 후
search = driver.find_element("name","q")
  • find_element(key, value): 를 이용하여 객체를 찾아준다.
search.send_keys("날씨")

만약 그게 검색창이라면, 위처럼 send_keys를 이용하여 값을 보내주면

위처럼 표시가 된다.

search.send_keys(Keys.RETURN)

해당 코드는 엔터키를 반환하는 키이다.

그러면 검색을 성공하게 된다.

네이버 웹툰 댓글 크롤링

driver = webdriver.Chrome()
driver.get('<https://comic.naver.com/webtoon/list?titleId=747269>')

위처럼 링크를 들어가준다.

그런데 링크를 들어가자마자 크롤링을 시작하면 안된다.

  • 위 사진의(f12 > network > f5) 내용처럼 인터넷에 들어간다. 그러나 코드, 정보들이 불러와지는 시간이 있기 때문에 바로 하면 안될 가능성이 있다. 따라서
time.sleep(2)

sleep을 이용해준다.(2초정도 기다리고 시작)

마찬가지로 댓글들의 위치를 파악한다

여기서 이름을 찾는건 힘들어 보이고, 내용을 찾기 힘들어 보인다 그러면

Copy full XPath를 이용하여 위치를 찾을 수 있다.

/html/body/div[1]/div[5]/div/div/div[5]/div[1]/div[3]/div/section/ul/li[1]

위와 같이 생겼는데.

다른것들도 확인하면

/html/body/div[1]/div[5]/div/div/div[5]/div[1]/div[3]/div/section/ul/li[2]
/html/body/div[1]/div[5]/div/div/div[5]/div[1]/div[3]/div/section/ul/li[3]

위와 같이 맨 뒤 숫자만 바뀌는 것을 알 수 있다.

그러면

  • 공통되는 부분을 빼고 xpath변수에 저장한다.
# /html/body/div[1]/div[5]/div/div/div[5]/div[1]/div[3]/div/section/ul/li[1]
xpath = '/html/body/div[1]/div[5]/div/div/div[5]/div[1]/div[3]/div/section/ul/li'
best_comment_elements = driver.find_elements(By.XPATH, xpath)
best_comment_elements

# [<selenium.webdriver.remote.webelement.WebElement (session="3e9871bcc34a397581d0247e0f611051", element="f.91C9BF00EB3387BEE61B004E446E94FB.d.8189D93B89322B04784F13F59E51766B.e.147")>,
#  <selenium.webdriver.remote.webelement.WebElement (session="3e9871bcc34a397581d0247e0f611051", element="f.91C9BF00EB3387BEE61B004E446E94FB.d.8189D93B89322B04784F13F59E51766B.e.148")>,
#  <selenium.webdriver.remote.webelement.WebElement (session="3e9871bcc34a397581d0247e0f611051", element="f.91C9BF00EB3387BEE61B004E446E94FB.d.8189D93B89322B04784F13F59E51766B.e.149")>,
#  <selenium.webdriver.remote.webelement.WebElement (session="3e9871bcc34a397581d0247e0f611051", element="f.91C9BF00EB3387BEE61B004E446E94FB.d.8189D93B89322B04784F13F59E51766B.e.150")>,
#  <selenium.webdriver.remote.webelement.WebElement (session="3e9871bcc34a397581d0247e0f611051", element="f.91C9BF00EB3387BEE61B004E446E94FB.d.8189D93B89322B04784F13F59E51766B.e.151")>,
#  <selenium.webdriver.remote.webelement.WebElement (session="3e9871bcc34a397581d0247e0f611051", element="f.91C9BF00EB3387BEE61B004E446E94FB.d.8189D93B89322B04784F13F59E51766B.e.152")>,
#  <selenium.webdriver.remote.webelement.WebElement (session="3e9871bcc34a397581d0247e0f611051", element="f.91C9BF00EB3387BEE61B004E446E94FB.d.8189D93B89322B04784F13F59E51766B.e.153")>,
#  <selenium.webdriver.remote.webelement.WebElement (session="3e9871bcc34a397581d0247e0f611051", element="f.91C9BF00EB3387BEE61B004E446E94FB.d.8189D93B89322B04784F13F59E51766B.e.154")>,
#  <selenium.webdriver.remote.webelement.WebElement (session="3e9871bcc34a397581d0247e0f611051", element="f.91C9BF00EB3387BEE61B004E446E94FB.d.8189D93B89322B04784F13F59E51766B.e.155")>,
#  <selenium.webdriver.remote.webelement.WebElement (session="3e9871bcc34a397581d0247e0f611051", element="f.91C9BF00EB3387BEE61B004E446E94FB.d.8189D93B89322B04784F13F59E51766B.e.156")>,
#  <selenium.webdriver.remote.webelement.WebElement (session="3e9871bcc34a397581d0247e0f611051", element="f.91C9BF00EB3387BEE61B004E446E94FB.d.8189D93B89322B04784F13F59E51766B.e.157")>,
#  <selenium.webdriver.remote.webelement.WebElement (session="3e9871bcc34a397581d0247e0f611051", element="f.91C9BF00EB3387BEE61B004E446E94FB.d.8189D93B89322B04784F13F59E51766B.e.158")>,
#  <selenium.webdriver.remote.webelement.WebElement (session="3e9871bcc34a397581d0247e0f611051", element="f.91C9BF00EB3387BEE61B004E446E94FB.d.8189D93B89322B04784F13F59E51766B.e.159")>,
#  <selenium.webdriver.remote.webelement.WebElement (session="3e9871bcc34a397581d0247e0f611051", element="f.91C9BF00EB3387BEE61B004E446E94FB.d.8189D93B89322B04784F13F59E51766B.e.160")>,
#  <selenium.webdriver.remote.webelement.WebElement (session="3e9871bcc34a397581d0247e0f611051", element="f.91C9BF00EB3387BEE61B004E446E94FB.d.8189D93B89322B04784F13F59E51766B.e.161")>]

이렇게 하면 댓글 박스 객체를 알아올 수 있다.

이제 for문을 돌며 찾은 박스들 중에서 댓글 객체만 찾아오면 된다,.

댓글을 찾아 xpath를 (full 이 아닌 xpath) 찾아주면

//*[@id="wcc_root"]/section/ul/li[1]/div[1]/div[2]/div/p

위와 같이 출력된다.

다른 것도 확인하면

//*[@id="wcc_root"]/section/ul/li[2]/div[1]/div[2]/div/p
//*[@id="wcc_root"]/section/ul/li[3]/div[1]/div[2]/div/p

위와 같이 중복되는 부분이 있다. 해당 부분을 제외하고

for문을돌며 찾으면

for li in best_comment_elements:
    try:
        comment_p = li.find_element(By.XPATH, './div/div[2]/div/p')
        comment_text = comment_p.text.strip()
        print(comment_text)
        print("-" * 30)
    except Exception as e:
        print(e)
  • 해당 댓글 칸 안에서 './div/div[2]/div/p 해당 xpath를 가진 값을 찾고, text만 찾아서 strip()
  • 이후 출력
  • 을 반복한다, 그러면
BEST웃기는 소리!!
진짜 김독자는 가터벨트와 치파오를 사랑한다!!
바위에 가터벨트와 치파오를 입혀라!!
거기에 코박죽 하는 놈이 김독자다!!
------------------------------
BEST저기서 소심하게 "나..나는 유중혁이다." 하면 끝인데
------------------------------
BEST진짜 김독자라면 김독자라 안하고
나는 유중혁이다 라고했지
------------------------------
BEST김독자 하도 사칭하고 다녔더니 이제 지가 사칭당하기 시작함
------------------------------
BEST코트 없어서 샤워가운 입고 댕기는 놈은 뭐냐
------------------------------
...

위처럼 출력된다.

다른곳 클릭해서 들어가기

위에서 댓글을 출력한 후 전체 댓글을 들어가서 다시 뽑아오려한다.

그러면 해당 버튼의 full xpath를 찾으면 /html/body/div[1]/div[5]/div/div/div[5]/div[1]/div[3]/div/section/div[4]/button[2]

위와 같은데,

해당 path를 클릭해주어야한다.

driver.find_element(By.XPATH, "/html/body/div[1]/div[5]/div/div/div[5]/div[1]/div[3]/div/section/div[4]/button[2]").click()

.click()함수를 사용하면 해당 객체를 찾아서 클릭까지 해준다.

그러면 클릭후 조금 기다려주는 코드를 사용한후 똑같이 찾으면 된다.

full_comments = driver.find_elements(By.XPATH, "/html/body/div[1]/div[5]/div/div/div[5]/div[1]/div[3]/div/section/ul/li")
for li in full_comments:
    try:
        comments_f = li.find_element(By.XPATH, "./div[1]/div[2]/div/p")
        full_text = comments_f.text.strip()
        print(full_text)
        print('-' * 30)
    except Exception as e:
        print(e)
        
# 중간에 폰 볼때 귀 이상한건 왜 아무도 말 안함?
# ------------------------------
# 진짜 나만 김독자가 김독자 답지않다고 샹각한게 아니였어...복선이 있었구만.
# ------------------------------
# 출산을요? 라니.. ㅋㅋ
# ------------------------------
# 몇명한테 나와 싸워 이긴다면 인정해주지 하면서 야차룰로 싸워 ㅋㅋ
# ------------------------------
# 김독자를 증명하는 방법 = 내가 유중혁이다 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
# ------------------------------
# 독자야! 성좌의 격을 써!
# ------------------------------
# 해결방법
# ???:전 치파오와 가터벨트를 입은 유중순이 좋습니다!!
# ------------------------------
# 별의별 망토가 다있네ㅋㅋㅋ
# ------------------------------
# 아.. ㅋㅋ 영광입니다 ㅋㅋ 글로만 보던 내가 김독자다를 보게 되다니ㅋㅋㅋ