본문 바로가기
코드리뷰/chatGPT(Python)코드

코드 한 줄로 프로그램 배포하기

by 디마드 2024. 10. 18.

초보자를 위한 나만의 쿠팡 크롤러 UI 만들기  다섯번 영상입니다.

이 영상을 끝까지 보고나면 파이썬이 설치되어 있지 않은 컴퓨터에서도 프로그램을 실행할 수 있습니다.

바로 exe 실행 파일이 만들어 집니다. 

https://youtu.be/oLWZoKGpznw

웹 크롤러 소스코드입니다. 조금씩 고도화하고 있어요. 

import tkinter as tk
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
import tkinter.messagebox as msg
from datetime import datetime
from tkinter import ttk
import csv
from threading import Thread

# 키보드(엔터) 입력 처리
def on_enter_key(event):
    btnSearch.invoke()

# Selenium 설정 함수
def setup_selenium():
    chrome_options = Options()
    chrome_options.add_argument("--headless")  # 헤드리스 모드 설정
    chrome_options.add_argument("--no-sandbox")  # 리눅스 환경에서 필요할 수 있는 옵션
    chrome_options.add_argument("--disable-dev-shm-usage")  # 메모리 부족 문제 해결을 위한 옵션
    chrome_options.add_argument("--disable-gpu")  # GPU 비활성화 (옵션)
    chrome_options.add_argument("--window-size=300,200")
    # chrome_options.add_argument("--no-startup-window")  # 브라우저 시작시 화면이 나타나지 않게 함
    chrome_options.add_argument(
        "user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
        )  # User-Agent 설정

    driver = webdriver.Chrome(options=chrome_options)
    return driver

# 상품 검색
def crawl_website() :
    # 진행 상태 초기화
    progress_var.set(0)
    # loading_bar.pack(fill=tk.X, expand=True, padx=10)
    loading_bar.pack(fill=tk.X, padx=5,pady=7)
    loading_bar.start()
    # 리스트 삭제 
    for item in result_tree.get_children():
        result_tree.delete(item)
    keyword = entKeyword.get()

    # 웹 드라이버 시작
    driver = setup_selenium()
    driver.get (f"https://www.coupang.com/np/search?q={keyword}")
    driver.implicitly_wait(5)
    results = driver.find_elements(By.CLASS_NAME, "descriptions-inner")
    # 페이지 제목 출력
    print(driver.title + "-" + str(len(results)))
    for rank, r in enumerate(results,1) :
        max_cnt = 20 # 최대 검색건수
        if rank > 20 : break
        try :
            name = r.find_element(By.CLASS_NAME, "name")
            price = r.find_element(By.CLASS_NAME, "price")
            
            sale_price = price.find_element(By.CLASS_NAME, "sale")
            if sale_price :
                last_price = sale_price.find_element(By.CLASS_NAME,"price-value")
                print(f"{rank}위 {name.text} {last_price.text}")
                result_tree.insert('', tk.END, values=(rank,name.text,last_price.text) )
            else :
                base_price = price.find_element(By.CLASS_NAME,"price-info")
                print(f"{rank}위 {name.text}  {base_price.text} ")
                result_tree.insert('',tk.END, values=(rank,name.text,base_price.text) )
            
            # result_tree.update_idletasks() # UI 즉시 업데이트
            progress_var.set(rank / max_cnt * 100)  # 진행률 업데이트

        except :
            print(f"skip")
    driver.quit()
    loading_bar.stop()
    loading_bar.pack_forget()
# 검색결과 treeview 입력 중지
def btn_exit_click() :
    win.quit()

# 파일 저장
def save_file():
    if len(result_tree.get_children()) == 0 : 
        msg.showwarning("저장","저장할 데이터가 없습니다.")
        return
    """Treeview의 모든 항목을 CSV 파일로 저장"""
    now = datetime.now()
    file_name = entKeyword.get() + "_" + now.strftime("%y%m%d_%H%M")
    
    with open(f"./data/{file_name}.csv", mode='w', newline='', encoding='euc-kr') as file:
        writer = csv.writer(file)
        # 열 제목 추가
        writer.writerow(["순위", "상품명", "가격"])
        
        for item in result_tree.get_children():
            writer.writerow(result_tree.item(item)["values"])  # 항목의 값을 가져와서 저장
            
    msg.showinfo("저장 완료", f"검색결과를 '{file_name}.csv'로 저장하였습니다.")

# 스레드를 사용해 크롤링 실행
def start_crawling():
    thread = Thread(target=crawl_website)
    thread.start()

# 메인 윈도우 설정
win = tk.Tk()
win.geometry("600x550")
win.title("쿠팡 크롤러")

# 스타일 설정
style = ttk.Style()
style.configure("TButton", font=("맑은 고딕", 12), padding=4)
style.configure("TLabel", font=("맑은 고딕", 12))
style.configure("TEntry", font=("맑은 고딕", 12), padding=4, relief="solid")

# 검색 필드 프레임(위)
top_frame = ttk.Frame(win)
top_frame.pack(side=tk.TOP, pady=10)

lblKeyword = ttk.Label(top_frame,text="Keyword")
lblKeyword.pack(side=tk.LEFT, padx=10)

entKeyword = ttk.Entry(top_frame,width=30)
entKeyword.pack(side=tk.LEFT, padx=10)
entKeyword.focus() 
entKeyword.bind("<Return>",on_enter_key)

btnSearch = ttk.Button(top_frame, text="쿠팡검색")
btnSearch.config(command=start_crawling)
btnSearch.pack(side=tk.LEFT, padx=10)

# 로딩 스피너 (진행 바)
loading_bar = ttk.Progressbar(top_frame, mode='indeterminate')
# loading_bar.pack(pady=5)

# 검색 결과 프레임(중간)
result_frame = ttk.Frame(win)
result_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

columns = ("순위","상품명","가격")
result_tree = ttk.Treeview(result_frame, columns=columns, show="headings")
result_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
result_tree.heading("순위", text="순위")
result_tree.column("순위",width=50,stretch=False, anchor=tk.E)
result_tree.heading("상품명", text="상품명")
result_tree.column("상품명",stretch=True, anchor=tk.W)
result_tree.heading("가격", text="가격")
result_tree.column("가격",width=100,stretch=False, anchor=tk.E)

scrollbar_y = ttk.Scrollbar(result_frame,orient=tk.VERTICAL,command=result_tree.yview)
scrollbar_y.pack(side=tk.RIGHT, fill=tk.Y)
result_tree.config(yscrollcommand=scrollbar_y.set)

# Progressbar 생성
progress_var = tk.DoubleVar()  # 진행 상태 값을 저장할 변수
progress_bar = ttk.Progressbar(win, orient="horizontal", length=400, mode="determinate", variable=progress_var)
progress_bar.pack(pady=2, side=tk.TOP, fill=tk.X, padx=20)

# 버튼 프레임 (하단 배치)
button_frame = ttk.Frame(win)
button_frame.pack(side=tk.TOP, pady=2)

btnSave = ttk.Button(button_frame,text="파일저장")
btnSave.config(command=save_file)
btnSave.pack(side=tk.LEFT, pady=2)

btnStop = ttk.Button(button_frame, text="종료")
btnStop.config(command=btn_exit_click)
btnStop.pack(side=tk.RIGHT, pady=2)



win.mainloop()

 

반응형

댓글