쉽지않은 블로그
네이버 지도를 크롤링할때 참고해야할것...[NAVER PLACE] 2021.4 본문
이 글은 네이버 지도 등과 같은 동적 웹페이지를 크롤링할 때 저와 같은 시간을 낭비하지 않길 바라며 적습니다....
들어가며
동적으로 움직여야 되는 다른 사이트들도 크롤링을 해본 경험이 있었는데도 불구하고
네이버 지도의 크롤링 난이도는 꽤 높았다고 생각이 든다.
네이버 지도를 크롤링한 이유는 여러 카페와 맛집들의 리스트를 받기 위함이었다
크롤링을 최후에 수단으로 두고 여러 방법들을 찾아보았지만
예전의 방법들은 다 지금 사용할 수 없게 된 걸 확인하고 파이 참을 켰다.. 😂😂
이전에 존재했던(?) 맛집 리스트를 얻는 방법 중 몇 가지를 소개하자면 (나는 이용하지 않아 봐서 정확히는 모른다)
1. 작년까지 제공되었던 NAVER_PLACE API 있었던 것 같다 naver devleops , naver 클라우드 플랫폼 등 에서 그 흔적을 찾을 수 있다.
2. 다른 블로그를 보니 몇 년 전 네이버 플레이스가 현재처럼 네이버 map과 통합되지 않고 다른 도메인으로 있었던 것 같다.
그 당시 네이버 플레이스 도메인 안에서 검색이 가능했었던 것 같다 그 기능을 이용하여 url 쿼리 파라미터로 전송을 하고
bs4로 파싱을 하면 되는 간단한 구조였던 것 같다.
현재 네이버 플레이스의 구조
현재 네이버 플레이스는 네이버 지도와 같이 붙어있다
(스마트 플레이스라고 다른 플랫폼이 있는 것 같은데 그거와는 아무 상관없다)
왼쪽 가게들을 클릭하면 사진처럼 예전 플레이스 도메인에서 봤었던 페이지가 오른쪽 페이지에 나오게 된다.
해당 영역들은
<iframe> 태그로 감싸져 있고 특정 주소로(pcmap.place.naver.com) 계속 요청을 보내오고 받아오고 있다
몇 번 이렇게 보다 보니까 휴대폰에서 보았을 때와 매우 비슷하다고 생각되었다.
실제로도 그런 것 같다...
물론 어플 개발은 해본 적이 없어서
웹과 앱을 편하게 관리하기 위해서 이렇게 한 건지
아니면 크롤링을 너무 많이 당해서 이렇게 해놓은 건지 잘은 모르겠지만
확실한 건 예전에 이용하던 naverplace 도메인은 닫혀있어서
네이버 지도를 이용해서 정보를 얻어낼 수밖에 없다는 것이다..
전략
네이버 map에 동적으로 event (클릭)을 발생시켜
iframe을 load 시킨 후 bs4를 이용하여 페이지를 파싱 해오는 전략을 세웠다.
(결국 사람이 하는 패턴 그대로 기계한테 시키는 꼴이다... 다른 방법은 도무지 떠오르지 않았다)
불행 중 다행인 것은 네이버 지도는 같은 ip의 연속된 요청에 아무 반응을 보이지 않는 것 같다.
필요한 라이브러리 : selenium , bs4
먼저 알면 좋은 것들
네이버 지도는 동적 페이지이기 때문에 많은 iframe들이 존재한다.
가게들의 리스트가 나오는 구간과 선택한 가게의 정보가 나오는 프레임이 다르다.
그래서 검색을 하거나 클릭을 하거나 html을 가져올 때도 해당 태그가 존재하는 iframe에 driver 가 위치해야 한다.
next_iframe = driver.find_element_by_id('커서를옮기고 싶은 특정프레임')
driver.switch_to.frame(next_iframe) //해당 프레임으로 점프
driver.switch_to.default_content() //다시 부모 iframe으로 복귀
계속 크롤링을 하다 보면 헷갈린다
현재 어디 iframe 인지... 그래서 iframe의 주소를 다른 곳에 적어놓고 잘 점프하도록 하자
그다음 다시 원래 프레임(상위) 프레임으로 가고 싶을 때 driver.switch_to.default_content()를 꼭 해주어야 한다.
이것만 잘 기억한다면 그다음부턴 시간싸움이다.
- 원하는 action을 취해서 원하는 정보가 나왔다면
- 해당 정보가 존재하는 iframe으로 jump 하여
- load 가 되길 기다린 후 driver.page_source를 받아 온 후
- bs4를 이용하여 파싱을 한다.
내가 마주한 문제들
1. 리스트가 한 번에 load 가 되지 않고 selenium의 스크롤이 먹히지 않는다.
왼쪽 가게 리스트들이 최대 50개까지 존재한다.
이게 밑에 스크롤이 되면서 아래로 내려가야 새로운 정보가 불러와진다.
물론 새로운 정보가 load 됐다면 driver를 이용하여 다시 페이지 소스를 불러와서 파싱을 해야 한다.
그래서 처음 생각은 그냥 스크롤 바닥까지 한 다음 크롤링을 마저 하면 되겠지라고 생각을 하고 50개의 리스트를 load 하는 것 까지는 성공을 했는데
문제는 selenium click 방식에 있었다
driver의 click() 메서드는 클릭을 할 대상이 화면에 보여야 가능하다는 점을 간과하고 있었다.
나의 크롤링 봇이 리스트가 운 좋게 사이즈가 하단의 footer와 딱 맞물리는 상황이면 그다음 단계를 진행하지 못하는 상황에 직면했다.
에러 메시지로 "element is not clickable at point"가 나를 반긴다
다른 글들을 보니 여러 해결 방법이 있었다 (참고로 저 글의 결론은 광고다)
www.testim.io/blog/selenium-element-is-not-clickable-at-point/
- driver.maximize_window() 창을 제일 크게 키우는 방법 (바로 안된다는 것을 실행해보고 알았다)
- send_keys(PAGE_DOWN)을 이용하는 법 (뭔가 잘하면 될 거 같은 녀석 중 하나이다)
- driver.switch_to.active_element를 사용하는 법 (
- 자바스크립트의 element.scrollIntoView(true); 메서드를 사용하는 법
3번까지 모든 삽질을 다해봤지만 먹히지 않았다
네이버 플레이스에서는 smoothscroll.js를 스크롤을 부드럽게 하기 위해 사용되는데
다른 곳은 되는데 여기는 왜 안되는지 모르겠다
그러던 와중 scrollIntoView() js 메서드를 발견하고
어차피 화면에 보여야 클릭할 수 있으니까 querySelector로 잘 조합하여 써보자 했었는데 이 방법이 바로 통했다.
이 메서드를 이용하면 하단까지 쭉쭉 내려가는 페이지를 볼 수 있을 것이다.
사실 selenium에 너무 매달려 있어 js에서 지원되는 메서드가 있지 않을까를 생각조차 못한 게 패착이었던 것 같다.
2. li태그 내부구조가 뭘 검색하냐에 따라 달라진다.
대부분의 대형 사이트들은 클래스 이름을 암호화하여 저장하고 모든 js파일도 압축을 하여 놓는다는 것은
이미 크롤링을 선택할 때부터 염두에 두고 있었다 그래서 웬만하면 class 이름을 쿼리 선택자로 사용하지 않기로 했었다.
하지만 무엇을 검색하냐에 따라 태그의 구조가 달라지는 것은 예상치 못했다.
문제가 되는 부분은 naver_place의 인공지능? 이 반응하는 키워드들을 입력하면
필터 , 요즘 뜨는 이렇게 나오면서 하단의 사진도 세장씩 나온다
하지만 오른쪽처럼 비주류의 검색어를 대입하게 되면 사진도 한 장씩 나오게 되면서
html 구조가 바뀌게 된다 그리고 class이름도 바뀌는 것을 확인할 수 있다
기본적으로 모든 가게 리스트들은
<li> 태그로 이루어져 있다
저 클래스 이름이 다른 것은 잘은 모르지만 뭔가 카테고리와 관련돼 있는 것 같다.
같은 업종은 무조건 같은 이름으로 나온다
저 맛집 사진이 세장이 나올 때와 한 장이 나올 때는 <li> 아래 구조가 달라 (스크린숏에는 포함하지 못했다)
driver.click()을 할 때 애를 먹었다. (click을 list를 대상으로 하게 되면 운이 나쁠 경우 원치 않은 page가 load 될 수도 있다)
주먹구구로 수정을 하여 돌아가긴 했었는데 추후에 수정이 필요해 보인다.
결론
크롤링을 할 때 사이트의 특성을 잘 관찰하고 내가 검색하는 범위 안에서 통용되는 공통점을 잘 찾아야 나중에 편하게 크롤링을 할 수 있을 것이다
참고로 네이버 플레이스의 별점 기능이나 이런 것은 올해 3분기 이후로 별점 방식의 rating 이 아닌 키워드 제공 위주의 AI설루션으로 대체할 것이라고 하는 기사를 보았다.
이 글을 보는 사람들은 위 내용을 기반으로 전략을 잘 짜면 좋을 거 같다.
오랜만에 크롤링을 해보았지만 네이버 크롤링은 다른 사이트에 비해 확실히 난이도가 있었던 것 같다🤯🤯🤯
힘들었지만 재밌는 경험이 되었던 것 같다.