반응형

이번에는, 셀레니움을 이용해서 간단하게 나라장터 데이터를 수집하는 프로그램을 제작해 볼 계획이에요

 

총 네 단계 포스팅으로 나누어 진행할 계획이고요

첫 번째 포스팅에서는  여러 개의 frame으로 이루어진 HTML을 다루는 방법에 대해

안내해 드리겠습니다 ^^

 

설명에 관심 없으신 분들은 전체 코드를 맨 아래에 입력해 둘 테니 설명은 스킵하셔도 좋습니다~

 

셀레니움이 뭔지, 셀레니움은 어떻게 설치하는지 등에 대해서는 아래 포스팅된 글을 참고해 주세요 ^^

https://rogios-story.tistory.com/entry/selenium-0-1

 

셀레니움(selenium) 시작하기

import selenium help(selenium)​ pip install selenium 파이썬으로 셀레니움(selenium)을 처음 이용하시는 분들을 위한 글이예요 셀레니움은 파이썬 코드를 이용해서 웹(Chrome, firefox, bing, explorer 등)을 조작하는

rogios-story.tistory.com

 

 

먼저, 나라장터 링크는 아래와 같아요

https://www.g2b.go.kr/pt/menu/selectSubFrame.do?framesrc=/pt/menu/frameTgong.do?url=https://www.g2b.go.kr:8101/ep/tbid/tbidFwd.do 

 

https://www.g2b.go.kr/pt/menu/selectSubFrame.do?framesrc=%2Fpt%2Fmenu%2FframeTgong.do%3Furl%3Dhttps%3A%2F%2Fwww.g2b.go.kr%3A8101%2Fep%2Ftbid%2FtbidFwd.do

 

www.g2b.go.kr

 

오늘은 아래 라이브러리들을 이용해서 나라장터 데이터들을 수집해서, 원하는 데이터만 필터링을 해볼게요

(참고로 이러한 작업을 크롤링이라고 표현하는 것 같아요)

from selenium import webdriver
from selenium.webdriver.common.by import By
import pandas as pd
import requests
from bs4 import BeautifulSoup

 

위 코드를 따라 할 때, 아래 Beautifulsoup 라이브러리가 없어서 에러가 나시는 분은 아래 방법대로,

Anaconda Prompt를 관리자모드로 열어서 아래 코드를 이용해 설치해 주세요

pip install beautifulsoup4

자 이제 모든 준비가 끝났으니, 크롤링을 시작해 볼게요

 

 

1. 셀레니움으로 크롬 열어서 url 접속하기

#%% 
# 셀레니움 웹드라이브 열기
driver = webdriver.Chrome()

# 나라장터  URL
url = "https://www.g2b.go.kr/pt/menu/selectSubFrame.do?framesrc=/pt/menu/frameTgong.do?url=https://www.g2b.go.kr:8101/ep/tbid/tbidFwd.do"

# 나라장터 접속
driver.get(url)

 

이 화면이 나오겠죠?

 

2. 여러 개의 frame으로 나뉜 HTML 이용 방법

 

위 페이지에서 용역 -> 공고현황 -> 입찰공고 -> 최근 6개월 -> 목록 수(100건, 검색건수 표시) 설정을 하도록

해볼게요

 

크롤링 작업을 할 때는, 크롤링하고자 하는 웹페이지를 자세히 살펴보는 게 중요해요

웹 페이지마다 다른 구조로 작성되어 있으니까요

 

웹페이지의 구조를 파악하기 위해서,

먼저 내가 클릭하고 싶은 요소의 위치에 마우스를 올리고 우클릭을 하고, 검사를 누르세요

 

처음 한 번만, 검사 버튼을 클릭하면 아래처럼 웹 페이지의 소스를 볼 수 있어요

 

이때, 다시 한번 더 같은 위치에 우클릭해서 검사 버튼을 클릭하면

(소스 보기가 열린 상태에서 원하는 요소를 우클릭해서 검사 버튼을 클릭하면)

 

바로, 해당 요소의 HTML위치가 탐색됩니다

 

