본문 바로가기
Python

[Python]BeautifulSoup과 Selenium을 이용한 네이버 검색광고 크롤링

by Basicer 2022. 12. 12.

네이버 키워드 광고를 진행하다 보면 다른 경쟁 업체는 어떤 소재를 사용하는지 궁금해집니다.

이것저것 찾아보니 웹 크롤링이란 방법이 있었고 무료 강의와 검색을 통해 얻는 지식을 가지고 네이버 검색광고 크롤러를 만들어 보았습니다.

매우 초보이므로 코드에 대한 많은 태클 바랍니다.

 

결과값은 다음과 같습니다.

Web_네이버 검색광고 결과
Mobile_네이버 검색광고 결과
Mobile_네이버 검색광고 결과

이 글을 읽고 있는 여러분들은 매우 바쁘시므로 코드도 바로 공개합니다. 총 4개의 파일입니다. 초보라 매우 복잡하지만 코드 자체는 어렵지 않으니까 천천히 따라 해 보세요. 각 파일명도 똑같이 해주셔야 합니다. 복사하기도 귀찮으시면 https://github.com/basic-Wk/Scraper 여기가서 복사하세요.

 

먼저 Main.py 파일입니다. 이 코드를 실행하면 결과값이 출력됩니다.

from Web_naver import extract_Web_naver, total_Web_search_count
from Mo_naver import extract_Mobile_naver, total_Mobile_search_count

keyword = "장기렌트" #검색하고 싶은 키워드를 입력해주세요.
Web_naver = extract_Web_naver(keyword)
Web_count = total_Web_search_count(keyword)
Mobile_naver = extract_Mobile_naver(keyword)
Mobile_count = total_Mobile_search_count(keyword)

file = open(f"search_{keyword}_Web_naver.csv", "w", encoding="utf-8-sig") #인코딩을 utf-8로 할경우 엑셀에서 한글이 깨졌습니다.

file.write(f"총 검색건수는 {Web_count}건 입니다.\n")
file.write("title1, title2, title3, ad_url, ad_event, ad_desc, ad_period\n")

#웹 검색결과를 출력합니다
for i in Web_naver:
    file.write(f"{i['title1']},{i['title2']},{i['title3']},{i['ad_url']},{i['ad_event']},{i['ad_desc']},{i['ad_period']}\n")
file.close()

file = open(f"search_{keyword}_Mobile_naver.csv", "w", encoding="utf-8-sig")

file.write(f"총 검색건수는 {Mobile_count}건 입니다.\n")
file.write("title1, title2, title3, ad_url, ad_event, ad_desc, ad_period\n")

#모바일 검색결과를 출력합니다.
for j in Mobile_naver:
    file.write(f"{j['title1']},{j['title2']},{j['title3']},{j['ad_url']},{j['ad_event']},{j['ad_desc']},{j['ad_period']}\n")
file.close()

네이버 웹 크롤링 파일입니다. 파일명은 Web_naver.py 입니다.

from requests import get
from bs4 import BeautifulSoup
from eliminate import my_eliminate
import math

def total_Web_search_count(keyword):
    base_url = "https://ad.search.naver.com/search.naver?"
    response = get(f"{base_url}query={keyword}&pagingIndex=1")

    if response.status_code != 200:
        print("Can't request Web search naver")
    else:
        soup = BeautifulSoup(response.text, "html.parser")
        inner = soup.find_all('div', class_ = "inner")[0]
        total_search = inner.find('span', class_="num_result").text
        total = total_search.split('/')[1].strip("건")
        search_count = int(total)
        return search_count

def page_count(keyword):
    page_count = math.ceil(total_Web_search_count(keyword)/25)
    return page_count

def extract_Web_naver(keyword):
    results = []
    search_page = page_count(keyword)
    for page in range(1, search_page + 1):
        base_url = "https://ad.search.naver.com/search.naver?"

        response = get(f"{base_url}query={keyword}&pagingIndex={page}")

        if response.status_code != 200:
            print("Can't request Web search naver")
        else:
            soup = BeautifulSoup(response.text, "html.parser")
            inner = soup.find_all('div', class_ = "inner") 
            del inner[0] #첫번째 리스트는 검색결과에 관한 값이므로 지워준다.
            for inner_list in inner:
                title = inner_list.find_all('span',class_ = "lnk_tit")
                bnk = 'empty'
                title_1 = title[0].text #노출되는 제목이 최대 3개. 1개 또는 2개일 경우 다른것으로 채워야함. 
                title_2 = bnk if len(title) == 1 else title[1].text
                title_3 = bnk if len(title) != 3 else title[2].text
                ad_url = inner_list.find('a', class_ = "url").string #url 가져오기, 경로가 명확해서 string 사용
                event = inner_list.find_all('p', class_ = "ad_dsc")
                ad_promo = bnk if len(event) == 1 else event[0].text
                ad_desc = event[0].text if len(event) == 1 else event[1].text
                ad_period = inner_list.find('em', class_="txt").text
                search_data = {
                    'title1' : my_eliminate(title_1),   #줄바꿈, 공백, 쉼표, 탭과 같은 파일변환시 문제되는 문자열 삭제함수
                    'title2' : my_eliminate(title_2),
                    'title3' : my_eliminate(title_3),
                    'ad_url' : ad_url,
                    'ad_event' : my_eliminate(ad_promo),
                    'ad_desc' : my_eliminate(ad_desc),
                    'ad_period': my_eliminate(ad_period)
                }
                results.append(search_data)
    return results

네이버 모바일 크롤링 파일입니다. 파일명은 Mo_naver.py 입니다.

