2

一部のサイトの Web スクレイピングを行い、データを PostgreSQL データベースに保存してから、このデータを (D3.js で) 視覚化して Web ページに表示する Node.js アプリケーションを作成したいと考えています。フロントエンド部分 (ビジュアライゼーションの作成と表示) とバックエンド部分 (Web スクレイピングとデータベースの更新) を分割することを考えました。

2つのアプリ(タスクを2つのアプリに分けているので2つあります)の骨格は以下の通りです。

バックエンド アプリ ( scraper):

  • データベースへの接続
  • テーブルが存在しない場合の作成
  • データのスクレーパー
  • データベースにデータを保存する
  • データベースからの切断。

このバックエンド アプリケーションは、年に数回だけ起動する必要があります (たとえば、Unix を使用している場合は、これを行うために CRON ファイルを構成できます)。

フロントエンド アプリ ( viz):

  • データベースへの接続
  • ポート 3000 で待機しているサーバーを起動します (可視化のために必要です)
  • ユーザーがページを更新するたびに ( onLoad())、アプリはSELECTデータベースからデータを取得するクエリを作成します ( )。このようにして、データは常に更新されます。

このアプリケーションは、プログラマーによって (理想的には) 1 回だけ開始されます。

このタイプのフォルダー構造を作成しました (私は と を使用npm initしましたExpress):

project
 |_ scraper
     |_ helpers // contains some useful .js files
          |_ elaborateJson.js
          |_ saveOnDb.js
          |_ utilFunc.js
     |_ node_modules // modules installed using `npm install moduleName --save`
     |_ routes // contains the files that make scraping
          |_ downloaderHome.js
          |_ downloaderWork.js
     |_ services // contains a files concerning the db
             |_ postgreSQLlib.js
     |_ app.js
     |_ package.json
     |_ package-lock.json
 |_ viz
     |_ helpers // // contains some useful .js files
          |_ utilFunc.js
     |_ node_modules // modules installed using `npm install moduleName --save`
     |_ public // contains files for visualizations
          |_ index.handlebars
          |_ script.js
          |_ style.css
     |_ services // contains a file concerning the db
             |_ postgreSQLlib.js
     |_ app.js
     |_ package.json
     |_ package-lock.json

この構造では、解決方法がわからない2つの問題がすでにあります。

  1.postgreSQLlib.jsファイル (およびutilFunc.js) は と の両方scraperで同じvizです。このコードの重複を避けるにはどうすればよいですか?

  2. 一部のモジュール (たとえばexpress-handlebarsand )を andフォルダーexpressに 2 回インストールする必要がありました。scraperviz

これはproject/scraper/app.js次のとおりです。

const downloaderHome = require('./routes/downloaderHome.js');
const downloaderWork = require('./routes/downloaderWork.js');
const postgreSQLlib = require('./services/postgreSQLlib.js');
const saveOnDb = require('./helpers/saveOnDb.js');
const utilFunc = require('./helpers/utilFunc.js');
const express = require('express');
const exphbs = require('express-handlebars');

var app = express();

start();

async function start() {
    console.log('\n Connect to db');
    await postgreSQLlib.connect();

    console.log('\n Create tables if they do not exist');
    await postgreSQLlib.createHomeTable();
    await postgreSQLlib.createWorkTable();

    console.log('\n Check if table \'home\' is updated or not');
    if(!await utilFunc.isTableUpdated('home', 6418)) { // 6308
        console.log('\n   Download data for home');
        await downloaderHome.download();
        console.log('\n   Saving data for home on db');
        await saveOnDb.saveHome();
    }   

    console.log('\n Check if table \'work\' is updated or not');
    if(!await utilFunc.isTableUpdated('work', 6804)) {
        console.log('\n   Download data for work');
        await downloaderWork.download();
        console.log('\n   Saving data for work on db');
        await saveOnDb.saveWork();
    }

    console.log('\n Disconnect from db');
    await postgreSQLlib.disconnect();
}

これはproject/viz/app.js次のとおりです。

const postgreSQLlib = require('./services/postgreSQLlib.js');
const utilFunc = require('./helpers/utilFunc.js');
const express = require('express');
const exphbs = require('express-handlebars');
const http = require('http');

var app = express();

var response;
var callback;

start();

