반응형

참고자료

 

https://jung-story.tistory.com/102

 

Node.js (Node.js 쿠팡 크롤링해서 Json 파일 만들기) & (React 로 연결)

개요 이번에는 쿠팡의 상품정보들을 이미지와 제목 가격 링크들을 크롤링해 온후 그 정보를 통해 Json 파일을 만들어 보도록 하겠습니다 지난번에는 Switch case 구문으로 원하는 정보를 뽑아 왔지

jung-story.tistory.com

 


 

개요

 

지난번에는 Json 파일을 읽어와 테이블에 넣어보는 방법에 대해서 알아보았습니다.

이번에는 그 테이블의 내용 중 가격을 기준으로 내림차순과 오름차순 정렬을 하는 방법을 알아보도록 하겠습니다.

 


설명

 

 

우선 constructor에 다음과 같이 적어주시면 됩니다.

여기서 저희가 읽어온 Json파일의 데이터는 Product_Data에 저장이 되어있습니다.

 


Product.Json

 

 

데이터 형식은 다음과 같습니다.

 


함수작성

 

이제 construct에서 정의한 함수를 작성해보도록 하겠습니다.

 

 

 

가격순으로 정렬하기 위해 다음과 같이 사용되었습니다.

 

이렇게 하게 된다면 버튼을 클릭할때 Product.json에 들어있는 Price를 기준으로 정렬이 가능하게 됩니다.

 


결과

 

 

 

내림차순

 

 

오름차순

 

와 같이 잘 작동하는 것을 볼 수 있습니다.

 


Server에서 받아온 Json파일을 이용하여 출력하는 Product.js

 

import React, { Component } from 'react';
import { withStyles, makeStyles } from "@material-ui/core/styles";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableContainer from "@material-ui/core/TableContainer";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import Paper from "@material-ui/core/Paper";
import Product_option from './Product_option'
import Button from '@material-ui/core/Button';


const StyledTableCell = withStyles((theme) => ({
    head: {
      backgroundColor: theme.palette.common.black,
      color: theme.palette.common.white,
    },
    body: {
      fontSize: 16,
    },
  }))(TableCell);

  const StyledTableContainer2 = withStyles((theme)=> ({
    root: {
      backgroundColor: "rgba( 255, 255, 255,0.8)",
      //opacity:'80%',
      width:'auto',
      marginLeft:'5%',
      marginRight:'5%',
      height:'500px',
    }
  }))(TableContainer)

  const StyledTableContainer1 = withStyles((theme)=> ({
    root: {
      backgroundColor: "rgba( 255, 255, 255,0.8)",
      //opacity:'80%',
      width:'auto',
      marginLeft:'5%',
      marginRight:'5%',
      height:'auto',
    }
  }))(TableContainer)
  

class Product extends Component {
    constructor(props) {
        super(props);
        this.state = {
            Product_Data: [],
        };
        this.compareBy_ASC.bind(this);
        this.sortBy_ASC.bind(this);
        this.compareBy_DESC.bind(this);
        this.sortBy_DESC.bind(this);
      
      }
      
      componentWillMount() {
        this.callApi().then((res) => {
          this.setState({
            Product_Data: res,
          });
        });
      }
    
      callApi = async () => {
        const response = await fetch("/api/product");
        const body = await response.json();
        return body;
      };

      compareBy_ASC(key) {
        return function (a, b) {
          var x = parseInt(a[key]);
          var y = parseInt(b[key]);
          
          if (x < y) return -1;
          if (x > y) return 1;
          return 0;
        };
      }
     
      sortBy_ASC(key) {
        let arrayCopy = [...this.state.Product_Data];
        arrayCopy.sort(this.compareBy_ASC(key));
        this.setState({Product_Data : arrayCopy});
      }

      compareBy_DESC(key) {
        return function (a, b) {
          var x = parseInt(a[key]);
          var y = parseInt(b[key]);
          
          if (x > y) return -1;
          if (x < y) return 1;
          return 0;
        };
      }
     
      sortBy_DESC(key) {
        let arrayCopy = [...this.state.Product_Data];
        arrayCopy.sort(this.compareBy_DESC(key));
        this.setState({Product_Data : arrayCopy});
      }
      
      render() {
        const { Product_Data } = this.state; 
        return (
            
            <div>
                <br></br>
                <br></br><br></br>
                <h2 style={{ color: "white", textAlignLast: "center" }}>
                ※ 면역력 증가 상품 (쿠팡 , 휴럼샾 , 이스더포뮬라)
              </h2>
              <br></br>
              <div>
              <StyledTableContainer1 component={Paper}>
                <Table>
                    <TableHead>
                    <TableRow>
                        <StyledTableCell align="center" width="20%">상품</StyledTableCell>
                        <StyledTableCell align="center" width="10%">상표</StyledTableCell>
                        <StyledTableCell align="center" width="30%">제목</StyledTableCell>
                        <StyledTableCell align="center" width="26%">가격
                        <Button color="secondary"onClick={()=>this.sortBy_ASC('price')}>▲
                          </Button>
                          <Button color="secondary"onClick={()=>this.sortBy_DESC('price')}>▼
                          </Button></StyledTableCell>
                        <StyledTableCell align="center" width="23%">구매</StyledTableCell>
                    </TableRow>
                    </TableHead>
                </Table>
                </StyledTableContainer1>
                
              </div>
                <StyledTableContainer2 component={Paper}>
                <Table>
                    
                    <TableBody>
                        
                        {Product_Data.map(post=> {
                            return <Product_option src ={post.src} brand={post.brand} title={post.title} price={post.price} a={post.a}/>
                        })
                    }
                    
                    </TableBody>
                </Table>
                </StyledTableContainer2>
                
            </div>
           
        );
    }
}

