2.1 사이트 소개2.2 Kotra 크롤링 목적2.3 Kotra 크롤링 가능 여부 확인2.4 Kotra 사이트 구조 탐색2.4.1 첫 페이지 확인2.4.2 상세 페이지 확인2.4.3 다음 페이지로 넘어가는 버튼 확인2.5 Kotra 해외 시장 상품 동향 크롤링2.5.1 크롤링 할 페이지 접속2.5.2 첫 페이지 크롤링2.5.3 상세 페이지 크롤링2.5.4 여러 페이지 크롤링 하기2.6 데이터 전처리2.6.1 중복 게시글 제거2.6.2 keyword 칼럼의 불필요한 요소 제거2.7 MySQL DB에 데이터 적재2.7.1 DB 생성2.7.2 테이블 생성2.7.3 데이터 저장2.7.4 MySQL에서 저장된 데이터 확인하기2.8 DB 활용 - 필터링
2.1 사이트 소개
KOTRA(https://dream.kotra.or.kr/dream/index.do)는 해외 경제 정보를 제공해 주는 사이트입니다.
「해외경제정보드림」 자체 수집, 생산한 KOTRA 해외시장 뉴스,국가 정보를 비롯한 핵심 정보를 Open API,파일, 링크 등 다양한 형태로 공공데이터포털을 통해 개방하고 있습니다.
해당 사이트 내에서 상품 DB 카테고리를 살펴볼 예정입니다. 상품 DB에는 각 국가별 최근 이슈가 되고 있는 상품들에 대한 시장 동향 정보들을 제공하고 있습니다. 각 국가별, 대륙별, 산업분류별로 검색을 할 수 있습니다. 각 동향 자료에는 간략한 정보를 알 수 있는 키워드와 2줄 요약이 존재합니다.
2.2 Kotra 크롤링 목적
Kotra에서는 상품 시장 동향 정보를 제공해 주고 있습니다. 국가별, 대륙별 이슈가 되는 상품 데이터를 수집하여 과거부터 현재까지의 동향을 요약 정리할 수 있습니다.
따라서 국가별, 대륙별 데이터를 수집하여 시장 동향을 예측해 보겠습니다. 예측한 데이터를 통하여 마케팅 및 상품 수출에 이용할 수 있고 상품 동향 데이터를 분석하여 변수에 따른 시장 동향의 상관관계도 파악할 수 있습니다.
2.3 Kotra 크롤링 가능 여부 확인
크롤링을 시작하기에 앞서 웹 페이지 주소 창에 https://dream.kotra.or.kr/robots.txt를 입력하여 크롤링 가능 여부를 확인합니다.
User-agent: * Allow: /
모든 크롤러에 대해 모든 페이지가 허용되어 있다는 결과가 나오게 됩니다.
robots.txt를 통해 크롤링이 허용된 것을 확인했다 하더라도 상업적으로 이용하거나, 웹 서비스 제작자의 의도에 반하면 법적 책임을 질 수 있어 주의가 필요합니다.
2.4 Kotra 사이트 구조 탐색
먼저, Chrome의 우측 상단부분 점 3개 아이콘을 클릭하여 Chrome 맞춤 설정 및 제어로 들어갑니다. 그럼 [도구 더보기]에서 [개발자 도구]로 들어가면 [Elements]라고 하는 웹 페이지를 구성하고 있는 HTML 코드를 분석하고 수정할 수 있는 도구 패널이 존재합니다. 저희는 이 부분을 활용하여 필요한 부분을 가져오도록 하겠습니다.
2.4.1 첫 페이지 확인
첫 페이지에서 총 4개의 데이터를 가져와 활용하려고 합니다. 각 데이터의 구체적인 정보와 활용 방안은 아래와 같습니다.
- 제목 상품과 관련된 제목이 저장되어 있습니다. 제목에서 어떤 상품에 대한 정보인지 알 수 있습니다
- 국가 해당 상품이 어떤 국가에 관련된 정보인지 알 수 있습니다.
- 무역관 해당 상품이 국가 내 어떤 무역관과 관련되어 있는 정보인지 알 수 있습니다
- 게시일 해당 상품에 대한 정보가 게시된 날짜를 알 수 있습니다
XPath 확인하기
각 요소들의 XPath를 확인하면, 다음과 같은 규칙이 발생하는 것을 확인할 수 있습니다. 이후 크롤링 코드 작성 시 해당 규칙을 활용하여 코드를 작성하게 됩니다.
- 게시글 순서에 따라 tr[게시글 순서]로 구성되어 있고 나머지는 동일한 것을 확인할 수 있습니다.
# 첫번째 게시글의 XPath //*[@id="tbody"]/tr[1]/td[2]/a # 제목 //*[@id="tbody"]/tr[1]/td[3] # 국가 //*[@id="tbody"]/tr[1]/td[4] # 무역관 //*[@id="tbody"]/tr[1]/td[5] # 게시일 # 열번째 게시글의 XPath //*[@id="tbody"]/tr[10]/td[2]/a # 제목 //*[@id="tbody"]/tr[10]/td[3] # 국가 //*[@id="tbody"]/tr[10]/td[4] # 무역관 //*[@id="tbody"]/tr[10]/td[5] # 게시일
2.4.2 상세 페이지 확인
상세 페이지에서 총 5개의 데이터를 가져와 활용하려고 합니다. 각 데이터의 구체적인 정보와 활용 방안은 아래와 같습니다.
- 제목 해당 상세 페이지의 제목을 알 수 있습니다.
- 국가 상세 페이지 내 상품정보의 국가를 알 수 있습니다.
- 무역관 상세 페이지 내 상품정보의 무역관을 알 수 있습니다.
- 게시일 상세 페이지 내 상품정보의 게시일을 알 수 있습니다.
- 키워드 상세 페이지 내 정보에 대한 키워드 값들을 알 수 있습니다. 해당 상품 내용에서 중요하게 생각하는 단어들을 파악할 수 있습니다.
- 요약 상세 페이지 내 정보를 요약한 내용을 알 수 있습니다. 전체적인 내용을 간단하게 파악할 수 있습니다.
XPath 확인하기
각 요소들의 XPath를 확인하면, 다음과 같은 규칙이 발생하는 것을 확인할 수 있습니다. 이후 크롤링 코드 작성 시 해당 규칙을 활용하여 코드를 작성하게 됩니다.
- 상세페이지 들어가는 부분에서 게시글 순서에 따라 tr[게시글 순서]로 구성되어 있는 것을 확인할 수 있습니다.
- 나머지 제목, 국가, 게시일, 키워드, 요약, 목록 요소는 동일한 것을 확인할 수 있습니다.
# 첫번째 게시글의 XPath //*[@id="tbody"]/tr[1]/td[2]/a # 상세 페이지 들어가기 //*[@id="pdfArea"]/dl/dt/div[1]/strong # 제목 //*[@id="pdfArea"]/dl/dt/div[2]/ul/li[2] # 국가 //*[@id="pdfArea"]/dl/dt/div[2]/ul/li[4] # 게시일 //*[@id="pdfArea"]/dl/dd/div[1] # 키워드 //*[@id="pdfArea"]/dl/dd/div[2] # 요약 //*[@id="contents"]/article/div[1]/div[2]/a/span # 상세 페이지 나오기(목록) # 열번째 게시글의 XPath //*[@id="tbody"]/tr[10]/td[2]/a # 상세 페이지 들어가기 //*[@id="pdfArea"]/dl/dt/div[1]/strong # 제목 //*[@id="pdfArea"]/dl/dt/div[2]/ul/li[2] # 국가 //*[@id="pdfArea"]/dl/dt/div[2]/ul/li[4] # 게시일 //*[@id="pdfArea"]/dl/dd/div[1] # 키워드 //*[@id="pdfArea"]/dl/dd/div[2] # 요약 //*[@id="contents"]/article/div[1]/div[2]/a/span # 상세 페이지 나오기(목록)
2.4.3 다음 페이지로 넘어가는 버튼 확인
웹 페이지 하단에는 총 5가지 종류의 페이지 버튼이 존재합니다. 각 버튼의 구체적인 정보와 활용 방안은 아래와 같습니다.
- 첫 페이지로 이동 버튼 맨 첫 번째 페이지로 이동할 수 있는 버튼입니다. 크롤링 시에는 필요하지 않은 요소입니다.
- 이전 5개 목록 페이지로 이동 버튼 이전 5개 목록으로 이동할 수 있는 버튼입니다. 크롤링 시에는 필요하지 않은 요소입니다.
- 숫자 페이지 버튼 각 페이지로 이동할 수 있는 버튼입니다. 해당 버튼을 활용하여 각 페이지를 순회하며 필요한 요소들을 크롤링 해 옵니다.
- 다음 5개 목록 페이지로 이동 버튼 다음 5개 목록 페이지로 이동할 수 있는 버튼입니다. 해당 버튼을 활용하여 다음 5개 목록 페이지로 넘어간 후 다시 각 페이지를 순회할 수 있도록 합니다.
- 마지막 페이지로 이동 버튼 맨 마지막 페이지로 이동할 수 있는 버튼입니다. 크롤링 시에는 필요하지 않은 요소입니다.
XPath 확인하기
각 요소들의 XPath를 확인하면, 다음과 같은 규칙이 발생하는 것을 확인할 수 있습니다. 이후 크롤링 코드 작성 시 해당 규칙을 활용하여 코드를 작성하게 됩니다.
- 1, 2, 3, 4, 5 페이지 버튼에 따라 li[1], li[2], li[3], li[4] li[5]로 구성되어 있는 것을 확인할 수 있습니다.
- 다음 5개 목록 페이지로 이동한 후에도 6, 7, 8, 9, 10 페이지 버튼은 li[1], li[2], li[3], li[4] li[5]로 동일하게 반복되는 것을 확인할 수 있습니다.
# 2번째 페이지의 XPath //*[@id="contents"]/article/div[4]/div[2]/ul/li[2]/button # 5번째 페이지의 XPath //*[@id="contents"]/article/div[4]/div[2]/ul/li[5]/button # 다음 5개 목록 페이지로 이동 //*[@id="contents"]/article/div[4]/div[2]/button[3]/span # 7번째 페이지의 XPath //*[@id="contents"]/article/div[4]/div[2]/ul/li[2]/button # 10번째 페이지의 XPath //*[@id="contents"]/article/div[4]/div[2]/ul/li[5]/button
2.5 Kotra 해외 시장 상품 동향 크롤링
2.5.1 크롤링 할 페이지 접속
먼저, 동적 크롤링에 필요한 selenium 라이브러리와 페이지 로딩을 기다려줄 때 필요한 time 라이브러리를 불러옵니다.
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys import time
selenium의 webdriver를 이용해 Chrome 브라우저를 열고 상품 DB 페이지로 이동합니다.
url = 'https://dream.kotra.or.kr/kotranews/cms/com/index.do?MENU_ID=430' driver = webdriver.Chrome() driver.get(url) print(driver.current_url) print(driver.title)
결과
https://dream.kotra.or.kr/kotranews/cms/com/index.do?MENU_ID=430 상품 DB - KOTRA 해외시장뉴스 상품·산업 | 상품 DB | 상품 DB
2.5.2 첫 페이지 크롤링
첫 페이지를 보면 게시일, 검색, 대륙, 국가, 무역관, 산업분류, HS CODE와 같이 검색 조건을 설정할 수 있게 되어 있지만, 저희는 조건을 따로 설정하지 않고 기본 페이지에서 크롤링을 진행하도록 하겠습니다.
먼저, 저희는 1페이지에 게시되어 있는 10개의 게시글의 제목, 국가, 무역관, 게시일을 차례로 크롤링 해보도록 하겠습니다. 4가지의 요소들을 저장할 데이터 프레임을 먼저 생성한 후, 각 요소들의 위치를 나타내는 XPath를 활용하여 제목, 국가, 무역관, 게시글의 텍스트 요소만을 찾아 데이터 프레임에 저장하도록 하겠습니다.
Dataframe 생성
크롤링을 통해 4가지 데이터를 저장할 Dataframe과 Column을 생성합니다.
import pandas as pd # 데이터 저장할 dataframe df = pd.DataFrame(columns=['title', 'country', 'trade', 'date'])
크롤링 할 요소들을 XPath를 이용해 불러오기
앞서 2.4.1에서 살펴본 각 요소들의 XPath로 첫 페이지 내 각 게시글의 4가지 요소들을 크롤링 하여 가져올 수 있습니다. for문을 사용해 각 게시글의 정보를 순회하며 필요한 데이터를 변수에 저장한 후 위에서 생성한 DataFrame에 저장해 줍니다.
for i in range(1, 11): # 제목 title = driver.find_element(By.XPATH, f"""//*[@id="tbody"]/tr[{i}]/td[2]/a""").text # 국가 country = driver.find_element(By.XPATH, f"""//*[@id="tbody"]/tr[{i}]/td[3]""").text # 무역관 trade = driver.find_element(By.XPATH, f"""//*[@id="tbody"]/tr[{i}]/td[4]""").text # 게시일 date = driver.find_element(By.XPATH, f"""//*[@id="tbody"]/tr[{i}]/td[5]""").text # 데이터 프레임에 저장 df.loc[len(df)] = [title, country, trade, date]
webdriver 종료
크롤링이 정상적으로 종료되었다면 아래 코드를 실행시켜 webdriver를 종료합니다.
driver.close()
Dataframe 확인
df를 출력해 크롤링 된 데이터와 그 형태를 확인합니다.
df
결과
2.5.3 상세 페이지 크롤링
앞서 크롤링 한 결과를 살펴보면, 제목이 “태국 철강 시장 동향”, “폴란드 알루미늄 포일 시장 동향”과 같이 제목만으로는 쉽게 내용을 파악할 수 없습니다. 따라서 상세 페이지 안으로 들어가 추가적인 내용을 크롤링 해보도록 하겠습니다.
완성된 크롤링 코드
아래 코드는 2.5.3에서 살펴볼 코드의 완성본입니다.
# 검색 조건 설정: 최근 3개년 간 대만의 상품 시장 동향 파악하기 # 게시일: 2020/07/01 - 2023/07/01 # 대륙: 아시아 # 국가: 대만 for title_num in range(1, 11): time.sleep(0.5) # 상세 페이지 들어가기 post_button = driver.find_element(By.XPATH, f"""//*[@id="tbody"]/tr[{title_num}]/td[2]/a""") post_button.click() # 제목 title = driver.find_element(By.XPATH, f"""//*[@id="pdfArea"]/dl/dt/div[1]/strong""").text # 국가 country = driver.find_element(By.XPATH, f"""//*[@id="pdfArea"]/dl/dt/div[2]/ul/li[2]""").text # 게시일 date = driver.find_element(By.XPATH, f"""//*[@id="pdfArea"]/dl/dt/div[2]/ul/li[4]""").text # 키워드 keyword = driver.find_element(By.XPATH, f"""//*[@id="pdfArea"]/dl/dd/div[1]""").text # 요약 summary = driver.find_element(By.XPATH, f"""//*[@id="pdfArea"]/dl/dd/div[2]""").text # 상세 페이지 나오기 list_button = driver.find_element(By.XPATH, """//*[@id="contents"]/article/div[1]/div[2]/a/span""") list_button.click() # 데이터프레임에 저장 df_taiwan.loc[len(df_taiwan)] = [title, country, date, keyword, summary]
다시, selenium의 webdriver를 이용해 Chrome 브라우저를 열고 상품 DB 페이지로 이동합니다.
url = 'https://dream.kotra.or.kr/kotranews/cms/com/index.do?MENU_ID=430' driver = webdriver.Chrome(path) driver.get(url)
이번 챕터에서는 크롤링을 하기 전 검색 조건을 설정하여 크롤링을 진행해 보도록 하겠습니다. 최근 3개년 간 대만의 상품 시장 동향을 파악하기 위해 검색 조건을 설정해 보도록 하겠습니다. 우선 게시일을 2020년 7월 1일부터 2023년 7월 1일로 설정을 하고, 대륙을 ‘아시아’, 국가를 ‘대만’으로 설정한 후, 검색을 누르도록 하겠습니다.
그럼 각 게시글의 상세 페이지에서 제목, 국가, 게시일, 키워드, 요약을 차례로 크롤링 해보도록 하겠습니다. 동일하게 5가지의 요소들을 저장할 데이터 프레임을 먼저 생성한 후, 앞서 2.4.2에서 살펴본 5가지 요소들의 XPath를 활용하여 제목, 국가, 게시일, 키워드, 요약을 찾아 데이터 프레임에 저장하도록 하겠습니다.
Dataframe 생성
크롤링을 통해 5가지 데이터를 저장할 Dataframe과 Column을 생성합니다.
# 데이터 저장할 dataframe df_taiwan = pd.DataFrame(columns=['title', 'country', 'date', 'keyword', 'summary'])
상세 페이지로 이동하기
이번에는 각 게시글의 상세 페이지에서 제목, 국가, 게시일, 키워드, 요약을 차례로 크롤링 해보도록 하겠습니다. 먼저, 상세 페이지로 들어가기 위해서는 제목을 클릭해 주어야 합니다. 다음과 같이 click 메서드를 이용해 상세 페이지로 들어갈 수 있습니다.
# 상세 페이지 들어가기 post_button = driver.find_element(By.XPATH, f"""//*[@id="tbody"]/tr[{title_num}]/td[2]/a""") post_button.click()
ElementClickInterceptedException
웹 페이지에서 클릭할 때 발생하는 예외입니다. 이 오류는 클릭하려는 요소가 다른 요소에 의해 가려져 클릭할 수 없는 경우에 발생합니다. 이러한 경우에는 아래 코드와 같이 Selenium의 JavaScriptExecutor를 사용하여 클릭 이벤트를 직접 발생시킬 수 있습니다.
# 상세 페이지 들어가기 post_button = driver.find_element(By.XPATH, f"""//*[@id="tbody"]/tr[{title_num}]/td[2]/a""") driver.execute_script("arguments[0].click();", post_button)
상세 페이지에서 나와 원래 목록으로 돌아가기
반대로, 상세 페이지에서 빠져나와 원래의 목록으로 돌아가기 위해서는 목록 버튼을 클릭해 주어야 합니다. 여기서도 동일하게 click 메서드를 이용해 상세 페이지에서 빠져나올 수 있습니다.
# 상세 페이지 나오기 list_button = driver.find_element(By.XPATH, """//*[@id="contents"]/article/div[1]/div[2]/a/span""") list_button.click()
크롤링 할 요소들을 XPath를 이용해 불러오기
for문을 사용해 각 게시글의 상세 페이지를 순회하며 필요한 데이터를 변수에 저장한 후 위에서 생성한 DataFrame에 저장해 줍니다. 여기서 주의할 점은 페이지 로딩이 완료되기 전에 크롤링이 시작되면 오류가 나거나, 결괏값이 유실될 수 있으므로 time.sleep을 이용해 다음 코드의 실행 시간을 유예해 줍니다.
# 제목 title = driver.find_element(By.XPATH, f"""//*[@id="pdfArea"]/dl/dt/div[1]/strong""").text # 국가 country = driver.find_element(By.XPATH, f"""//*[@id="pdfArea"]/dl/dt/div[2]/ul/li[2]""").text # 게시일 date = driver.find_element(By.XPATH, f"""//*[@id="pdfArea"]/dl/dt/div[2]/ul/li[4]""").text # 키워드 keyword = driver.find_element(By.XPATH, f"""//*[@id="pdfArea"]/dl/dd/div[1]""").text # 요약 summary = driver.find_element(By.XPATH, f"""//*[@id="pdfArea"]/dl/dd/div[2]""").text
NoSuchElementError
웹 페이지가 모두 로딩이 되기 전에 크롤링이 시작되어서 나는 오류입니다.
해당 오류가 발생할 경우에 time.sleep()의 시간을 늘려주므로 해결할 수 있습니다.
webdriver 종료
크롤링이 정상적으로 종료되었다면 아래 코드를 실행시켜 webdriver를 종료합니다.
driver.close()
Dataframe 확인
df_taiwan을 출력해 크롤링 된 데이터와 그 형태를 확인합니다.
df_taiwan
결과
2.5.4 여러 페이지 크롤링 하기
이제는 단일 페이지가 아닌 여러 페이지로부터 크롤링을 해오도록 하겠습니다.
완성된 크롤링 코드
아래 코드는 2.5.4에서 살펴볼 코드의 완성본입니다.
# 검색 조건 설정: 최근 1개년 간 유럽 대륙의 상품 시장 동향 파악하기 # 게시일: 2022/07/01 - 2023/07/01 # 대륙: 유럽 for page_num in range(1, 16): try: for title_num in range(1, 11): time.sleep(2) post_button = driver.find_element(By.XPATH, f"""//*[@id="tbody"]/tr[{title_num}]/td[2]/a""") driver.execute_script("arguments[0].click();", post_button) time.sleep(1) # title title = driver.find_element(By.XPATH, f"""//*[@id="pdfArea"]/dl/dt/div[1]/strong""").text # country country = driver.find_element(By.XPATH, f"""//*[@id="pdfArea"]/dl/dt/div[2]/ul/li[2]""").text # date date = driver.find_element(By.XPATH, f"""//*[@id="pdfArea"]/dl/dt/div[2]/ul/li[4]""").text # keyword keyword = driver.find_element(By.XPATH, f"""//*[@id="pdfArea"]/dl/dd/div[1]""").text # summary summary = driver.find_element(By.XPATH, f"""//*[@id="pdfArea"]/dl/dd/div[2]""").text time.sleep(1) # 상세 페이지 나오기 list_button = driver.find_element(By.XPATH, """//*[@id="contents"]/article/div[1]/div[2]/a/span""") list_button.click() # 데이터프레임에 저장 df_europe.loc[len(df_europe)] = [title, country, date, keyword, summary] time.sleep(2) except: pass try: if (page_num % 5) != 0: page_button = driver.find_element(By.XPATH, f"""//*[@id="contents"]/article/div[4]/div[2]/ul/li[{page_num % 5 + 1}]/button""") driver.execute_script("arguments[0].click();", page_button) else: next_button = driver.find_element(By.XPATH, f"""//*[@id="contents"]/article/div[4]/div[2]/button[3]/span""") driver.execute_script("arguments[0].click();", next_button) time.sleep(2) except: pass
driver 실행
다시, selenium의 webdriver를 이용해 Chrome 브라우저를 열고 상품 DB 페이지로 이동합니다.
url = 'https://dream.kotra.or.kr/kotranews/cms/com/index.do?MENU_ID=430' driver = webdriver.Chrome(path) driver.get(url)
이번 챕터에서는 최근 1개년 간 유럽 대륙의 상품 시장 동향을 파악하기 위해 검색 조건을 설정해 보도록 하겠습니다. 우선 게시일을 2022년 7월 1일부터 2023년 7월 1일로 설정을 하고, 대륙을 ‘유럽’으로 설정한 후, 검색을 누르도록 하겠습니다.
그럼 이번에는 여러 페이지들을 넘겨가며 앞선 챕터와 동일하게 게시글의 상세 페이지로부터 제목, 국가, 게시일, 키워드, 요약을 차례로 크롤링 해보도록 하겠습니다.
Dataframe 생성
크롤링을 통해 5가지 데이터를 저장할 Dataframe과 Column을 생성합니다.
import pandas as pd # 데이터 저장할 dataframe df_europe = pd.DataFrame(columns=['title', 'country', 'date', 'keyword', 'summary'])
페이지 넘기기
앞서 2.4.3에서 살펴본 바와 같이 하단에 페이지 버튼의 구성을 보면 5개 단위로 페이지 버튼이 존재하고, 양쪽에 다음 페이지로 넘어가는 버튼(<, >)과, 끝 페이지로 넘어가는 버튼(<<, >>)이 있는 것을 확인할 수 있습니다. 페이지를 넘길 때에는 일정한 규칙이 있는 것을 확인할 수 있는데요,
- 페이지 번호가 5의 배수가 아닐 경우에는 숫자로 이루어진 바로 다음 페이지의 버튼을 눌러야 합니다. 가령, 현재 페이지가 1페이지일 경우에는 2페이지로, 2페이지일 경우에는 3페이지로, 3페이지일 경우에는 4페이지로 이동해야 합니다.
- 반면, 페이지 번호가 5의 배수일 경우에는 숫자 버튼이 아닌 다음 페이지로 넘어가는 버튼(>)을 눌러야 합니다.
이러한 규칙을 적용하여 코드를 작성해 보면 다음과 같이 작성할 수 있습니다.
# 페이지 번호가 5의 배수가 아닐 경우 if (page_num % 5) != 0: page_button = driver.find_element(By.XPATH, f"""//*[@id="contents"]/article/div[4]/div[2]/ul/li[{page_num % 5 + 1}]/button""") driver.execute_script("arguments[0].click();", page_button) # 페이지 번호가 5의 배수일 경우 else: next_button = driver.find_element(By.XPATH, f"""//*[@id="contents"]/article/div[4]/div[2]/button[3]/span""") driver.execute_script("arguments[0].click();", next_button)
크롤링 할 요소들을 XPath를 이용해 불러오기
상세 페이지를 순회할 때처럼 동일하게, 여러 페이지를 순회할 때에도 for문을 사용합니다. 한 페이지 내의 상세 페이지들의 크롤링을 완료하고, DataFrame에 데이터를 저장한 후에는 다음 페이지 버튼의 XPath를 찾고 클릭하여 다음 페이지로 넘어갑니다. 첫 페이지부터 마지막 페이지까지 크롤링을 할 것이기 때문에 페이지 하단의 총 페이지 수를 확인해 줍니다.
이번에 주의해야 할 점은 마지막 페이지의 경우 게시글의 수가 10개가 아닐 수 있고, 숫자 페이지 버튼이 존재하지 않을 수 있기 때문에, try-except문을 사용해 주어 코드가 비정상적으로 종료되지 않게 제어해 줍니다.
for page_num in range(1, 16): try: for title_num in range(1, 11): time.sleep(2) post_button = driver.find_element(By.XPATH, f"""//*[@id="tbody"]/tr[{title_num}]/td[2]/a""") driver.execute_script("arguments[0].click();", post_button) time.sleep(1) # title title = driver.find_element(By.XPATH, f"""//*[@id="pdfArea"]/dl/dt/div[1]/strong""").text # country country = driver.find_element(By.XPATH, f"""//*[@id="pdfArea"]/dl/dt/div[2]/ul/li[2]""").text # date date = driver.find_element(By.XPATH, f"""//*[@id="pdfArea"]/dl/dt/div[2]/ul/li[4]""").text # keyword keyword = driver.find_element(By.XPATH, f"""//*[@id="pdfArea"]/dl/dd/div[1]""").text # summary summary = driver.find_element(By.XPATH, f"""//*[@id="pdfArea"]/dl/dd/div[2]""").text time.sleep(1) # 상세 페이지 나오기 list_button = driver.find_element(By.XPATH, """//*[@id="contents"]/article/div[1]/div[2]/a/span""") list_button.click() # 데이터프레임에 저장 df_europe.loc[len(df_europe)] = [title, country, date, keyword, summary] time.sleep(2) except: pass
webdriver 종료
크롤링이 정상적으로 종료되었다면 아래 코드를 실행시켜 webdriver를 종료합니다.
driver.close()
Dataframe 확인
df_europe을 출력해 크롤링 된 데이터와 그 형태를 확인합니다.
df_europe
결과
2.6 데이터 전처리
Dataframe에 저장된 데이터의 전처리를 진행하겠습니다.
데이터 원본 복사
본격적인 전처리를 시작하기에 앞서, 데이터 원본 새로운 변수에 저장합니다. Dataframe 원본을 복사해두는 이유는 전처리 과정에서 오류가 나거나, 실수를 했을 경우를 대비하는 것입니다.
df_temp = df_europe.copy()
만약 전처리 과정에서 오류가 난다면, 아래 코드를 실행하여 데이터 원본을 불러와 사용할 수 있습니다.
# 전처리 과정 중에 데이터 원본이 다시 필요한 경우 아래 코드 실행 df = df_temp.copy()
2.6.1 중복 게시글 제거
만약 크롤링을 여러 번 반복하여 Dataframe에 데이터를 저장했다면, 중복되는 값이 존재할 수 있습니다. 이런 경우 아래 코드를 활용해 중복되는 행을 제거할 수 있습니다.
# 중복행 확인 df_europe[df_europe.duplicated(subset=['title'], keep='first')]
결과
# 중복행 제거 df_europe = df_europe.drop_duplicates(subset=['title'],keep='first')
2.6.2 keyword 칼럼의 불필요한 요소 제거
keyword에 저장된 값들 중에서 ‘Keyword’나 ‘#(해시태그)’와 같은 불필요한 요소들을 제거하고 정확한 키워드만 남기도록 전처리를 진행해 주도록 하겠습니다. 또한, 앞뒤 공백도 함께 제거해 주도록 하겠습니다.
# Keyword 및 해시태그 제거 # 앞 뒤 공백 제거 for index, row in df_europe.iterrows(): df_europe.loc[index, 'keyword'] = df_europe.loc[index, 'keyword'].replace('Keyword', '').replace('#', '').strip()
df_europe을 출력해 전처리가 정상적으로 실행되었는지 확인합니다.
df_europe
결과
2.7 MySQL DB에 데이터 적재
Kotra에서 크롤링 하여 전처리까지 완료한 데이터를 MySQL DB에 저장해 보겠습니다.
2.7.1 DB 생성
먼저, root 계정에 ‘kotra’ DB를 생성하겠습니다.
MySQL DB와 연동을 도와줄 pymysql 라이브러리를 import 하고, root 계정의 password를 변수에 저장합니다.
import pymysql # password 입력 password = '0000'
앞서 저장한 password를 이용하여 MySQL DB에 root 계정으로 접속하는 Connection 객체를 생성합니다.
# MySQL Connection conn = pymysql.connect(host = '127.0.0.1', port = 3306, user = 'root', password = password, charset='utf8')
Connection 객체의 커서(cusor) 객체를 생성하고, ‘kotra’ DB를 생성하는 CREATE문을 실행합니다. 실행한 SQL문을 실제 DB에 적용하기 위해 commit을 실행합니다. with 절을 이용하였기에 Connection과 커서는 모든 작업을 끝낸 후 자동으로 close 됩니다.
# SQL문 실행 with conn: with conn.cursor() as cur: cur.execute('CREATE DATABASE kotra') conn.commit()
2.7.2 테이블 생성
앞에서 생성한 ‘kotra’ DB에 접속하여 테이블을 생성하고 크롤링 한 데이터를 저장해 보겠습니다.
먼저 ‘kotra’ DB에 root 계정으로 접속하는 Connection 객체를 생성합니다.
# MySQL Connection conn = pymysql.connect(host = '127.0.0.1', port = 3306, user = 'root', password = password, charset='utf8', database='kotra')
아래는 5개 속성을 저장할 테이블을 생성하는 SQL문입니다. 제목(title), 국가(country), 키워드(keyword), 요약(summary)를 text형으로, date(게시일)을 date형으로 설정하겠습니다.
# 테이블 생성 SQL문 sql = '''create table kotra ( title text, country text, date date, keyword text, summary text);'''
Connection 객체의 커서(cusor) 객체를 생성하고, ‘kotra’ DB에 ‘kotra’ 테이블을 생성하는 SQL문을 실행합니다. 실행한 SQL문을 실제 DB에 적용하기 위해 commit을 실행합니다. with 절을 이용하였기에 커서는 SQL문을 실행한 후 자동으로 close 됩니다.
# SQL문 실행 - 테이블 생성 with conn.cursor() as cur: cur.execute(sql) conn.commit()
2.7.3 데이터 저장
앞서 생성한 ‘kotra’ 테이블에 크롤링 한 데이터를 저장하겠습니다.
# SQL문 실행 - 데이터 삽입 with conn.cursor() as cursor: for _, row in df_europe.iterrows(): sql = ''' INSERT INTO kotra (title, country, date, keyword, summary) VALUES (%s, %s, %s, %s, %s) ''' values = ( row['title'], row['country'], row['date'], row['keyword'], row['summary'] ) cursor.execute(sql, values) # 커밋 conn.commit()
테이블 생성 및 데이터 저장 작업을 모두 마쳤으니 ‘kotra’ DB에 연결시켜둔 Connection 객체를 close 합니다.
# 연결 종료 conn.close()
2.7.4 MySQL에서 저장된 데이터 확인하기
MySQL Workbench 프로그램을 실행하여 생성한 ‘kotra’ DB와 ‘kotra’ 테이블과 테이블에 저장된 데이터들을 확인해 보겠습니다.
아래 코드를 실행시키면 MySQL DB에 생성되어 있는 모든 Database를 확인할 수 있습니다. 앞서 생성한 ‘kotra’ DB가 존재하는 것을 확인할 수 있습니다.
SHOW DATABASES;
결과
아래 코드를 실행시키면 ‘kotra’ DB를 선택하여 사용할 수 있습니다.
USE kotra;
아래 코드를 실행시키면 현재 선택한 DB(’kotra’ DB)에 생성되어 있는 모든 테이블을 확인할 수 있습니다. 앞서 생성한 ‘kotra’ 테이블이 존재하는 것을 확인할 수 있습니다.
SHOW TABLES;
결과
아래 코드를 실행시키면 INSERT문으로 삽입한 데이터들이 모두 정상적으로 저장된 것을 확인할 수 있습니다.
select * from kotra;
결과
2.8 DB 활용 - 필터링
필터링을 사용하여 DB에 저장한 데이터들에서 조건에 만족하는 제품을 찾아보겠습니다.
먼저, ‘kotra’ DB에 root 계정으로 접속하는 Connection 객체를 생성합니다.
# MySQL Connection conn = pymysql.connect(host = '127.0.0.1', port = 3306, user = 'root', password = password, charset='utf8', database='kotra')
다음으로, ‘독일’ 국가의 제목, 국가, 게시일, 키워드를 조회하고, 최근 게시글 순으로 정렬하는 SQL문을 작성해 보도록 하겠습니다.
query = '''SELECT title, country, date, keyword FROM kotra WHERE country = '독일' ORDER BY date DESC;'''
이후, Connection 객체의 커서(cusor) 객체를 생성하고, 조건을 만족하는 게시글을 조회하는 SQL문을 실행합니다. 조건을 만족하는 게시글 데이터를 fetchall 메서드를 통해 받아옵니다. with 절을 이용하였기에 커서는 SQL문을 실행한 후 자동으로 close 됩니다.
# SQL문 실행 - 데이터 조회 with conn.cursor() as cursor: cursor.execute(query) result = cursor.fetchall()
조건을 만족하는 데이터가 총 몇 개인지 len 메서드를 통해 확인했습니다. 또한 상품의 전체 데이터를 데이터프레임으로 변환하여 확인했습니다.
# 결과 출력 print(f'조건에 일치하는 상품 동향: 총 {len(result)}개') result_df = pd.DataFrame(result, columns=['title', 'country', 'keyword', 'date']) result_df
결과
데이터 조회 작업을 모두 마쳤으니 ‘kotra’ DB에 연결해둔 Connection 객체를 close 합니다.
# MySQL DB Connection 종료 conn.close()