async function start() {
    console.log('\n Connect to db');
    await postgreSQLlib.connect();

    // how do I check when page is refreshed?!
    http.get({
            hostname: 'localhost',
            port: 3000,
            path: '/',
            agent: false
        }, callback);

    callback = function(res) {
        response = res;
        console.log(response); // here response will return an object
        console.log('refresh callback');
    }

    console.log(response);
    console.log('refresh');

    ///////////////////////////////////////////////
    // How do I check the disconnection from the db? 
    // If I disconnect now, the visualizations are no longer work. 
    // So when do I get disconnected? 
    // Create problems leaving the connection to the active db?
    ///////////////////////////////////////////////
    //console.log('\n Disconnect from db');
    //await postgreSQLlib.disconnect();
}

最初のアプリケーション ( project/scraper/app.js) は完全に機能します。

第二の申請(project/viz/app.js)番号。私はあなたにこれをしてもらいたい:

  • データベースへの接続[完了。できます]
  • ポート 3000 で待機しているサーバーを起動します (視覚化のために必要です) [どうすればよいですか? 下を向いて(*) ]
  • ユーザーがページを更新するたびに ( onLoad())、アプリはSELECTデータベースからデータを取得するクエリ ( ) を作成します [どうすればいいですか?]

(*)私は次のようなことを考えていました:

async function start() {
    console.log('\n Connect to db');
    await postgreSQLlib.connect();

    console.log('\n Get data from db');
    var dataHome = await postgreSQLlib.getTableHome();
    var dataWork = await postgreSQLlib.getTableWork();

    //console.log('\n Connect to my server');
    pageLoad(dataHome, dataWork); 
}

function pageLoad(dataHome, dataWork) {
    var hbs = exphbs.create({
        helpers: {
            getDataHome: function() { 
                return JSON.stringify(dataHome); 
            },
            getDataWork: function() { 
                return JSON.stringify(dataWork); 
            }
        }
    });

    app.engine('handlebars', hbs.engine);
    app.set('view engine', 'handlebars');

    app.get('/', function(req, res, next) {
        res.render('index', { // index is html filename
            showTitle: true,
        });
    });
    console.log('Go to http://localhost:3000/ to see visualizations');
    app.listen(3000);
}

dataHomeとは、クエリdataWorkを使用してデータベースからダウンロードされたデータを含む 2 つのオブジェクトですSELECT。ただし、この方法では、ユーザーがページを更新するたびにデータが破棄されるわけではなく、1 回だけ破棄されます。

助けていただければ幸いです。ありがとうございました!

編集

もっと正確に言えますか?私はそれをやろうとしましたが、うまくいきません:

project/viz/app.js :

const postgreSQLlib = require('../shared_libs/postgreSQLlib.js');
const express = require('express');

var app = express();

start();
async function start() {
    console.log('Connect to db');
    await postgreSQLlib.connect();

    app.get('/', fetchFreshData);
}

async function fetchFreshData(req, res) {
    // download data from db
    var dataHome = await postgreSQLlib.getTableHome();
    var dataWork = await postgreSQLlib.getTableWork();
    // fill this JSON using the results
    var viewData = {dataHome, dataWork};
    // pass data to view
    res.render('index', viewData);
}

project\viz\view\index.handlebars :

<!DOCTYPE html>
<html lang='en'>
    <head>
        <meta charset='utf-8'>
        <title>Map</title>
        <script src='https://d3js.org/d3.v5.js' charset='utf-8'></script>
        <link rel='stylesheet' type='text/css' href='/style.css' media='screen'/>
    </head>
    <body>
        <div id='example'></div>
    </body>
    <script src='/script.js'></script>
</html>

project\viz\view\script.js :

console.log('viewData:', viewData);

どこが間違っていますか?

編集2

わかりました、viz/app.jsコードを再度変更します。

const postgreSQLlib = require('../shared_libs/postgreSQLlib.js');
const express = require('express');
const exphbs = require('express-handlebars');

var app = express();

start();

async function start() {
    await postgreSQLlib.connect();

    var hbs = Handlebars.registerHelper('json', function(context) {
        return JSON.stringify(context);
    });
    app.engine('handlebars', hbs.engine);
    app.set('view engine', 'handlebars');
    app.get('/', fetchFreshData);
    console.log('Go to http://localhost:3000/ to see data');
    app.listen(3000);
}

