☠️ TroubleShooting

네이버 플레이스 크롤링시 404 Not Found 발생 현상

kyuu_ng 2023. 2. 12. 01:10

문제 사항

  • 셀레니움을 이용한 크롤링 시 특정 건수에서 404 Not Found 발생하며 웹 접근 차단 되는 현상
  • 1000건의 음식점 정보에 대해 크롤링 한다고 가정했을 때 대략적으로 700~800건 사이에서 일시적으로 아래와 같이 오류가 뜨며 차단되는 현상이 있음 ( Not Found로 일시적으로 화면이 Block되면 정보 크롤링이 불가능하고, 약 100~300건을 크롤링 할 동안 아래 페이지가 뜨다가 다시 정상적으로 보여짐 ) ( Not Found가 발생하는 시점은 불특정하며, 주피터노트북을 켜고 있는 Pc가 아닌 다른 Pc에서 해당 URL접속 시 이상 없음 == url 문제는 아닌 것으로 보임)

 

원인

  • 추측 원인 : 셀레니움을 통합 반복적인 웹 접속 시 컴퓨터로 판단하여 크롬 브라우저에서 차단
  • 판단 사유 :
  • Not Found가 발생하는 시점에 크롤링하고 있는 Url 자체에는 문제가 없음을 다른 pc를 통해서 확인

 

시도한 방법

  • 구글 검색 시 User-Agent 정보를 헤더에 넣어서 같이 보내면 차단 현상이 개선된다는 글이 있어 적용하였으나 증상 동일
  • headless 옵션을 사용하여 보낼시에도 현상이 개선된다는 글이 있어 추가 하였으나 증상 동일
  • Not Found 오류가 발생하여도 무시하고 그냥 진행
  • 이 경우 1000건 중 Not Found가 발생한 시점의 크롤링 건 수 (100~200건)들의 데이터 크롤링 실패
  • 20% 데이터 유실 발생

질의사항

  • 저희조의 경우 공공API 포탈에서 제공하는 음식점 리스트를 받아서 폐업된 음식점을 제외한 현 영업중인 음식점 리스트를 엑셀로 만들어서 네이버 지도에 ‘음식점 이름’을 키워드로 검색한 결과를 크롤링 하게끔 구성하였습니다.
  • ( 음식점 리스트 엑셀 → 가게 이름으로 네이버 지도 검색 → Url 획득하여 Url에 있는 블로그 리뷰 획득 )
  • 크롤링은 python, 셀레니움을 이용하였고 문제가 발생하는 코드는 아래의 부분으로 ‘음식점 이름’을 키워드로 검색한 결과를 크롤링 하게끔 동작하는 부분입니다.
  • 구글링시 반복적인 작업으로 인한 브라우저 차단 시 User-agent/headless 옵션을 통해 해결이 가능하다고 작성되어 있으나, 해결되지 않아 글을 작성드립니다.
  • 이 방법 외 해결방안이나, 데이터를 수집하는 방법 중 다른 걸 시도해볼 부분이 있을 지 문의드리고싶습니다. (만약 현재 상황으로 Not Found가 나지 않게 하려면 음식점 데이터를 500건씩 잘라서 해야하는데 비효율 적인 것 같습니다. 🥺 )

 

Python 크롤링 코드

# 포스팅 작성 당시 크롬 버젼 : 92

options = webdriver.ChromeOptions()
options.add_argument('headless').     # headless 옵션 사용
options.add_argument("disable-gpu")   # 가속 사용 x
options.add_argument("lang=ko_KR")    # 가짜 플러그인 탑재
options.add_argument('user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Whale/3.18.154.8 Safari/537.36')

# 네이버 지도 검색창에 [~동 @@식당]으로 검색해 정확도를 높여야 합니다. 검색어를 미리 설정해줍시다.

#df['naver_keyword'] = df['dong'] + "%20" + df['name']  # "%20"는 띄어쓰기를 의미합니다.
df['naver_map_url'] = ''

# 본격적으로 가게 상세페이지의 URL을 가져옵시다