import time
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
import math
from bs4 import BeautifulSoup
from eliminate import my_eliminate
from requests import get

def extract_Mobile_naver(Keyword):
    chrome_options = Options()
    
    #최신 Selenium 사용시 브라우저가 자동으로 꺼지는데 이를 방지해 주는 옵션입니다.
    chrome_options.add_experimental_option("detach", True) 
    
    #본인 브라우저에 맞는 크롬드라이브를 같은 경로에 넣어야 하는데 자동으로 드라이버를 설치해주는 코드입니다.
    browser = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options = chrome_options)

    browser.get(f"https://m.ad.search.naver.com/search.naver?where=m_expd&query={Keyword}")
    
    # 페이지 수를 구하고 더보기를 몇번 눌러야 하는지 구하는 코드입니다.
    more_count = math.ceil(int(browser.find_element(By.ID, '_total_count').text)/15)

    for i in range(1, more_count):
        browser.find_element(By.ID, "_get_more").click()
        time.sleep(2)

    source_code = browser.execute_script("return document.body.innerHTML;")
    soup = BeautifulSoup(source_code, 'html.parser')

    inner = soup.find_all('div', class_= 'lst_cont')
    results = []

    for inner_list in inner:
        title = inner_list.find_all('span', class_ = 'tit')
        bnk = 'empty'
        title_1 = title[0].text
        title_2 = bnk if len(title) == 1 else title[1].text
        title_3 = bnk if len(title) != 3 else title[2].text
        ad_url = inner_list.find('span', class_ = 'url_link').text
        event = inner_list.find_all('div', class_ = 'pr')
        ad_promo = event[0].text if len(event) == 1 else bnk
        ad_desc = inner_list.find('div', class_ = 'desc_area').text
        ad_period_area = inner_list.find('div', class_ = 'period_area')
        ad_period = ad_period_area.find('em').text
        search_data = {
        'title1' : my_eliminate(title_1),   #줄바꿈, 공백, 쉼표, 탭과 같은 파일변환시 문제되는 문자열 삭제함수
        'title2' : my_eliminate(title_2),
        'title3' : my_eliminate(title_3),
        'ad_url' : ad_url,
        'ad_event' : my_eliminate(ad_promo),
        'ad_desc' : my_eliminate(ad_desc),
        'ad_period': my_eliminate(ad_period)
        }
        results.append(search_data)
    return results

def total_Mobile_search_count(Keyword):
    response = get(f"https://m.ad.search.naver.com/search.naver?where=m_expd&query={Keyword}")
    soup = BeautifulSoup(response.text, "html.parser")
    total_count = soup.find('em', class_ = 'total').text
    return total_count

마지막으로 크롤링한 데이터 전처리 파일입니다. 파일명은 eliminate.py 입니다.

def my_eliminate(abc):
    abc1 = abc.strip().replace('\n','').replace(' ','').replace(',', '')
    return abc1

모든 파일은 한 폴더에 넣고 돌리시면 됩니다.

제가 사용한 파이썬과 모듈 버전은 아래와 같습니다. 모두 최신 버전만 사용했습니다.

Python 3.11.0
Beautifulsoup4 4.11.1
requests 2.28.1
Selenium 4.6.1

저도 초보라 답변 가능할지 모르겠지만, 궁금한 점 남겨주시면 아는 한도 내로 답변드리겠습니다.


간단히 회고를 해보자 한다.

 

1. Print 명령어가 최고다!

     나 같은 입문자들은 Print 명령어를 활용하세요. 변수 만들고 print 하고, 변수 만들고 print 하고. 오래 걸리지만 저한테는 최고의 방법이었습니다. 괜히 프로그래밍 입문에 print('hello world!')가 있는 게 아니었다는 걸 느꼈습니다.

 

2. 검색은 영어로 하자!

     저도 한글로 구글 검색을 많이 했습니다. 그런데 검색하다 보면 영어 자료가 훨씬 많다는 걸 알게 됩니다. 영어 잘할 필요 없습니다. 저도 영어 못합니다. 오류 나면 복사해서 구글에 붙여 넣으세요. 나오는 사이트는 크롬으로 번역해서 보세요. 나오는 코드들은 test.py 만들어서 직접 돌려보세요. 삽질의 연속이지만 해결되면 그만한 성취감이 없습니다. 그리고 워낙 전문용어들이 많다 보니 전 아직도 어떤 검색어를 구글에 쳐야 하는지 아직도 모릅니다.

 

3. 공식문서를 읽자!

    여러 책도 보고 해 봤지만 공식문서만 한 게 없습니다. 공식문서 예제를 가져온 책들도 많고요... 문법 같은 것들 공식문서와 비교해서 보면 많은 도움이 됩니다. 다 읽을 필요 없습니다. 쓰는 함수에 관한 것만 읽어봐도 좋더라고요.

 

4. 개선점

    - 검색어를 하나만 입력하게 되어있는데, 리스트 형식으로 변형 가능하다.

    - 검색어를 코드에서 입력하지 않고 입력 받을 수 있게 만들 수 있다.

    - 매 시간마다 수집하도록 만들 수 있다.

    - 중복되는 코드가 보인다. 좀 더 줄일 수 있어 보인다.

    - 다음 검색광고도 만들어야 하나?

 

 

코드 설명은 다음 포스트에 하겠습니다.

(설명이라고 할 것도 없습니다. 그저 제가 한 것에 대한 기록으로 사용하려고요. 전문 프로그래머도 아니고ㅋㅋㅋ. 저처럼 입문자들만 보시면 될 것 같습니다.)

댓글