async function fetchFreshData(req, res) {
    // download data from db
    var dataHome = await postgreSQLlib.getTableHome();
    var dataWork = await postgreSQLlib.getTableWork();
    // fill this JSON using the results
    var viewData = {}; 
    viewData.timestamp = Date.now();
    viewData.entries = dataHome;
    // pass data to view
    res.render('index', viewData);
}

アプリを実行するとエラーは発生しませんが、http://localhost:3000/に接続すると、ブラウザーはサイトにアクセスできないと通知します。ちょっとバカな気がする…

編集3

私があなたのコードを正しく理解していれば、あなたのコードには (気を散らすような) エラーがあります。returnOBJ()代わりに(ファイルに関連する)にres.render('index', viewData);する必要があります。右?res.render('obj', viewData);obj.hbs

この方法で index.hbs ファイルを変更します。

<html lang='en'>
    <head>
        <meta charset='utf-8'>
        <title>Index</title>
        <script src='https://d3js.org/d3.v5.js' charset='utf-8'></script>
        <link rel='stylesheet' type='text/css' href='/style.css' media='screen'/>
    </head>
    <body>
        <h1>INDEX<small>{{timestamp}}</small></h1>
    </body>
    <script> 
        // add global variables in the .hbs file 
        window.viewData_dataWork = {{ json entries }}
        console.log(window.viewData);
    </script>
    <script src='/script.js'></script>
</html>

しかし、私は得る:

(node:207156) UnhandledPromiseRejectionWarning: Error: callback function required
    at Function.engine (C:\...\node_modules\express\lib\application.js:295:11)
    at start (C:\...\viz\app.js:20:6)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:182:7)
(node:207156) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:207156) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

このコードもわかりません。

app.set('view engine', 'hbs');
app.engine('hbs', hbs.__express);
hbs.registerHelper('json', function(context) {
    return JSON.stringify(context);
});
app.engine('handlebars', hbs.engine);
app.set('view engine', 'handlebars');

app.set('view engine', ...)異なる値で 2 回呼び出すのはなぜですか?

編集4

コードをさらに単純化しました。

/viz/app.js :

const postgreSQLlib = require(__dirname + './../shared_libs/services/postgreSQLlib.js');
const express = require('express');
const hbs = require('hbs');

var app = express();

// Server initiator
async function start() {
    await postgreSQLlib.connect();

    // hbs
    app.set('views', '' + __dirname + '/views');
    app.set('view engine', 'hbs');
    app.engine('hbs', hbs.__express);
    hbs.registerHelper('json', function(context) {
        return JSON.stringify(context);
    });
    app.engine('handlebars', hbs.engine);
    app.set('view engine', 'handlebars');

    // router
    app.get('/', testMe);

    console.log('Go to http://localhost:3000/ to see data');
    app.listen(3000);
}

// Your section with fresh data has been populated properly
async function testMe(req, res) {
    console.log('testMe');
    // fill this JSON using the results
    var viewData = {}; 
    viewData.data = 'this string';
    // pass data to view
    res.render('test', viewData);
}

// start the server
start();

/viz/views/test.hbs :

<html>
    <head>
        <title>Server test</title>
    </head>
    <body>
        {{data}}
    </body>
</html>

次に、プロンプト コマンドで に移動しproject/viznode app.js+ Enter と入力します。プロセスが開始され、待機します。エラーはありません。http://localhost:3000/に行くとConnection failed が表示されます。

私は夢中になっています。

編集5

問題はconnect選択を行った関数でもなかったので、コードを少し単純化しました。そして今、それはほとんど動作します!

これがコードです。

viz/app.js :

const postgreSQLlib = require(__dirname + './../shared_libs/services/postgreSQLlib.js');
const express = require('express'); 
var app = express()
const hbs = require('hbs');
const webapp_opts = {"port":3000};

Initialize();

//.: Setup & Start Server
async function Initialize(){
    await postgreSQLlib.connect();

    console.log("[~] starting ...")
    //:[HBS]:Setup
    app.set('view engine', 'hbs'); 
    app.engine('hbs', hbs.__express)
    app.set('views', "" + __dirname + "/views")
    //:[HBS]:Helpers
    hbs.registerHelper('json', function(context) {
        return JSON.stringify(context);
    })
    //:[EXPRESS]:Router.Paths
    app.get("/", IndexPathFunction);
    // app.get("/script.js", scriptFile); <-- for script.js file

    //:[EXPRESS]:Start 
    app.listen(webapp_opts.port,()=>{
        console.log("[i] ready & listening","\n    http://localhost:"+webapp_opts.port+"/")
    })
}