for i, keyword in enumerate(df['placename'].tolist()):
    print("이번에 찾을 키워드 :", i, f"/ {df.shape[0] -1} 행", keyword)
    try              
        naver_map_search_url = f"<https://m.map.naver.com/search2/search.naver?query={keyword}&sm=hty&style=v5>"
        print(naver_map_search_url)
        driver.get(naver_map_search_url)
        time.sleep(3.5)
        df.iloc[i,-1] = driver.find_element(By.CSS_SELECTOR, "#ct > div.search_listview._content._ctList > ul > li:nth-child(1) > div.item_info > a.a_item.a_item_distance._linkSiteview").get_attribute('data-cid')
        # 네이버 지도 시스템은 data-cid에 url 파라미터를 저장해두고 있었습니다.
        # data-cid 번호를 뽑아두었다가 기본 url 템플릿에 넣어 최종적인 url을 완성하면 됩니다.
        #만약 검색 결과가 없다면?
    except Exception as e1:
        if "li:nth-child(1)" in str(e1):  # -> "child(1)이 없던데요?"
            try:
                df.iloc[i,-1] = driver.find_element(By.CSS_SELECTOR, "#ct > div.search_listview._content._ctList > ul > li:nth-child(1) > div.item_info > a.a_item.a_item_distance._linkSiteview").get_attribute('data-cid')
                time.sleep(1)
            except Exception as e2:
                print(e2)
                time.sleep(1)
        else:
            pass

driver.quit()

# 이때 수집한 것은 완전한 URL이 아니라 URL에 들어갈 ID (data-cid 라는 코드명으로 저장된) 이므로, 온전한 URL로 만들어줍니다

df['naver_map_url'] = "<https://m.place.naver.com/restaurant/>" + df['naver_map_url']

# URL이 수집되지 않은 데이터는 제거합니다.
df = df.loc[~df['naver_map_url'].isnull()]

 

해결방안

멘토님의 조언을 듣고 브라우저에 Not Found 오류가 발생하고 페이지가 Block되는 기준을 측정해보았다.

약 500 ~ 700회 사이에서 불규칙하게 Not Found 오류가 발생하였고, 약 20분~30분 사이에 Block이 해제되는 것을 알 수 있었다.

Block되는 패턴이 일정하지않아서 어느 기준까지 조회하게 한 후 잠시 대기할지에 대해서 고민을 하였는데

1차시도는 3번의 테스트 중 중간정도의 기준으로 테스트를 시도했다.

 

1차 시도

case1) 발생 시점(600회)에서 약 10분간 대기

if i != 0 and i % 600 == 0:         // 0번째가 아니거나, 600번째 마다 반복
        time.sleep(600)             // 10분(60초 * 10)간 대기

결과 : 767회에서 Block이 발생하였으나, Block 해제 시간이 적용전보다 감소됨 ( 적용 전 : 약 20분 , 적용 후 : 약 5분) → Not Found 오류로 인한 크롤링 데이터 손실율 약 20 % → 10 %

 

💡 Block이 발생하긴 하였으나 Sleep 적용 후 Block 해제 되는 시간이 1/4로 감소됨!

 

 

2차 시도

case2) 발생 시점(500회)에서 약 20분간 대기

  • 두번째는 3회의 테스트 중 가장 빨리 발생된 횟수(591회)를 기준으로 평균 해제시간만큼 대기하도록 설정
if i != 0 and i % 500 == 0:         // 0번째가 아니거나, 500번째 마다 반복
        time.sleep(1200)             // 20분(60초 * 20)간 대기

결과 : 1000건 ~ 3000건이 포함된 데이터로 크롤링 시도시 Not Found 오류는 한번도 발생되지 않았음!!!!!

→ Not Found 오류로 인한 크롤링 데이터 손실율 20% → 0%

 

 

 💡 알게된 점 :

  • API가 아닌 웹 크롤링시에도 무분별한 요청은 서비스에 부하를 줄 수 있어 이러한 처리를 해두었을 수도 있기 때문에 크롤링시에는 이러한 부분들을 잘 확인해보고 안정적으로 크롤링 할 수 있는 방법을 파악하는 부분이 필요함!.