반응형

 

<이전 ver 1.0 개발 포스팅>

https://cocoon1787.tistory.com/741

 

[Node.js] Node + Kakao API로 상품 재입고 알림 만들기 (ver 1.0)

🚀 이제 날이 슬슬 추워지기 시작하는 것 같고 패딩도 좀 필요해서 무신사에서 숏 패딩 하나 구매나 할까 하고 좀 둘러봤습니다. 괜찮은 상품이 있어서 살려했는데 아쉽게도 L, XL 사이즈 모두

cocoon1787.tistory.com

 

🛠️ 주요 업데이트 사항

  • 메서드 모듈화
  • 여러 상품 크롤링
  • 코드 리팩터링

 

 

📁 디렉터리 구조

 

 

🚀 Run Code

server.js

const crawl = require("./crawl.js");

crawl.restock(1145380, "L");
crawl.restock(1520534, "XL");
...

restock 함수의 첫 번째 파라미터는 itemNum, 두 번째 파라미터는 size

상품의 주소 goods/ 다음 값이 itemNum
사이즈는 해당 상품의 사이즈들

 

터미널에서 다음을 입력.

node server.js

 

 

📋 전체 코드

crawl.js

const scheduler = require("node-schedule");
const cheerio = require("cheerio");
const request = require("request");
const kakaoTalk = require("./kakaoTalk.js");

function restock(itemNum, size) {
  const schedule = scheduler.scheduleJob("*/2 * * * * *", function () {
    let url = "https://store.musinsa.com/app/goods/" + itemNum;
    request(url, function (error, response, html) {
      if (error) {
        throw error;
      }
      const $ = cheerio.load(html);
      const productTitle = $(".product_title em").text();
      const option = $(".option1 option");
      let map = new Map();
      console.log(productTitle);
      for (let i = 1; i < option.length; i++) {
        // console.log(option[i].attribs.value + " " + option[i].attribs.jaego_yn);
        map.set(option[i].attribs.value, option[i].attribs.jaego_yn);
      }

      console.log("사이즈: " + size);
      console.log("재고: " + map.get(size));

      if (map.get(size) === "Y") {
        const msg = productTitle + " " + size + " 재고있음";
        console.log(msg);
        kakaoTalk.sendMessage(msg);
        schedule.cancel();
      }
    });
  });
}

module.exports = {
  restock: restock,
};

kakaoTalk.js

const request = require("request");
const accessToken = "tFcr8OIN47i4J6dgN9JL9FrHHx_6a52OaY4jfAo9dJkAAAF_xjVFJg";
const headers = {
  "Content-Type": "application/x-www-form-urlencoded",
  Authorization: "Bearer " + accessToken,
};

function sendMessage(msg) {
  const dataString = `template_object={
    "object_type": "text",
    "text": "${msg}",
    "link": {
        "web_url": "https://developers.kakao.com",
        "mobile_web_url": "https://developers.kakao.com"
    },
    "button_title": "바로 확인"
}`;
  const options = {
    url: "https://kapi.kakao.com/v2/api/talk/memo/default/send",
    method: "POST",
    headers: headers,
    body: dataString,
  };
  request(options, (error, response, body) => {
    console.log(response.statusCode);
    if (!error && response.statusCode == 200) {
      console.log("메시지 전송 완료.");
    } else {
      console.log(error);
    }
  });
}

module.exports = {
  sendMessage: sendMessage,
};

server.js

const crawl = require("./crawl.js");

crawl.restock(1145380, "L");
crawl.restock(1520534, "LL");

 

 

🟩 Node.js 크롤링

crawl.js 

const scheduler = require("node-schedule");
const cheerio = require("cheerio");
const request = require("request");
const kakaoTalk = require("./kakaoTalk.js");

function restock(itemNum, size) {
  const schedule = scheduler.scheduleJob("*/2 * * * * *", function () {
    let url = "https://store.musinsa.com/app/goods/" + itemNum;
    request(url, function (error, response, html) {
      if (error) {
        throw error;
      }
      const $ = cheerio.load(html);
      const productTitle = $(".product_title em").text();
      const option = $(".option1 option");
      let map = new Map();
      console.log(productTitle);
      for (let i = 1; i < option.length; i++) {
        // console.log(option[i].attribs.value + " " + option[i].attribs.jaego_yn);
        map.set(option[i].attribs.value, option[i].attribs.jaego_yn);
      }

      console.log("사이즈: " + size);
      console.log("재고: " + map.get(size));

      if (map.get(size) === "Y") {
        const msg = productTitle + " " + size + " 재고있음";
        console.log(msg);
        kakaoTalk.sendMessage(msg);
        schedule.cancel();
      }
    });
  });
}

module.exports = {
  restock: restock,
};

 

상품명 출력

const $ = cheerio.load(html);  
const productTitle = $(".product_title em").text();

 

옵션 값들 map으로 저장(key : 사이즈, value : 재고 존재 여부)

const $ = cheerio.load(html);
const productTitle = $(".product_title em").text();
const option = $(".option1 option");
let map = new Map();
console.log(productTitle);
for (let i = 1; i < option.length; i++) {
	// console.log(option[i].attribs.value + " " + option[i].attribs.jaego_yn);
	map.set(option[i].attribs.value, option[i].attribs.jaego_yn);
}

 