/*async function scriptFile(req, res) { <-- for script.js file
    console.log('\nscriptFile');
    var viewData = {}; 
    viewData.number = 50;
    console.log('viewData:', viewData);
    res.render('script.js', viewData);
}*/

//.: Router Function : "/"
async function IndexPathFunction(req,res){
    var viewData = {}; 
    viewData.timestamp = Date.now();
    viewData.exJson = [{color: 'red', year: '1955'}, {color: 'blue', year: '2000'}, {color: 'yellow', year: '2013'}]; 
    viewData.exString = 'example of string'; 
    console.log('viewData:', viewData);
    res.render('index', viewData);
}

viz/views/index.hbs :

<html lang='en'>
    <head>
        <meta charset='utf-8'>
        <title>Index</title>
        <script src='https://d3js.org/d3.v5.js' charset='utf-8'></script>
        <link rel='stylesheet' type='text/css' href='/style.css' media='screen'/>
    </head>
    <body>
        <h1>INDEX timestamp: <small>{{timestamp}}</small></h1>
    </body>
    <script> 
        viewData = {}; 
        console.log('viewData:', viewData);
        viewData.exJson = JSON.parse('{{ json exJson }}'.replace(/&quot;/g, '"').replace(/&lt;/, ''));
        viewData.timestamp = {{timestamp}}; // doesn't work
        viewData.exString = {{ exString }}; // doesn't work
        console.log('viewData.exJson:', viewData.exJson);
        console.log('viewData.timestamp:', viewData.timestamp);
        console.log('viewData.exString:', viewData.exString);
    </script>
    <!--<script src='/script.js'></script>-->
</html>

問題は、json ではないデータ型を取得することです。たとえば、タイムスタンプと exString を出力しようとするとエラーが発生します。なんで?

また、コードを少しクリーンアップして、javascript の部分をを使用してscript.js呼び出されるファイル内に配置したいと思います。index.hbs<script src='/script.js'></script>

編集6

私にとって非常に役立つこのチュートリアルを見つけました。

index.hbscssファイル、画像、スクリプトを追加してファイルを編集しました(これにはaのみが含まれていますconsole.log('here');が、script.jsにviewData変数を配置することを考えています)。

project/viz/views/index.hbs :

<html lang='en'>
    <head>
        <meta charset='utf-8'>
        <title>Index</title>
        <script src='https://d3js.org/d3.v5.js' charset='utf-8'></script>
        <link href="/css/style.css" rel="stylesheet">
    </head>
    <body>
        <img src="/images/logo.png"/>
        <h1>timestamp: <small>{{timestamp}}</small></h1>
        <h2>Welcome in index.hbs</h2>
    </body>
    <script> 
        viewData = {}; 
        console.log('viewData:', viewData);
        viewData.exJson = JSON.parse('{{json exJson }}'.replace(/&quot;/g, '"').replace(/&lt;/, ''));
        viewData.timestamp = {{timestamp}};
        viewData.exString = '{{exString}}'; 
        console.log('viewData.exJson:', viewData.exJson);
        console.log('viewData.timestamp:', viewData.timestamp);
        console.log('viewData.exString:', viewData.exString);
    </script>
    <link href='/script/script.js' rel='script'>
</html>

私のファイル構造は次のとおりです。

project
    |_ node_modules
    |_ scraper
    |_ shared_libs
    |_ viz  
        |_ app.js 
        |_ public
            |_ css
                |_ style.css
            |_ images
                |_ logo.png
            |_ script
                |_ script.js
        |_ views
            |_ index.hbs

これで画像が表示され、css が使用されます。しかし、ここに文字列が出力されていないため、スクリプトは機能していないようです。

スクリプトタグから外部jsファイルに変数を渡す方法についてインターネットで検索しましたが、自分に合ったものが見つからなかったようです。ハンドルバー API を読みましたが、役に立ちませんでした。

4

1 に答える 1