diff --git a/product_images/product_063e81de0e8b49288ee56de046800822.png b/product_images/product_063e81de0e8b49288ee56de046800822.png new file mode 100644 index 0000000..ce5a1ef Binary files /dev/null and b/product_images/product_063e81de0e8b49288ee56de046800822.png differ diff --git a/product_images/product_0c50cf571c694c2b8ca139ac26a7b055.png b/product_images/product_0c50cf571c694c2b8ca139ac26a7b055.png new file mode 100644 index 0000000..0cfb32f Binary files /dev/null and b/product_images/product_0c50cf571c694c2b8ca139ac26a7b055.png differ diff --git a/product_images/product_0fc762ed8cfe413daef36625d0b145c5.png b/product_images/product_0fc762ed8cfe413daef36625d0b145c5.png new file mode 100644 index 0000000..353abed Binary files /dev/null and b/product_images/product_0fc762ed8cfe413daef36625d0b145c5.png differ diff --git a/product_images/product_1169e95f64df48d29b2a582f1c8d3874.png b/product_images/product_1169e95f64df48d29b2a582f1c8d3874.png new file mode 100644 index 0000000..a4c2d82 Binary files /dev/null and b/product_images/product_1169e95f64df48d29b2a582f1c8d3874.png differ diff --git a/product_images/product_148e290caac34e5481fcbf7cea5ede2e.png b/product_images/product_148e290caac34e5481fcbf7cea5ede2e.png new file mode 100644 index 0000000..24c9e7c Binary files /dev/null and b/product_images/product_148e290caac34e5481fcbf7cea5ede2e.png differ diff --git a/product_images/product_17b924bdb74d4fb3b5feaffc8495dcf4.png b/product_images/product_17b924bdb74d4fb3b5feaffc8495dcf4.png new file mode 100644 index 0000000..2f29424 Binary files /dev/null and b/product_images/product_17b924bdb74d4fb3b5feaffc8495dcf4.png differ diff --git a/product_images/product_1df8cd0647f94a5cbc02c5f4b2cdcc87.png b/product_images/product_1df8cd0647f94a5cbc02c5f4b2cdcc87.png new file mode 100644 index 0000000..fd6b0a1 Binary files /dev/null and b/product_images/product_1df8cd0647f94a5cbc02c5f4b2cdcc87.png differ diff --git a/product_images/product_23c312973d4941b2bac3479e218fb2a8.png b/product_images/product_23c312973d4941b2bac3479e218fb2a8.png new file mode 100644 index 0000000..a2c79b6 Binary files /dev/null and b/product_images/product_23c312973d4941b2bac3479e218fb2a8.png differ diff --git a/product_images/product_23ce390e7d8f48d18d3d89530cd25800.png b/product_images/product_23ce390e7d8f48d18d3d89530cd25800.png new file mode 100644 index 0000000..fc80d7a Binary files /dev/null and b/product_images/product_23ce390e7d8f48d18d3d89530cd25800.png differ diff --git a/product_images/product_29fa359fe8d54456ae99ecac5b2adf78.png b/product_images/product_29fa359fe8d54456ae99ecac5b2adf78.png new file mode 100644 index 0000000..ffbd0c4 Binary files /dev/null and b/product_images/product_29fa359fe8d54456ae99ecac5b2adf78.png differ diff --git a/product_images/product_2c74ce9d8e8f489aa8e4fbe8ea9626d9.png b/product_images/product_2c74ce9d8e8f489aa8e4fbe8ea9626d9.png new file mode 100644 index 0000000..768a85d Binary files /dev/null and b/product_images/product_2c74ce9d8e8f489aa8e4fbe8ea9626d9.png differ diff --git a/product_images/product_37bebafbc8c54489bfc3b45d8ad44a8b.png b/product_images/product_37bebafbc8c54489bfc3b45d8ad44a8b.png new file mode 100644 index 0000000..4993ed1 Binary files /dev/null and b/product_images/product_37bebafbc8c54489bfc3b45d8ad44a8b.png differ diff --git a/product_images/product_3cc2174c2778471b8141faaac2e5783e.png b/product_images/product_3cc2174c2778471b8141faaac2e5783e.png new file mode 100644 index 0000000..3b3f1a0 Binary files /dev/null and b/product_images/product_3cc2174c2778471b8141faaac2e5783e.png differ diff --git a/product_images/product_3f200814da514e1e94ce9a20febd18dc.png b/product_images/product_3f200814da514e1e94ce9a20febd18dc.png new file mode 100644 index 0000000..807d0ea Binary files /dev/null and b/product_images/product_3f200814da514e1e94ce9a20febd18dc.png differ diff --git a/product_images/product_43b01ae9498049298ab27885fa2838f6.png b/product_images/product_43b01ae9498049298ab27885fa2838f6.png new file mode 100644 index 0000000..a359b76 Binary files /dev/null and b/product_images/product_43b01ae9498049298ab27885fa2838f6.png differ diff --git a/product_images/product_473c3553e32d4e1ebaf9227eca1e59c7.png b/product_images/product_473c3553e32d4e1ebaf9227eca1e59c7.png new file mode 100644 index 0000000..9920f8d Binary files /dev/null and b/product_images/product_473c3553e32d4e1ebaf9227eca1e59c7.png differ diff --git a/product_images/product_48b4d40102c144bcbabed7dd3089cde9.png b/product_images/product_48b4d40102c144bcbabed7dd3089cde9.png new file mode 100644 index 0000000..f41db1d Binary files /dev/null and b/product_images/product_48b4d40102c144bcbabed7dd3089cde9.png differ diff --git a/product_images/product_502856951ab946a1adc83c3074392b18.png b/product_images/product_502856951ab946a1adc83c3074392b18.png new file mode 100644 index 0000000..d0c1414 Binary files /dev/null and b/product_images/product_502856951ab946a1adc83c3074392b18.png differ diff --git a/product_images/product_5957470a639b4180a22f9c18fca23afd.png b/product_images/product_5957470a639b4180a22f9c18fca23afd.png new file mode 100644 index 0000000..b3808a0 Binary files /dev/null and b/product_images/product_5957470a639b4180a22f9c18fca23afd.png differ diff --git a/product_images/product_59cc91b02f014408b3e5e7545ffbad73.png b/product_images/product_59cc91b02f014408b3e5e7545ffbad73.png new file mode 100644 index 0000000..a2cf091 Binary files /dev/null and b/product_images/product_59cc91b02f014408b3e5e7545ffbad73.png differ diff --git a/product_images/product_5a56e5789fe74c16b9e5037ec914b106.png b/product_images/product_5a56e5789fe74c16b9e5037ec914b106.png new file mode 100644 index 0000000..5bdade5 Binary files /dev/null and b/product_images/product_5a56e5789fe74c16b9e5037ec914b106.png differ diff --git a/product_images/product_5e671ac50b564265a764270add58e248.png b/product_images/product_5e671ac50b564265a764270add58e248.png new file mode 100644 index 0000000..64babd7 Binary files /dev/null and b/product_images/product_5e671ac50b564265a764270add58e248.png differ diff --git a/product_images/product_5f608e609da446f8b0a51e423ac93e84.png b/product_images/product_5f608e609da446f8b0a51e423ac93e84.png new file mode 100644 index 0000000..66a92e0 Binary files /dev/null and b/product_images/product_5f608e609da446f8b0a51e423ac93e84.png differ diff --git a/product_images/product_6df649ca3838444ca556f4693ac384a3.png b/product_images/product_6df649ca3838444ca556f4693ac384a3.png new file mode 100644 index 0000000..74a6a96 Binary files /dev/null and b/product_images/product_6df649ca3838444ca556f4693ac384a3.png differ diff --git a/product_images/product_7a22aa364e4c4d6d9b99887e8ead4e64.png b/product_images/product_7a22aa364e4c4d6d9b99887e8ead4e64.png new file mode 100644 index 0000000..8d71e67 Binary files /dev/null and b/product_images/product_7a22aa364e4c4d6d9b99887e8ead4e64.png differ diff --git a/product_images/product_7b6c14cdeac14e2faa597ab0a680790a.png b/product_images/product_7b6c14cdeac14e2faa597ab0a680790a.png new file mode 100644 index 0000000..a3f37f4 Binary files /dev/null and b/product_images/product_7b6c14cdeac14e2faa597ab0a680790a.png differ diff --git a/product_images/product_817680227fa540d88203e618fdc96fb5.png b/product_images/product_817680227fa540d88203e618fdc96fb5.png new file mode 100644 index 0000000..a3bada3 Binary files /dev/null and b/product_images/product_817680227fa540d88203e618fdc96fb5.png differ diff --git a/product_images/product_8b6bcfc5f734425bae5e87c0adde824d.png b/product_images/product_8b6bcfc5f734425bae5e87c0adde824d.png new file mode 100644 index 0000000..754bbbf Binary files /dev/null and b/product_images/product_8b6bcfc5f734425bae5e87c0adde824d.png differ diff --git a/product_images/product_9bd09d1625d64ab699ed2d88882deaca.png b/product_images/product_9bd09d1625d64ab699ed2d88882deaca.png new file mode 100644 index 0000000..9278420 Binary files /dev/null and b/product_images/product_9bd09d1625d64ab699ed2d88882deaca.png differ diff --git a/product_images/product_a116e9c7b38c4b2d89cdfd410f103f66.png b/product_images/product_a116e9c7b38c4b2d89cdfd410f103f66.png new file mode 100644 index 0000000..7ef9451 Binary files /dev/null and b/product_images/product_a116e9c7b38c4b2d89cdfd410f103f66.png differ diff --git a/product_images/product_a230732423d6436f9ef8e1ba5ffd192c.png b/product_images/product_a230732423d6436f9ef8e1ba5ffd192c.png new file mode 100644 index 0000000..a54cd4e Binary files /dev/null and b/product_images/product_a230732423d6436f9ef8e1ba5ffd192c.png differ diff --git a/product_images/product_a68081c6c2db4ecd9c4195b40202fb75.png b/product_images/product_a68081c6c2db4ecd9c4195b40202fb75.png new file mode 100644 index 0000000..58c06f4 Binary files /dev/null and b/product_images/product_a68081c6c2db4ecd9c4195b40202fb75.png differ diff --git a/product_images/product_ace9bfa4d520497086b7698eb2b7835a.png b/product_images/product_ace9bfa4d520497086b7698eb2b7835a.png new file mode 100644 index 0000000..df6a859 Binary files /dev/null and b/product_images/product_ace9bfa4d520497086b7698eb2b7835a.png differ diff --git a/product_images/product_b28aa2cfad4640b589e6fade6b70b016.png b/product_images/product_b28aa2cfad4640b589e6fade6b70b016.png new file mode 100644 index 0000000..59ae3e6 Binary files /dev/null and b/product_images/product_b28aa2cfad4640b589e6fade6b70b016.png differ diff --git a/product_images/product_b66f337655d147e38f0be07d0886b818.png b/product_images/product_b66f337655d147e38f0be07d0886b818.png new file mode 100644 index 0000000..78132c3 Binary files /dev/null and b/product_images/product_b66f337655d147e38f0be07d0886b818.png differ diff --git a/product_images/product_b7e01c5f527a4208a4989fb37f737164.png b/product_images/product_b7e01c5f527a4208a4989fb37f737164.png new file mode 100644 index 0000000..5561161 Binary files /dev/null and b/product_images/product_b7e01c5f527a4208a4989fb37f737164.png differ diff --git a/product_images/product_bbc49c72c06346ef8264985cee4ff82f.png b/product_images/product_bbc49c72c06346ef8264985cee4ff82f.png new file mode 100644 index 0000000..5b85d3f Binary files /dev/null and b/product_images/product_bbc49c72c06346ef8264985cee4ff82f.png differ diff --git a/product_images/product_c105edcb869d4bda9351f726f168170e.png b/product_images/product_c105edcb869d4bda9351f726f168170e.png new file mode 100644 index 0000000..22949df Binary files /dev/null and b/product_images/product_c105edcb869d4bda9351f726f168170e.png differ diff --git a/product_images/product_c65f057eb88140b896a276be67564138.png b/product_images/product_c65f057eb88140b896a276be67564138.png new file mode 100644 index 0000000..a6c0496 Binary files /dev/null and b/product_images/product_c65f057eb88140b896a276be67564138.png differ diff --git a/product_images/product_e86619975512415d94adc1b0dc0801cc.png b/product_images/product_e86619975512415d94adc1b0dc0801cc.png new file mode 100644 index 0000000..45356d1 Binary files /dev/null and b/product_images/product_e86619975512415d94adc1b0dc0801cc.png differ diff --git a/product_images/product_e882a0d30eb24e51b62d8fdb87667c21.png b/product_images/product_e882a0d30eb24e51b62d8fdb87667c21.png new file mode 100644 index 0000000..1bca0fc Binary files /dev/null and b/product_images/product_e882a0d30eb24e51b62d8fdb87667c21.png differ diff --git a/product_images/product_e989d2924e2b4c51aae4c335d76f1f86.png b/product_images/product_e989d2924e2b4c51aae4c335d76f1f86.png new file mode 100644 index 0000000..99dcbd7 Binary files /dev/null and b/product_images/product_e989d2924e2b4c51aae4c335d76f1f86.png differ diff --git a/product_images/product_eb87f3e4ebd143ca9eaa26d5dff44d59.png b/product_images/product_eb87f3e4ebd143ca9eaa26d5dff44d59.png new file mode 100644 index 0000000..361f61d Binary files /dev/null and b/product_images/product_eb87f3e4ebd143ca9eaa26d5dff44d59.png differ diff --git a/product_images/product_fa2f019446984122aa69d67766de89d7.png b/product_images/product_fa2f019446984122aa69d67766de89d7.png new file mode 100644 index 0000000..2a8a4dc Binary files /dev/null and b/product_images/product_fa2f019446984122aa69d67766de89d7.png differ diff --git a/product_images/product_fd615950f076467393acf6e538fc2bc6.png b/product_images/product_fd615950f076467393acf6e538fc2bc6.png new file mode 100644 index 0000000..12d6860 Binary files /dev/null and b/product_images/product_fd615950f076467393acf6e538fc2bc6.png differ diff --git a/qrtest.py b/qrtest.py new file mode 100644 index 0000000..fcc06bb --- /dev/null +++ b/qrtest.py @@ -0,0 +1,280 @@ +import sys +import os +import random +import asyncio +import uuid +from PySide6.QtWidgets import QApplication, QLabel, QMainWindow, QVBoxLayout, QWidget +from PySide6.QtGui import QPixmap +from playwright.async_api import async_playwright +import qasync + +# ============================================================ +# 1. PySide6 GUI: QR 이미지 표시 창 +# ============================================================ +class QRCodeWindow(QMainWindow): + def __init__(self, img_bytes): + super().__init__() + self.setWindowTitle("타오바오 QR 코드 로그인") + central_widget = QWidget() + self.setCentralWidget(central_widget) + layout = QVBoxLayout(central_widget) + + # QR 이미지를 표시할 QLabel + self.label = QLabel() + layout.addWidget(self.label) + + pixmap = QPixmap() + if not pixmap.loadFromData(img_bytes): + self.label.setText("이미지를 로드할 수 없습니다.") + else: + self.label.setPixmap(pixmap) + # 이미지 크기에 따라 창 크기 조절 + self.resize(pixmap.width(), pixmap.height()) + + +# ============================================================ +# 2. 로그인 페이지 접속 함수 (브라우저 실행 및 로그인 페이지 열기) +# ============================================================ +async def open_login_page(p): + """ + 제공해주신 브라우저 설정 코드를 사용하여 타오바오 로그인 페이지에 접속합니다. + 반환값: browser, page, login_url + """ + # 브라우저 경로 설정 + if getattr(sys, 'frozen', False): + browser_path = os.path.join(os.path.dirname(sys.executable), 'src', 'browsers', 'chromium-1112', 'chrome-win', 'chrome.exe') + else: + browser_path = os.path.join(os.path.dirname(__file__), 'src', 'browsers', 'chromium-1112', 'chrome-win', 'chrome.exe') + + # 사용자 에이전트 설정 + user_agent = random.choice([ + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.0.0", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:108.0) Gecko/20100101 Firefox/108.0", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 12_0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 OPR/85.0.0.0", + ]) + + browser = await p.chromium.launch( + headless=False, # 디버깅을 위해 브라우저 창을 표시 + executable_path=browser_path, + args=[ + '--disable-popup-blocking', + '--start-maximized', + '--window-size=1920,1080' + ] + ) + + # 시크릿 브라우저 컨텍스트 생성 + context = await browser.new_context( + user_agent=user_agent, + geolocation={"latitude": 37.5665, "longitude": 126.9780}, + locale="ko-KR", + permissions=["geolocation", "notifications"] + ) + + # 페이지 열기 + page = await context.new_page() + login_url = "https://login.taobao.com/member/login.jhtml" + await page.goto(login_url) + return browser, page, login_url + + +# ============================================================ +# 3. QR 스크린샷 캡쳐 함수 (QR 이미지를 리턴) +# ============================================================ +async def capture_qr_screenshot(page): + """ + 페이지에서 'div#qrcode-img canvas' 요소의 스크린샷을 찍어 이미지 바이트 데이터를 반환합니다. + """ + await page.wait_for_selector("div#qrcode-img canvas", timeout=10000) + await asyncio.sleep(2) # QR 코드가 완전히 렌더링될 때까지 딜레이 + qr_canvas = await page.query_selector("div#qrcode-img canvas") + if not qr_canvas: + print("QR 코드 캔버스를 찾을 수 없습니다.") + return None + img_bytes = await qr_canvas.screenshot() + return img_bytes + + +# ============================================================ +# 4. 로그인 완료 감시 및 검색 페이지 이동 함수 +# ============================================================ +async def monitor_login(page, login_url, search_url): + """ + 1초마다 현재 페이지 URL을 출력하다가 로그인 완료(로그인 페이지 URL과 달라짐)를 감지하면 + 지정한 검색 URL로 이동합니다. + """ + print("로그인 완료 감시 시작...") + while True: + current_url = page.url + print("현재 URL:", current_url) + if current_url != login_url: + print("로그인 완료 감지됨. 현재 URL:", current_url) + break + await asyncio.sleep(1) + + # 지정한 검색 페이지로 이동 + await page.goto(search_url) + print("검색 페이지로 이동했습니다:", search_url) + + # 상품 카드가 나타날 때까지 대기 + try: + await page.wait_for_selector(".doubleCard--gO3Bz6bu", timeout=15000) + print("상품 카드 요소가 나타났습니다.") + except Exception as e: + print("상품 카드 요소를 찾지 못했습니다.", e) + + +# ============================================================ +# 5. 상품정보 추출 함수 (현재 페이지의 모든 상품 카드 처리) +# ============================================================ +async def extract_products_from_current_page(page, card_or_img="card"): + """ + 현재 페이지의 모든 상품 카드(.doubleCard--gO3Bz6bu)를 순회하며, + 상품 이미지 URL, 상품명, 가격, 구매량 정보를 추출하고, + 각 상품 카드의 스크린샷을 찍어 로컬 파일에 저장한 후, 파일 경로를 함께 반환합니다. + """ + await page.wait_for_selector(".doubleCard--gO3Bz6bu", timeout=10000) + cards = await page.query_selector_all(".doubleCard--gO3Bz6bu") + products = [] + + # 로컬에 상품 이미지 저장 폴더 생성 + images_folder = "product_images" + os.makedirs(images_folder, exist_ok=True) + + for card in cards: + # 상품 이미지 URL 추출 + img_elem = await card.query_selector("img.mainPic--Ds3X7I8z") + img_src = await img_elem.get_attribute("src") if img_elem else None + + # 상품명 추출 + name_elem = await card.query_selector("div.title--qJ7Xg_90 span") + name_text = (await name_elem.inner_text()).strip() if name_elem else "" + + # 가격 추출 (정수 부분) + price_elem = await card.query_selector("span.priceInt--yqqZMJ5a") + price_text = (await price_elem.inner_text()).strip() if price_elem else "" + + # 구매량 추출 + sales_elem = await card.query_selector("span.realSales--XZJiepmt") + sales_text = (await sales_elem.inner_text()).strip() if sales_elem else "" + + # 각 상품 카드의 스크린샷을 찍어서 로컬 파일로 저장 + unique_filename = f"product_{uuid.uuid4().hex}.png" + local_path = os.path.join(images_folder, unique_filename) + try: + if card_or_img == "card": + await card.screenshot(path=local_path) + elif card_or_img == "img": + if img_elem: + await img_elem.screenshot(path=local_path) + else: + await card.screenshot(path=local_path) + print(f"상품 스크린샷 저장됨: {local_path}") + except Exception as e: + print("상품 스크린샷 저장 실패:", e) + local_path = None + + product = { + "image_url": img_src, + "local_image": local_path, + "name": name_text, + "price": price_text, + "sales": sales_text + } + products.append(product) + return products + + +# ============================================================ +# 6. 여러 페이지에서 상품정보 수집 함수 +# ============================================================ +async def collect_products(page, pages_to_collect): + """ + 사용자가 지정한 페이지 수만큼 각 페이지의 상품 정보를 추출합니다. + 각 페이지 당 최대 45개 상품이 있다고 가정하며, 페이지 이동 버튼을 클릭하여 이동합니다. + """ + all_products = [] + for i in range(pages_to_collect): + print(f"\n--- Page {i+1} 상품정보 추출 시작 ---") + products = await extract_products_from_current_page(page) + print(f"페이지 {i+1}에서 {len(products)}개의 상품 정보 추출됨.") + all_products.extend(products) + + # 마지막 페이지가 아니라면 다음 페이지로 이동 + if i < pages_to_collect - 1: + try: + # 페이지 이동 버튼 영역 대기 + await page.wait_for_selector("div.next-pagination-list", timeout=10000) + next_page = i + 2 # 현재 페이지가 i+1이면, 다음 페이지는 i+2 + # 페이지 이동 버튼 클릭 (버튼 내 텍스트가 페이지 번호와 일치) + btn_selector = f"button.next-pagination-item:has-text('{next_page}')" + await page.click(btn_selector) + print(f"페이지 {next_page}로 이동 버튼 클릭됨.") + # 페이지 이동 후 로드 대기 + await page.wait_for_load_state("networkidle") + await asyncio.sleep(2) + except Exception as e: + print(f"페이지 {next_page}로 이동 실패: {e}") + break + return all_products + + +# ============================================================ +# 7. 전체 동작을 통합하는 메인 함수 (qasync 사용) +# ============================================================ +async def main(pages_to_collect): + async with async_playwright() as p: + # 1) 로그인 페이지 접속 + browser, page, login_url = await open_login_page(p) + + # 2) QR 스크린샷 캡쳐 (GUI에 표시할 이미지) + img_bytes = await capture_qr_screenshot(page) + if img_bytes is None: + print("QR 코드 이미지를 가져오지 못했습니다.") + await browser.close() + return + + # 3) 검색 페이지 URL (로그인 완료 후 이동) + search_url = ("https://s.taobao.com/search?commend=all&ie=utf8page=1&" + "q=%E5%A5%B3%E5%A3%AB%E8%A5%BF%E6%9C%8D&search_type=item") + + # 4) GUI 창 생성 (QR 이미지 표시) + app = QApplication.instance() + if app is None: + app = QApplication(sys.argv) + window = QRCodeWindow(img_bytes) + window.show() + + # 5) 로그인 완료 감시 및 검색 페이지 이동 + await monitor_login(page, login_url, search_url) + + # 6) 사용자가 지정한 페이지 수만큼 상품 정보 수집 + products = await collect_products(page, pages_to_collect) + print(f"\n총 {len(products)}개의 상품 정보를 수집했습니다.") + for idx, prod in enumerate(products, start=1): + print(f"[{idx}] {prod}") + + # 7) 디버깅 후 브라우저 종료 및 GUI 종료 처리 + await browser.close() + await asyncio.sleep(2) + app.quit() + + +# ============================================================ +# 8. qasync를 통한 이벤트 루프 실행 및 사용자 입력 처리 +# ============================================================ +if __name__ == "__main__": + # 사용자에게 수집할 페이지 수 입력 받기 (기본값 1) + page_count_input = input("수집할 페이지 수를 입력하세요 (기본값 1): ") + try: + pages_to_collect = int(page_count_input) if page_count_input.strip() else 1 + except Exception: + pages_to_collect = 1 + + app = QApplication(sys.argv) + loop = qasync.QEventLoop(app) + asyncio.set_event_loop(loop) + with loop: + loop.run_until_complete(main(pages_to_collect)) diff --git a/sms.py b/sms.py new file mode 100644 index 0000000..0491fd8 --- /dev/null +++ b/sms.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +import argparse +import asyncio +import getpass +import textwrap +from playwright.async_api import async_playwright + +DEFAULT_DELAY = 1 +MESSAGES_URL = 'https://messages.google.com/web/conversations/new' + +parser = argparse.ArgumentParser( + prog='messages-for-web-playwright', + description='Playwright를 사용하여 Google Messages for Web에서 SMS 전송 자동화', + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=textwrap.dedent('''\ + 예시: + python script.py --to 010-1234-5678 "안녕하세요, 테스트 메시지입니다." + ''') +) +parser.add_argument('MESSAGE', type=str, help='전송할 문자 메시지 내용 (따옴표로 감싸서 입력)') +parser.add_argument('--to', type=str, required=True, help='받는 사람의 전화번호 (예: 010-1234-5678)') +parser.add_argument('-d', '--delay', type=int, default=DEFAULT_DELAY, help='동작 사이의 지연 시간 (초)') +parser.add_argument('--dry_run', action='store_true', help='실제 전송 없이 테스트 모드 실행') +args = parser.parse_args() + +async def main(): + async with async_playwright() as p: + # persistent context를 사용하여 로그인 세션을 재사용 (로그인 되어 있어야 함) + user_data_dir = f"C:\\Users\\{getpass.getuser()}\\AppData\\Local\\ms-playwright" + context = await p.chromium.launch_persistent_context(user_data_dir=user_data_dir, headless=False) + page = await context.new_page() + print("Playwright로 Google Messages for Web에 접속합니다.") + + # 1. Google Messages for Web 새 대화 페이지로 이동 + await page.goto(MESSAGES_URL) + print("Google Messages for Web 페이지로 이동합니다.") + await page.wait_for_timeout(args.delay * 1000) + print("Google Messages for Web에 접속했습니다.") + + # 2. "이름, 전화번호 또는 이메일 입력" input에 받는 사람 정보 입력 + recipient_input_selector = 'input[placeholder="이름, 전화번호 또는 이메일 입력"]' + await page.wait_for_selector(recipient_input_selector, timeout=45000) + print("받는 사람 정보 입력란을 찾았습니다.") + await page.fill(recipient_input_selector, args.to) + print(f"받는 사람 정보를 입력했습니다: {args.to}") + + # 3. 새 대화 요소 + new_conv_selector = "span:has-text('번으로 보내기')" + await page.wait_for_selector(new_conv_selector, timeout=45000) + print("새 대화 버튼을 찾았습니다.") + await page.click(new_conv_selector) + print("새 대화 버튼을 클릭했습니다.") + + # 4. "문자메시지" 입력란이 나타날 때까지 기다림 + message_input_selector = 'textarea[placeholder="문자메시지"]' + await page.wait_for_selector(message_input_selector, timeout=45000) + await page.fill(message_input_selector, args.MESSAGE) + await page.wait_for_timeout(args.delay * 1000) + + if args.dry_run: + print("Dry run: 실제 전송 없이 메시지 입력만 수행했습니다.") + else: + await page.click('mws-message-compose > div > mws-message-send-button > div > mw-message-send-button > button') + print("메시지가 전송되었습니다.") + + await page.wait_for_timeout(args.delay * 1000) + await context.close() + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/src/playwright_thread.py b/src/playwright_thread.py index d622f08..850f0d9 100644 --- a/src/playwright_thread.py +++ b/src/playwright_thread.py @@ -59,11 +59,15 @@ class PlaywrightThread(QThread): # 페이지 열기 page = await context.new_page() - await page.goto("https://world.taobao.com") + # await page.goto("https://world.taobao.com") + await page.goto("https://s.taobao.com/search?commend=all&ie=utf8page=1&q=%E5%A5%B3%E5%A3%AB%E8%A5%BF%E6%9C%8D&search_type=item") + self.logger.log(f"접속 완료.", level=logging.INFO) # 페이지 로딩 확인 및 pagedown - await page.wait_for_selector(".tb-pick-content-item") # 상품 카드 로딩 대기 + # await page.wait_for_selector(".tb-pick-content-item") # 상품 카드 로딩 대기 + await page.wait_for_selector("doubleCard--gO3Bz6bu") # 상품 카드 로딩 대기 + self.logger.log(f"페이지 로딩 완료", level=logging.INFO) await page.keyboard.press("PageDown") await page.wait_for_timeout(1000) # 1초 대기