node.js+Express+pugでDBDのマップカウンターを作る


はじめに

DBDという対人ゲームで、あるキャラクターを使うと苦手なマップばかり当たるので、猜疑心からマップカウンターを作りました。(なお結果としては私が苦手なマップがDBDに多いだけでした!)

目次

  1. node.js+Express+pugインストール
  2. DBDのマップをスクリピング
  3. JSONの読み書き
  4. 動的リンクを扱う
  5. .pugへ変数を渡す
  6. .pugでhtmlを生成
  7. cssの記述

1.node.js+Express+pugインストール

ここを見ながらインストール

2.DBDのマップをスクレイピング

稚拙なソースですが動きます!

dbdmap.py
from time import sleep
import json

def main():
    import requests
    from bs4 import BeautifulSoup

    FullURL = "https://deadbydaylight.fandom.com"
    URL = "https://deadbydaylight.fandom.com/wiki/Dead_by_Daylight_Wiki"
    page = requests.get(URL)

    soup = BeautifulSoup(page.content, "html.parser")

    results = soup.find(id="fpRealms")
    results = results.find("div", {"class": "fplinks"})
    results = results.find_all("a")

    maplst = []

    mapdict = {}

    for result in results:
        dbdmap = result["href"]
        if not dbdmap in maplst:
            maplst.append(dbdmap)

    for mapdbd in maplst:
        mapurl = FullURL + mapdbd
        mappage = requests.get(mapurl)
        mapsoup = BeautifulSoup(mappage.content, "html.parser")

        mapresults = mapsoup.find("table", {"class": "wikitable"})
        mapresults = mapresults.find("tbody")
        mapresults = mapresults.find("tr")
        mapresults = mapresults.find_all("td")

        for mapresult in mapresults:
            mapresult = mapresult.find("center")
            mapresult = mapresult.find("a")
            mapdict[mapresult.text.strip()] = 0

        sleep(3)

    with open('<保存したい場所>.json', 'w') as outfile:
        json.dump(mapdict, outfile)

if __name__ == "__main__":
    main()

これにより以下の様なJSONを得られる。

{"Coal Tower": 0, "Groaning Storehouse": 0, "Ironworks of Misery": 0, ...

3.JSONの読み書き

ここで詰まったのでいくつか注意点を書いておきます

jsonはクライアント側からは読み書きできません。

つまり生成されたウェブページに組み込まれたスクリプトからは操作できないです。

そのためサーバー側で操作する必要があります。

また、サーバー側での操作はroutes/index.jsonで行います。(リクエストされたページに対応したファイル)

const fs = require('fs');
const filePath = "./routes/dbdmap.json"

//jsonを読み取る
const dbdmaplst = require("./dbdmap.json");

//jsonに書き込む
fs.writeFile(filePath, JSON.stringify(dbdmaplst), (err) => {
  if (err) console.log('Error writing file:', err)
})

4.動的リンクを扱う

カウンターなので、index/"map名"/"up/down"のような動的リンクを扱えるようにします。

複数個設定できます。

router.get('/', function(req, res, next) {

router.get('/:map/:vote', function(req, res, next) {

受け取ったリンクのキーワードを引数のように扱えます

router.get('/:map/:vote', function(req, res, next) {
  const dbdmaplst = require("./dbdmap.json");
  const vote = req.params.vote;
  if (vote == "up") {
    dbdmaplst[req.params.map] += 1;
  }
  else if (vote == "down") {
    if (dbdmaplst[req.params.map] > 0) {
      dbdmaplst[req.params.map] -= 1;
    }
  }

5.pugへ変数を渡す

json形式で変数を渡します。

res.render('index', { title: 'Epress', dbdmap: dbdmaplst});

以上index.jsの完全なソースはこちら

index.js
var express = require('express');
var router = express.Router();

//read json
const fs = require('fs');
const filePath = "./routes/dbdmap.json"

/* GET home page. */
router.get('/', function(req, res, next) {
  const dbdmaplst = require("./dbdmap.json");
  res.render('index', { title: 'Epress', dbdmap: dbdmaplst});
});

router.get('/:map/:vote', function(req, res, next) {
  const dbdmaplst = require("./dbdmap.json");
  const vote = req.params.vote;
  if (vote == "up") {
    dbdmaplst[req.params.map] += 1;
  }
  else if (vote == "down") {
    if (dbdmaplst[req.params.map] > 0) {
      dbdmaplst[req.params.map] -= 1;
    }
  }

  fs.writeFile(filePath, JSON.stringify(dbdmaplst), (err) => {
    if (err) console.log('Error writing file:', err)
  })

  res.render('index', { title: 'DBD MAP LOG', dbdmap: dbdmaplst});
});
module.exports = router;

6 .pugでhtmlを生成

pugはpythonのようにインデントで管理されているようです。

以下ソース内に注意点を記載

index.pug
//layout.pugをテンプレートに使う
extends layout

//テンプレート内のblock contentに置き換えられる
block content
  //'-'を先頭に記載することでjavascriptを組み込むことができる。
  -var count = 0;
  div(class="tbl")
    table(cellpadding=10)
      //pug 独自のfor/while/if-else文を扱える
      //ここの'dbdmap'はindex.jsから受け取ったもの
      each val, key in dbdmap
        -count += val;
        tr 
          //'#{}'で変数を扱うことができる。
          td #{val}
          td #{key}
          td
            a(href= "/" + key + "/up") up
          td 
            a(href= "/" + key + "/down") down

      h2 Match count: #{count}

pugの記述方法はこちら

7.cssの記述

cssよく分からないのですが、こんな感じにしたらいい感じになりました。。。

style.css
body {
  padding: 50px;
  font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}

a {
  color: #00B7FF;
}

table, tr, td {
  border: 1px solid black;
  padding: 10px;
}

table {
  display: inline-block;
  float: left;
}

cssの読み込み方。テンプレートのlayout.pugに記載する。

layout.pug
doctype html
html
  head
    title= title
  body
    block content
    //javascript
    script(src='/javascripts/todo.js')
    //css
    link(rel='stylesheet', href='/stylesheets/style.css')

終わりに

以上すべてのソースコードを記載したわけではありませんが、詰まる恐れがある点について解説できました。

もし質問があればどうぞコメントにどうぞ。