export default Product

 


Product_option.js 

 

import React from 'react'
import TableRow from '@material-ui/core/TableRow'
import TableCell from '@material-ui/core/TableCell'

class Product_option extends React.Component {
    render() {
        return (
            <TableRow>
                <TableCell width="20%" align="center"><img src={this.props.src} width="80px" height="80px"/></TableCell>
                <TableCell width="10%" align="center"><img src={this.props.brand} width="40px" height="15px"></img></TableCell>
                <TableCell width="30%" align="center">{this.props.title}</TableCell>
                <TableCell width="25%" align="center"> <h2>{this.props.price}</h2></TableCell>
                <TableCell ></TableCell>
                <TableCell width="10%" align="center"><a href={this.props.a}>구매</a></TableCell>
            </TableRow>
        )
    }
}

export default Product_option;

 


크롤링해오는 파일인 crawlProduct.js

 

const axios = require("axios");
const cheerio = require("cheerio");
const fs = require("fs");

let html = "";
let html2 = "";
let html3 = "";


async function getHtml1() {
  try {
    return await axios.get("https://www.coupang.com/np/categories/306749?channel=plp_C2");
  } catch (error) {
    console.error(error);
  }
}

async function getHtml2() {
  try {
    return await axios.get("http://www.hurumshop.com/goods/goods_list.php?cateCd=038006");
  } catch (error) {
    console.error(error);
  }
}

async function getHtml3() {
  try {
    return await axios.get("https://www.esthermall.co.kr/front/product_list.php?ct_id=001001016");
  } catch (error) {
    console.error(error);
  }
}

async function getProduct1() {
  if (!html) {
    html = await getHtml1();
  }

  const dataArr = [];
  const dataPath = "./product.json";
  const $ = cheerio.load(html.data);

  $(".newcx-container .newcx-body .newcx-main .newcx-list ul li")
  .each(async function (index, item) {
    var src ='http:'+ $(item).find("dl").find("img").attr("src");
    var title = $(item).find("dl").find("img").attr("alt");
    var price = $(item).find("dl").find("dd").find(".sale")
    .find(".price-value").first().text();
    var a ='https://www.coupang.com/'+$(item).find("a").attr("href");
      var data = {
        src: src,
        brand:'http://image7.coupangcdn.com/image/coupang/common/logo_coupang_w350.png',
        title: title,
        price: price,
        a:a
      };
      if (index > 7) {
        dataArr.push(data);
      
        console.log("product on json file");
      } 
    }
  );

  if (!html2) {
      html2 = await getHtml2();
    }
    
  const $2 = cheerio.load(html2.data);
  $2("#contents .goods_list_item .goods_list .goods_list_cont ul li")
  .each(async function (index, item) {
    var src = 'http://www.hurumshop.com'+$(item).find("img").attr("src");
    var title = $(item).find("img").attr("alt");
    var price = $(item).find(".item_money_box").find("span").first()
    .text().trim();
    var a = $(item).find("a").attr("href");
      var data = {
        src: src,
        brand:'http://www.hurumshop.com/data/skin/front/moment_wib_C/img/banner/63a4166288d354972728984236833236_84861.png',
        title: title,
        price: price.replace("원",""),
        a:a.replace("..","http://www.hurumshop.com")
      };
      
      if (index >= 0) {
        dataArr.push(data);
        
        console.log("Product2222");
    
        
      } 
    }
  );

  if(!html3) {
    html3 = await getHtml3();
  }
  const $3 = cheerio.load(html3.data);
  $3("#fullContent #content .productList ul li")
  .each(async function (index, item) {
    var src = 'https://www.esthermall.co.kr'+ $(item).find(".imgArea").find("img").attr("src");
    var title = $(item).find(".tit").text();
    var price = $(item).find(".price").text().substring(0,7);
    
    var a = 'https://www.esthermall.co.kr'+$(item).find(".hideBox").find("a").eq(1).attr("href");  
    var data = {
        src: src,
        brand:'https://www.esthermall.co.kr/skin/default/images/img/img_h1_logo_renew.gif',
        title: title,
        price: price.replace('원',""),
        a:a
      };
      
      if (index >= 0) {
        dataArr.push(data);
        fs.writeFileSync(dataPath, JSON.stringify(dataArr));
        console.log("Product333");
     
        
      } 
    }
  );
 
  fs.writeFileSync(dataPath, JSON.stringify(dataArr));

}




module.exports = {  getProduct1};

 


만들어진 Json 파일을 서버로 전송시키는 server.js

 

const express = require("express");
const { getProduct1} = require("./crawlProduct.js");
const fs = require("fs");

const bodyParser = require("body-parser");
const app = express();
const port = process.env.PORT || 5000;
const cron = require("node-cron");

const ProudctJSON = fs.readFileSync("./product.json");

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

async function getProductAsync() {
  const Product_data = await getProduct1();
}

cron.schedule("*/1 * * * *", async () => {
  console.log("running a task every two minutes");
  await getProductAsync();
});




app.get("/api/product", async (req, res) => {
  res.send(ProudctJSON);
});





app.listen(port, () => console.log(`Listening on port ${port}`));

 

반응형

+ Recent posts