오답노트

네이버 카페 크롤링 본문

Python/Web Crawling

네이버 카페 크롤링

권멋져 2022. 12. 5. 21:21

개요

개요

빅프로젝트를 위해서 네이버 카페 크롤링을 해야하는 상황이 생겼다.

그런데 하는 과정에서 어떻게 해야될지 조금 헤맸는데, 정리 해놓으면 좋을거 같아서 정리한다.

 

카페 접속 링크 크롤링

 

우선 네이버에서 VIEW 탭에 카페로 필터링한 결과이다.

 

해당 페이지는 스크롤을 내리면 10개씩 검색 결과를 추가로 가져온다.

개발자 도구에서 스크롤을 내렸을 때 보내는 요청을 확인해 봤다.

 

위 사진은 스크롤 했을 때만 필터링하여 받아온 데이터를 확인한 것이다. 

보아하니 js로 함수를 만들어 놓고 스크롤을 내릴 때 함수를 호출해서 정보를 가져오는 것 같다.

 

이렇게 받아온 정보들에서 페이지에 출력된 카페들의 접속 url을 크롤링했다.

 

카페 게시글 크롤링

다음은 url에 접속하여 카페 내부의 게시글 내용을 크롤링해야한다.

 

카페 게시글로 이동하면 맨 처음으로 카페에 대한 레이아웃을 먼저 받아오게 된다.

이 때 html의 js에서 cludid 변수에 카페에 따른 넘버를 가지고 있다. 이는 이후에 게시글 내용을 받아 올 때 사용된다.

게시글 id는 url에서 맨위에 있는 숫자이다.

 

위 url이 게시글에 대한 html을 담고 있는 json을 응답해주는 url이다.

cafes 다음의 숫자는 cludid 변수고

articles 다음의 숫자는 게시글 id이다.

 

응답받은 내용을 살펴보자

 

article 아래 contentHtml이 게시글에 대한 html을 담고 있다.

여기서 selector를 통해서 글이 있는 부분의 태그만 골라내 글을 추출한다.

 

위 상황은 운좋게도 카페 운영자가 카페에 가입하지 않아도 게시글을 볼 수 있도록 설정을 해놨는데,

카페 운영자가 카페에 가입해야만 게시글을 볼 수 있도록 설정했을 때 응답을 살펴보자.

 

이런식으로 errorCode를 응답한다.

따라서 이런 errooCode를 가지고 있는 페이지는 그냥 넘기면 된다.

 

코드

위 개요에 대한 설명을 코드로 옮겼다.

path를 적절하게 바꿔서 사용하면 어느정도 사용 가능하다.

 

def crawling_naver_cafe(begin:int,cnt:int):
    try:
        err_urls = []
        result_list = []
        for i in range(cnt):
            print(i,'/',cnt)
            start = i * 10 + begin
            
            path = f'https://s.search.naver.com/p/cafe/search.naver?where=article&ie=utf8&query=%EC%95%84%EC%9D%B4%ED%8F%B0+14&prdtype=0&t=0&st=rel&srchby=text&dup_remove=1&cafe_url=&without_cafe_url=&sm=tab_opt&nso_open=0&rev=44&abuse=0&ac=0&aq=0&converted=0&is_dst=0&nlu_query=%7B%22r_category%22%3A%2230+21%22%2C%22payment%22%3A%221%22%7D&nqx_context=&nx_and_query=&nx_search_hlquery=&nx_search_query=&nx_sub_query=&people_sql=0&spq=0&x_tab_article=&is_person=0&start={start}&display=10&prmore=1&_callback=viewMoreContents'
            response = requests.get(path,)
            
            command_len = len("'viewMoreContents(")
            response_string = response.text[command_len:-2]
            json_object = json.loads(response_string)
            
            soup = bs(json_object['html'], "html.parser")
            elements = soup.find_all('a','api_txt_lines total_tit')
                                 
            for elem in elements: # 검색된 카페 게시글들
                url = elem.attrs['href'][:elem.attrs['href'].find('?')]
                cafe_response = requests.get(url)
                
                cafe_soup = bs(cafe_response.text, "html.parser")  
                cafe_html = cafe_soup.find('script').text
            
                var_index = cafe_html.find('g_sClubId')
                semi_index =cafe_html.find(';',var_index)
                
                no_cloud = cafe_html[var_index:semi_index]
                no_cloud = no_cloud[no_cloud.find('"')+1:-1]
                no_post = url[url.rfind('/')+1:]
                
                articles_path = f'https://apis.naver.com/cafe-web/cafe-articleapi/v2/cafes/{no_cloud}/articles/{no_post}?useCafeId=true&requestFrom=A'

                articles_response = requests.get(articles_path)
                
                articles_json_object = json.loads(articles_response.text)
                        
                if 'errorCode' in articles_json_object['result']:
                    err_urls.append(url)
                    continue
                
                html = articles_json_object['result']['article']['contentHtml']
                soup = bs(html, "html.parser")
                
                p_tags = soup.find_all('p','se-text-paragraph se-text-paragraph-align-')
                
                post = ''
                
                for p in p_tags: # 카페 게시글
                    post += p.text + ' '
            
                result_list.append(post)
                    
    except requests.exceptions.Timeout as errd:
        print("Timeout Error : ", errd)
    
    except requests.exceptions.ConnectionError as errc:
        print("Error Connecting : ", errc)
        
    except requests.exceptions.HTTPError as errb:
        print("Http Error : ", errb)

    # Any Error except upper exception
    except requests.exceptions.RequestException as erra:
        print("AnyException : ", erra)
    
    return result_list, err_urls