본문으로 바로가기

1. 특정 뉴스 웹페이지 크롤링 중 에러 발생 시 회피

: 종종 naver.news로 들어갔는데도 이상한 페이지로 넘어가서 HTML 태그가 바뀌는 경우가 있다 (특히, 연예 섹션 뉴스)

-> try, except 함수 사용

DataFrame을 만드는 과정에서 각 list의 길이가 완전히 일치해야한다. 이를 위해 본문이나 언론사, 등등 어느 하나라도 ERROR가 나는 경우에 전체가 각 list에 append되지 않도록 해야한다.

따라서, Error가 날 경우 append가 되지 않도록 1) append 함수는 제목/날짜/본문/언론사 정보가 모두 추출되었을 때 제일 마지막에 몰아서 코드를 작성하도록 한다. 또한, 2) 한 곳이라도 에러가 나는 경우에도 전체 코드가 except로 넘어가도록 하는 코드를 작성해야 한다.

 

+ 스크래핑할 때 가장 많이 나는 에러:

find함수로 search 실패하여 None이 입력되는 경우, get_text()하면 에러가 남

예를 들자면, source_news.find('h3', {'id' : 'articleTitle'}).get_text()

이미 source_news.find('h3', {'id' : 'articleTitle'}) 이 함수에서 search가 실패하여 None인 상태에서 get_text를 실행해주면 에러가 나는 것이다.

이를 방지하기 위해 try, except 함수를 사용한다.

 

 

2. 여러 페이지에 걸쳐 크롤링하기

: 페이지네이션

- 콘텐츠를 여러 개 페이지에 나눠서 보여주는 사용자 인터페이스

- 페이지 넘어가도록 설정하는 방법

   1) 단순하게 a 태그로 만들어놓은 경우 (네이버)

   2) 자바스크립트 함수로 만들어놓은 경우, li or button 태그

       -> 그러면 브라우저 강제로 onClick을 활용하여 페이지 바꾸도록

           (다음 또는 다다음 게시글에 소개될 예정)

 

- URL 분석이 필수적

https://search.naver.com/search.naver?
    where=news&
    sm=tab_pge&
    query=데이터분석&
    sort=0&
    photo=0&
    field=0&
    pd=0&
    ds=&
    de=&cluster_rank=20&
    mynews=0&
    office_type=0&
    office_section_code=0&
    news_office_checked=&
    nso=so:r,p:all,a:all&
    start=11   # 페이지당 기사 시작 번호

 

start=' '를 활용해서 아래와 같은 코드 작성

current_call = 1
last_call = (max_page - 1) * 10 + 1 # max_page이 5일 경우 41에 해당 

while current_call <= last_call:
    print(current_call) # 1, 11, 21, 31, 41 
    current_call += 10

 

 

3. 날짜 지정하여 크롤링하기

'''
https://search.naver.com/search.naver?
    where=news&
    query=데이터분석&
    sm=tab_opt&
    sort=0&
    photo=0&
    field=0&
    pd=3&
    ds=2022.01.01&
    de=2022.03.28&
    docid=&
    related=0&
    mynews=0&
    office_type=0&
    office_section_code=0&
    news_office_checked=&
    nso=so%3Ar%2Cp%3Afrom20220101to20220328&
    is_sug_officeid=0
'''

https://search.naver.com/search.naver?
    where=news&
    query=데이터분석&
#     ds=2022.01.01&
#     de=2022.03.27&
    nso=so%3Ar%2Cp%3Afrom20220101to20220327

url에 아래와 같이 nso 파라미터를 추가해주었다.

# \ : 사칙연산 쓸 때 줄바꿈하고 싶을 때 씀 (여러 개의 lines를 하나의 line으로 연결시키는 방법)
url = "https://search.naver.com/search.naver?where=news&query=" + query \
      + "&nso=so%3Ar%2Cp%3Afrom" + start_date \
      + "to" + end_date \
      + "%2Ca%3A&start=" + str(current_call)

 

 

4. 기사 정렬 순서 지정하여 크롤링하기

https://search.naver.com/search.naver?where=news&query=데이터분석&sm=tab_opt&sort=1&photo=0&field=0&pd=0&ds=&de=&docid=&related=0&mynews=0&office_type=0&office_section_code=0&news_office_checked=&nso=so%3Add%2Cp%3Aall&is_sug_officeid=0
https://search.naver.com/search.naver?where=news&query=데이터분석&sm=tab_opt&sort=0&photo=0&field=0&pd=0&ds=&de=&docid=&related=0&mynews=0&office_type=0&office_section_code=0&news_office_checked=&nso=so%3Ar%2Cp%3Aall&is_sug_officeid=0
https://search.naver.com/search.naver?where=news&query=데이터분석&sm=tab_opt&sort=2&photo=0&field=0&pd=0&ds=&de=&docid=&related=0&mynews=0&office_type=0&office_section_code=0&news_office_checked=&nso=so%3Ar%2Cp%3Aall&is_sug_officeid=0


# 관련도순 = 0  최신순 = 1  오래된순 = 2

관련도순 = 0, 최신순 = 1, 오래된순 = 2

 

 

 


최종 코드