해당 사이즈의 재고가 있을 경우 카카오톡 메시지 보내기

if (map.get(size) === "Y") {
  const msg = productTitle + " " + size + " 재고있음";
  console.log(msg);
  kakaoTalk.sendMessage(msg);
  schedule.cancel();
}

 

 

✉️ 카카오톡 메시지 보내기

kakaoTalk.js

const request = require("request");
const accessToken = "tFcr8OIN47i4J6dgN9JL9FrHHx_6a52OaY4jfAo9dJkAAAF_xjVFJg";
const headers = {
  "Content-Type": "application/x-www-form-urlencoded",
  Authorization: "Bearer " + accessToken,
};

function sendMessage(msg) {
  const dataString = `template_object={
    "object_type": "text",
    "text": "${msg}",
    "link": {
        "web_url": "https://developers.kakao.com",
        "mobile_web_url": "https://developers.kakao.com"
    },
    "button_title": "바로 확인"
}`;
  const options = {
    url: "https://kapi.kakao.com/v2/api/talk/memo/default/send",
    method: "POST",
    headers: headers,
    body: dataString,
  };
  request(options, (error, response, body) => {
    console.log(response.statusCode);
    if (!error && response.statusCode == 200) {
      console.log("메시지 전송 완료.");
    } else {
      console.log(error);
    }
  });
}

module.exports = {
  sendMessage: sendMessage,
};

 

 

⭐️ 카카오 API 액세스 토큰 발급

kakao developers에서 애플리케이션 생성

https://developers.kakao.com/console/app

 

카카오계정 로그인

여기를 눌러 링크를 확인하세요.

accounts.kakao.com

애플리케이션을 추가하고 추가한 애플리케이션을 클릭합니다.

 

애플리케이션을 생성했으니 이제 아래의 URL로 들어가 봅시다.

https://developers.kakao.com/tool/rest-api/open/post/v2-api-talk-memo-default-send

 

카카오계정 로그인

여기를 눌러 링크를 확인하세요.

accounts.kakao.com

 

요청 - 인증 앱 부분이 developers-sample로 되어있습니다. 오른쪽에 목록 아이콘을 클릭하여 아까 만들어준 애플리케이션으로 변경시켜 줍니다.

 

그리고 토큰 발급을 받아보려고 하니....

지금 사용해야 할 기능은 talk_message이지만 활성화할 수 없는 상태입니다. talk_message기능을 활성화해주러 가봅시다.

 

최상단의 "내 애플리케이션"으로 들어가 생성한 애플리케이션을 클릭해주시고 

제품 설정 - 카카오 로그인으로 들어가 주세요.

카카오 로그인을 활성화시켜줍니다.

 

그리고 이번엔 제품 설정 - 카카오 로그인 - 동의 항목으로 들어가 주세요.


맨 아래쪽 접근권한 - 카카오톡 메시지 전송 - 설정

선택 동의로 체크해줍니다.

https://devtalk.kakao.com/t/topic/60237 (선택 동의, 이용 중 동의 차이점)

 

동의를 하였으니 이제 아까 페이지로 돌아가서 토큰을 발급받아 봅시다.

https://developers.kakao.com/tool/rest-api/open/post/v2-api-talk-memo-default-send

 

카카오계정 로그인

여기를 눌러 링크를 확인하세요.

accounts.kakao.com

talk_message 항목이 활성화되었습니다.

선택항목을 체크하고 "동의하고 계속하기"를 눌러주면 토큰 발급됩니다.

발급받으신 토큰을 kakaoTalk.js의 accessToken이라는 변수에 붙여 넣어 주세요.

 

 

💻 테스트

server.js

const crawl = require("./crawl.js");

crawl.restock(1145380, "L");
crawl.restock(1520534, "LL");

<상황>

itemNum : 1145380 ->  플리스 집업 자켓 블랙 / L 사이즈 / 재고 있음

itemNum : 1520534 - > 304-ALT 드라이 무지 긴팔티 블랙 / LL 사이즈 / 재고 없음

 

scheduler 모듈을 통해 2초마다 크롤링하도록 하였고 크롤링 후 재고가 있을 시에 카카오톡으로 메시지를 보내고 scheduler를 종료하도록 하였습니다.

위의 콘솔 창에서 1145380번 상품의 L사이즈 재고를 확인하고 스케쥴러를 종료하여 1520534번 상품만 크롤링되고 있는 것을 확인할 수 있습니다.

물론 카카오톡 나와의 채팅에서는 "플리스 집업 자켓 블랙 L 재고 있음"이라고 메시지가 하나만 와있게 됩니다.

 

 

🐞 Bug

const crawl = require("./crawl.js");

crawl.restock(9999999, "L");
crawl.restock(1520534, "asdasd");

존재하지 않는 itemNum이거나 사이즈 명이 이상할 경우 위와 같이 출력될 수 있으니 꼭 해당 상품의 itemNum과 사이즈명을 확인하시고 크롤링하시길 바랍니다.

 

 

https://github.com/K-Junyyy/MUSINSA-CRWALING

 

GitHub - K-Junyyy/MUSINSA-CRWALING

Contribute to K-Junyyy/MUSINSA-CRWALING development by creating an account on GitHub.

github.com

 

 

반응형

+ Recent posts