요소의 위치를 찾았으니, 해당 요소를 셀레니움으로 탐색하게 해서 변수로 담아줄 거예요

 

셀레늄으로 변수를 탐색하는 방법은 다양한데, 나중에 자세히 다루기로 하고

이번 포스팅에서는 XPATH를 이용해서 탐색하게 해 보겠습니다

 

 

 

위 방법대로 Copy Xpath 버튼을 누르면, 자동으로 해당 요소의 Xpath가 복사돼요

//*[@id="000019"]/a  <<<<<<<<이렇게 나왔네요

 

이 요소를 셀레니움으로 탐색하게 하려면 아래 코드를 이용하면 됩니다

# 용역 << 요소 탐색
용역버튼 = driver.find_element(By.XPATH, '//*[@id="000019"]/a')

 

여기에서 문제!!! 중요하니, 자세히 보세요!

HTML을 잘 모르고 일반적인 셀레니움 사용법만 터득해서 이용하는 사람들이 오랫동안 헤매는 부분이 있습니다!

위 코드를 그대로 이용하면 다음과 같은 에러가 발생할 거예요!!!!

저도 이런 부분에서 "나는 분명히 잘 따라 했는데, 왜 안 되는 거지???"

라고 스트레스 엄청 받았던 기억이 있네요

 

분명히 소스보기에서 있는 요소의 XPATH 주소를 복사했고, 코드에도 문제가 없는데 이러한 에러가 발생하면 frame이 나뉘어있는지 의심해보아야 해요

 

 

첫 번째 프레임은 id='tops'라고 되어있고, 보이는 화면에서도 맨 위쪽에 대한 요소들이 담겨있네요

 

두 번째 frame은 id 값은 없지만, name='left'로 지정해서 왼쪽 목록들의 요소가 담겨있는 게 보여요

 

저희는 frame를  이동하지 않고 초기 지정된 첫 번째 frame에서 요소를 탐색했기 때문에 발생한 에러였어요!

 

 

초보자 분들은 이 부분에 대한 정보가 없기 때문에 여기에서 막혀서 헤매는 경우가 무지 많거든요

(과거의 나...)

 

이제, 저는 이 정도 수준은 넘어섰으니 프레임을 이동해서 진행하는 방법을 안내해 드리겠습니다 하하

 

천천히 step by step으로 진행해 볼게요

먼저, 소스보기에서 frame들만 보이게 접어요

 

아래 결과가 출력될 거예요

기본으로 설정된(default) frame에서는 총 3개의 프레임이 존재하고, 각각의 id가 [tops, sub, hdn]인 것을 확인할 수 있고

우리가 이동하려는 요소는 frame id가 id='sub'라는 곳에 담겨있는 모습을 확인할 수 있어요

 

그럼, 검토 차원에서 초기 설정된 frame에서 몇 개의 frame이 존재하는지 코드로 검토해 볼까요?

#%% default 프레임에서 모든 프레임 요소 가져오기

driver.switch_to.default_content()  # 기본 문서로 돌아가기   <<<<<<< 한번도 이동하지 않았기에 필요 없지만, 넣어둠

frames = driver.find_elements(By.TAG_NAME,'frame')

# 각 프레임의 name 출력
for frame in frames:
    frame_name = frame.get_attribute('name')
    print(f'프레임 이름: {frame_name}')
print('프레임 개수 : ',len(frames))

 

아래 코드를 실행시켜 보면 아래와 같은 결과가 출력될 거예요

각각 tops, sub, hdn이라는 프레임 name이 출력되었고, 총프레임의 개수가 3개라는 것을 알았어요

 

 

우리가 클릭하고 싶었던 요소는 두 번째 프레임인 id = 'sub'이며, name = 'sub'인 곳에 있었으니

id 값을 이용해서 두 번째 프레임으로 이동해 볼게요

 

driver.switch_to.frame('sub') #frame id를 이용하여 이동하는 코드
driver.switch_to.frame(1) #frame 번호로 이동하는 코드  (0,1,2) 순서 이므로 2번 째 프레임으로 이동함

위 코드 둘 중 하나를 이용하시면 돼요 ^^

 

저는 driver.switch_to.frame('sub') 코드를 이용할게요