def main_crawling(query, start_date, end_date, sort_type, max_page):
    # if type(query) == None: 이 아니라
    # if query == '': 이렇게 써야 아무것도 입력하지 않았을 때에도 확인할 수 있음
    if query == '':
        query = '데이터 분석'
    if len(start_date) != 10:
        start_date = '2021.01.01'
    if len(end_date) != 10:
        end_date = '2021.12.31'
    if sort_type not in ['0', '1', '2']:
        sort_type = '0'
#     if max_page == '':
#         max_page = 5


    # 각 기사들의 데이터를 종류별로 나눠담을 리스트를 생성합니다. (추후 DataFrame으로 모을 예정)
    titles = []
    dates = []
    articles = []
    article_urls = []
    press_companies = []

    # 주어진 일자를 쿼리에 맞는 형태로 변경해줍니다.
    start_date = start_date.replace(".", "")
    end_date = end_date.replace(".", "")

    # 지정한 기간 내 원하는 페이지 수만큼의 기사를 크롤링합니다.
    current_call = 1
    last_call = (max_page - 1) * 10 + 1 # max_page이 5일 경우 41에 해당 


    while current_call <= last_call:

        print('\n{}번째 기사글부터 크롤링을 시작합니다.'.format(current_call))

        url = "https://search.naver.com/search.naver?where=news&query=" + query \
              + "&sort=" + sort_type \
              + "&nso=so%3Ar%2Cp%3Afrom" + start_date \
              + "to" + end_date \
              + "%2Ca%3A&start=" + str(current_call)

        urls_list = []
        try: # 네이버 뉴스 검색결과 페이지 자체에 접근이 불가능할 경우 에러가 발생할 수 있습니다.
            web = requests.get(url).content
            source = BeautifulSoup(web, 'html.parser')

            for urls in source.find_all('a', {'class' : "info"}):
                if urls["href"].startswith("https://news.naver.com"):
                    urls_list.append(urls["href"])
        except:
            print('해당 뉴스 검색 페이지의 네이버 뉴스 링크를 모으는 중 에러가 발생했습니다. : ', url)
        
        # urls_list : 해당 페이지에 있는 "네이버 뉴스"의 링크 모음(list)
        if urls_list != []:
            for url in urls_list:
                try: # 특정 뉴스 기사글 하나를 크롤링하는 중 에러가 발생할 수 있습니다.ㄴ
                    headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'}
                    web_news = requests.get(url, headers=headers).content
                    source_news = BeautifulSoup(web_news, 'html.parser')

                    title = source_news.find('h3', {'id' : 'articleTitle'}).get_text()
                    print('Processing article : {}'.format(title))

                    date = source_news.find('span', {'class' : 't11'}).get_text()

                    article = source_news.find('div', {'id' : 'articleBodyContents'}).get_text()
                    article = article.replace("\n", "")
                    article = article.replace("// flash 오류를 우회하기 위한 함수 추가function _flash_removeCallback() {}", "")
                    article = article.replace("동영상 뉴스       ", "")
                    article = article.replace("동영상 뉴스", "")
                    article = article.strip()

                    press_company = source_news.find('address', {'class' : 'address_cp'}).find('a').get_text()

                    titles.append(title)
                    dates.append(date)
                    articles.append(article)
                    press_companies.append(press_company)
                    article_urls.append(url)
                except:
                    print('\n*** {}번부터 {}번까지의 기사글을 크롤링하는 중 문제가 발생했습니다.'.format(current_call, current_call+9))
                    print('*** 다음 링크의 뉴스를 크롤링하는 중 에러가 발생했습니다 : {}'.format(url))
        else:
            pass

        time.sleep(5)
        current_call += 10
            
            
    article_df = pd.DataFrame({'Title':titles, 
                               'Date':dates, 
                               'Article':articles, 
                               'URL':article_urls, 
                               'PressCompany':press_companies})

    article_df.to_excel('result_{}.xlsx'.format(datetime.now().strftime('%y%m%d_%H%M')), index=False, encoding='utf-8')
    
    print('\n크롤링이 성공적으로 완료되었습니다!')
    print('\n크롤링 결과를 다음 파일에 저장하였습니다 : {}'.format(datetime.now().strftime('%y%m%d_%H%M')))
query = input('검색어를 입력해주세요. (ex. 데이터 분석) : ')
start_date = input('검색 시작 날짜를 입력해주세요. (형식 : 2021.01.01) : ')
end_date = input('검색 종료 날짜를 입력해주세요. (형식 : 2021.12.31) : ')
sort_type = input('정렬 타입을 입력해주세요 (관련도순 = 0, 최신순 = 1, 오래된순 = 2) : ')
max_page = input('크롤링을 원하는 전체 페이지 수를 입력해주세요. (ex. 5) : ')

if start_date > end_date:
    print('\n시작 날짜는 종료 날짜보다 이후로 지정하실 수 없습니다. 다시 실행해주세요!')
elif max_page == '':
    max_page = 5
    print('\n원하시는 페이지 수가 입력되지 않았습니다. 5 페이지까지만 크롤링을 진행합니다.')
    main_crawling(query, start_date, end_date, sort_type, max_page)
else:
    max_page = int(max_page)
    main_crawling(query, start_date, end_date, sort_type, max_page)