[react]Mobby App#2-Naver API+スクロールによる検索機能とリアルタイムランキング


👉 [react]アプリケーション#1-NAVER API+スクロールによる検索機能とリアルタイムランキング不要

3.映画ランキング実施


映画振興委員会APIを利用すれば簡単に興行データを得ることができるが、NAVER映画のホームページをスクロールすることで実現する.

3-1. Cheerio設定


スクロール操作ではaxiosとcherioが使用されます.axiosはすでにインストールされており、cherioのみがインストールされています.npm install cheerio --save
  • axios:WebページのHTMLを取得
  • cacheio:インポートしたHTMLから必要な情報(パケット)を抽出
  • 3-2. HTMLのインポート


    サーバフォルダの下でキャプチャします.jsファイルを生成します.
    cherioとaxiosモジュールをインポートし、HTMLをインポートする関数を記述します.async文とtry-catch文が使用されます.
    (fetching.js)
    // 설치한 axios와 cheerio 모듈을 가져온다.
    const cheerio = require("cheerio");
    const axios = require("axios");
    
    // axios로 HTML을 가져오는 함수
    const getHTML = async () => {
      try {
        return await axios.get('https://movie.naver.com/movie/sdb/rank/rmovie.naver')
      } catch (error) {
        console.log(error);
      }
    }
    
    NAVER映画ランキングページのHTML全体からコードを作成しました.
    今から抽出を始めましょう.解析関数を生成し、関数からaxiosに抽出したHTMLを変数に入れます.
    (fetching.js)
    // 설치한 axios와 cheerio 모듈을 가져온다.
    const cheerio = require("cheerio");
    const axios = require("axios");
    
    // axios로 HTML을 가져오는 함수
    const getHTML = async () => {
      try {
        return await axios.get('https://movie.naver.com/movie/sdb/rank/rmovie.naver')
      } catch (error) {
        console.log(error);
      }
    }
    
    // 파싱
    const parsing = async () => {
      // 위에서 추출한 HTML 전체 가져오기
      const html = await getHTML();
    
      console.log(html)
    }
    
    parsing()
    
    ルートパスからnode server/fetching.jsコマンドを入力します.axiosで吹き飛ばされた大量のデータが端末に出力される.

    3-3. データ抽出


    Cheerioによって低データから映画のタイトルとリンクを抽出し、1位から10位まで.CheerioはJQuery構文を使用します.
    (fetching.js)
    const cheerio = require("cheerio");
    const axios = require("axios");
    
    // HTML 가져오기
    const getHTML = async () => {
      ...
    }
    
    // 파싱
    const parsing = async () => {
      // 위에서 추출한 HTML 전체 가져오기
      const html = await getHTML();
      // JQuery처럼 사용하기 위해 '$'에 cheerio를 로드한다.
      const $ = cheerio.load(html.data);
      
      // 개발자 모드에서 확인해보면
      // .list_ranking 아래 tr들이 있고 그 안에 하나씩 타이틀 존재
      // 반복문을 돌릴 수 있어야 하니 병렬로 있는 요소까지만 찾는다.
      const $trs = $("#old_content > .list_ranking > tbody > tr");
     
      // 파싱한 데이터를 담을 배열
      let dataArr = [];
    
      // 찾은 tr 개수 만큼 반복문을 돌린다.
      $trs.each((idx, node) => {
        const title = $(node).find(".tit3 a").text();
        const link = $(node).find(".tit3 a").attr("href");
    
        // 빈 값 리턴
        if (title === "") {
          return;
        }
    
        // 오브젝트 형식으로 배열에 담기
        dataArr.push({
          title: title,
          link: link
        });
      });
    
      console.log(dataArr)
    }
    
    parsing()
    サーバーを閉じて再オープンします.下図のように出力が成功しました.

    3-4. fetching.jsモジュール化


    今から捕まえるjsをserver/indexにモジュール化します.jsに変えましょう
    (fetching.js)
    const cheerio = require("cheerio");
    const axios = require("axios");
    
    // HTML 가져오기
    const getHTML = async () => {
      ...
    };
    
    // 파싱
    const parsing = async () => {
      ...
    };
    
    // parsing 함수를 모듈화해서 내보내기
    module.exports = parsing;
    
    (server/index.js)
    const express = require("express");
    const app = express();
    const port = process.env.PORT || 5000;
    const parsing = require('./fetching.js');
    ...
    // parsing 모듈 import
    const parsing = require('./fetching.js');
    
    ...
    
    // api/rank로 get 요청이 들어오면 
    // parsing() 실행해서 요청 결괏값 client로 내보내기
    app.get('/api/rank', (req, res) => {
      parsing().then(response => res.send(response))
    })
    
    ...
    
    app.listen(port, () => {
      console.log(`Example app listening on port ${port}`);
    });
    
    

    3-5. クライアントへのエクスポート


    このように獲得したランキングタイトルをclientで獲得できるように設定しましょう.
    URLが/apiで始まるリクエストは5000個のポートから発行され、サーバが実行するエージェント操作はそれまでに完了していたのでスキップした.
    (setupProxy.js)
    const { createProxyMiddleware } = require('http-proxy-middleware');
    
    module.exports = function(app) {
      app.use (
        createProxyMiddleware( '/api', {
          target: 'http://localhost:5000',
          changeOrigin: true
        })
      )
    }
    
    (ranking.tsx)
    import axios from 'axios'
    import React, { useEffect, useState } from 'react'
    
    const Ranking = () => {
      // 랭킹 데이터를 담을 State
      const [Data, setData] = useState([])
    
      // 랭킹 데이터 가져오는 함수
      const fetchRank = async () => {
        try {
          const { data } = await axios.get('/api/rank')
          // state에 담기
          setData(data)
        } catch (error) {
          let message = 'Unknown Error'
          if (error instanceof Error) message = error.message
          console.log(message);
        }
      }
    
      // 마운트 시 데이터 가져오는 함수 실행
      useEffect(() => {
        fetchRank()
      }, [])
    
      return (
        <div className="ranking">
          <div className="ranking__inner">
            <h2>영화 랭킹</h2>
            <div className="ranking__list">
              <div className="ranking__list__inner">
                <ul>
                  {
                    Data &&
                      Data.map((data: { title: string, link: string }, idx) => (
                      <li key={ idx } >
                        <span className="num">{ idx+1 }</span>
                        <a href={ `https://movie.naver.com/${data.link}` } target="_blank">
                          <span className="title">{ data.title }</span>
                        </a>
                      </li>
                    ))
                  }
                </ul>
              </div>
            </div>
          </div>
        </div>
      )
    }
    
    export default Ranking

    4.終了


    CSS


    スプーン1杯CSSを入れて完成です.

    News Picker


    サムネイルのようにランキング領域をリアルタイム検索語フォーマット(News Picker)として実装する場合は、次のコードを参照してください.




    Lazy-loading


    Lazy-loadingを設定して

    コンポーネントの不要なレンダリングを低減します.router v 6からLazy-loadingを適用する方法は、次のページを参照してください.


    📎 [React] Router v6 - Lazy-loading




    完了したページの表示


    📎 Demo








    配置


    暴力団への配布方法は以下のリンクにまとめられており,参考になる.p>

    📎 MERNスタックアプリケーションの配備Heroku