프레임 이동 후 아래 코드를 입력하면 검토가 가능하답니다 ^^

driver.execute_script("return window.frameElement.id;")

#출력 결과

 

sub 프레임으로 이동한 것을 확인했으니, 소스보기에서 sub 프레임을 펼쳐볼게요

아래 결과가 보일 거예요

 

여기에서 우리가 클릭하려 했던 요소는 left 프레임에 담겨있었으니, 다시 한번 left 프레임으로 이동해야 해요

driver.switch_to.frame('left') #frame id or name을 이용하여 이동하는 코드 저도 확실하지 않아서 검토 해 보아야함
driver.execute_script("return window.frameElement.name;")   #현재 프레임 name 갑을 출력하는 코드

#출력 결과

 

드디어 left 프레임으로 들어왔으니, 해당 요소를 클릭해 볼게요!

 

다시 셀레니움으로 요소를 탐색하여 변수에 담는 코드를 실행시키고!

용역버튼 = driver.find_element(By.XPATH, '//*[@id="000019"]/a')  
용역버튼.click()

 

요소를 클릭하는 코드를 실행하면?

짜란~ 잘 클릭된 모습을 볼 수 있어요!!

 

ㅜㅜ 초보자용으로 포스팅하려다 보니, 프레임 이용에만 포스팅이 너무 길었네요

 

하지만, 제가 frame 때문에 며칠을 고생하다 포기하고 다시 했던 기억을 되돌려보면...

 

꼭 필요했던 내용이라 가능한 자세히 설명하고자 했어요

 

이다음 내용부터는 빠르게 진행할 테니 이해해 주시면 감사하겠습니다~

 

다음 포스팅에서 봬요~

 

#전체 코드

# -*- coding: utf-8 -*-
"""
Created on Fri Sep 29 21:12:23 2023

@author: 진연녹
"""

from selenium import webdriver
from selenium.webdriver.common.by import By
import pandas as pd
import requests
from bs4 import BeautifulSoup

#%% 
# 셀레니움 웹드라이브 열기
driver = webdriver.Chrome()

# 나라장터  URL
url = "https://www.g2b.go.kr/pt/menu/selectSubFrame.do?framesrc=/pt/menu/frameTgong.do?url=https://www.g2b.go.kr:8101/ep/tbid/tbidFwd.do"

# 나라장터 접속
driver.get(url)
#%%

# 용역 << 요소 탐색
# 용역버튼 = driver.find_element(By.XPATH, '//*[@id="000019"]/a')  # <<<<<<<<<<< 코드는 설명을 위해 고의로 에러를 발생시켰으므로 최종 코드에서는 주석처리 함


#%% default 프레임에서 모든 프레임 요소 가져오기

driver.switch_to.default_content()  # 기본 문서로 돌아가기   <<<<<<< 한번도 이동하지 않았기에 필요 없지만, 넣어둠

frames = driver.find_elements(By.TAG_NAME,'frame')

# 각 프레임의 name 출력
for frame in frames:
    frame_name = frame.get_attribute('name')
    print(f'프레임 이름: {frame_name}')
print('프레임 개수 : ',len(frames))


driver.switch_to.frame('sub') #frame id or name을 이용하여 이동하는 코드 저도 확실하지 않아서 검토 해 보아야함
# driver.switch_to.frame(1) #frame 번호로 이동하는 코드  (0,1,2) 순서 이므로 2번 째 프레임으로 이동함

driver.execute_script("return window.frameElement.id;")   #현재 프레임 id 갑을 출력하는 코드



driver.switch_to.frame('left') #frame id or name을 이용하여 이동하는 코드 저도 확실하지 않아서 검토 해 보아야함
driver.execute_script("return window.frameElement.name;")   #현재 프레임 name 갑을 출력하는 코드


용역버튼 = driver.find_element(By.XPATH, '//*[@id="000019"]/a')  
용역버튼.click()

driver.switch_to.default_content()  # 기본 문서로 돌아가기

 

잘 보셨으면 댓글 하나씩만 달아주세요!!!!!!!!!!!!

반응형

+ Recent posts