ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Alo 재입고 알림봇 개발_1
    Dev🚀 2025. 9. 13. 15:20

    🌱 개발 배경

    - 평소에 사고 싶었던 상품인 Alo Yoga 'Seamless Delight High Neck Bra – White Heather / L'의 수시 재입고 타이밍을 놓치기 쉬워서, 자동 알림이 필요했다..🥹

     

    - Alo는 Shopify 기반이라(공식 사이트가 Shopify Plus), 각 상품에 대해 변형(variant) 재고 여부를 공개 JSON으로 확인할 수 있음.(상품 상세 URL이 https://www.aloyoga.com/products/상품핸들?variant=숫자 형태 → 이건 Shopify 기본 패턴)

    - 즉, L 사이즈(variant id: 43774160568500)가 available인지 바로 체크→ 텔레그램으로 알림 보내기 흐름이 깔끔해진다.

     

    - /products/상품핸들.js로 요청을 보내면 상품 데이터를 JSON으로 내려주는 것도 Shopify 특징 중 하나.
    👉 Alo 사이트가 Shopify라는 걸 확인했기 때문에 재고 크롤링 접근을 Shopify 방식으로 설계할 수 있었음.

     

    • 체크 방식 1: Shopify의 공개 JSON(/products/<handle>.js)에서 해당 variant의 available 플래그 확인
    • 체크 방식 2(백업): 상품 페이지 HTML에서 “Out Of Stock!” 등 UI 신호로 보조 판별 (테마 변경 시 대비)
    • 상태 변화(품절→구매가능 / 구매가능→품절) 시 텔레그램 알림 발송

     

    🌟 판별 전략

    1. Shopify 공개 상품 JSON

    • Shopify JSON 응답에는 보통 이런 정보가 포함됨:
    {
      "id": 43774160568500,
      "title": "White Heather / L",
      "available": false
    }

     

    • 여기서 id = Variant ID, available = true/false → 이 값으로 바로 재고 상태를 확인할 수 있음.
    • 하지만 JSON 요청이 차단(403)될 때가 있어서, 백업 방법으로 HTML을 파싱하는 fallback 로직도 추가:
      • “품절”, “Sold Out” 같은 텍스트 탐색
      • "Add to Cart" 버튼이 disabled인지 확인
      • meta 태그 og:availability 값 (instock/oos) 확인

     

    2. HTML 보조 판별 (fallback)

    API 요청이 차단될 수 있으므로 BeautifulSoup를 활용해 HTML을 파싱하는 보조 로직도 구현했다. BeautifulSoup는 파이썬의 HTML 파싱 라이브러리로, DOM 트리를 탐색해 "품절" 텍스트나 버튼 상태 등을 쉽게 확인할 수 있다.

    • 403 등으로 JSON이 막히면 페이지 HTML을 파싱해서 판별:
    • script[type="application/ld+json"]의 offers.availability → InStock/OutOfStock
    • 본문 텍스트에 Out of stock, Sold out, 품절, 재고 없음 등 키워드
    • “Add to Bag/Add to Cart/장바구니 담기” 버튼의 disabled 여부

    ✔️ 코드 스니펫 (핵심 BeautifulSoup 로직)

    from bs4 import BeautifulSoup
    import json, requests
    
    html = requests.get(product_url, headers=HEADERS, timeout=20).text
    soup = BeautifulSoup(html, "html.parser")
    text = soup.get_text(" ").lower()
    
    # 1) ld+json → availability
    for tag in soup.find_all("script", type="application/ld+json"):
        try:
            data = json.loads(tag.string or "{}")
        except Exception:
            continue
        arr = data if isinstance(data, list) else [data]
        for d in arr:
            if isinstance(d, dict):
                offers = d.get("offers")
                if isinstance(offers, dict):
                    avail = str(offers.get("availability", "")).lower()
                    if "instock" in avail:  # InStock
                        available = True
                    if "outofstock" in avail:  # OutOfStock
                        available = False
    
    # 2) 텍스트 신호
    if any(sig in text for sig in ["out of stock", "sold out", "품절", "재고 없음"]):
        available = False

    📍시행착오

    • 403 Forbidden: GitHub Actions IP 대역이 막히는 경우가 많다 → 브라우저 User-Agent/Referer 헤더, 재시도(백오프), HTML fallback, 마지막으로 텍스트 프록시(r.jina.ai) 로 우회.
    • 경로 인식 실수: Actions는 .github/workflows/*.yml 경로에 있어야 탭에 뜬다.
    • 초기 알림: 최초 실행 때는 이전 상태가 없으므로 baseline 알림(현재 상태)이 1회 발송된다. 이후에는 상태 변화 시에만 알림.
    반응형
Designed by Tistory.