You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js技术咨询:Express路由渲染与爬虫可视化前后端架构实现

解决方案:Node.js 双场景实现指南(路由渲染 + 爬虫可视化)

针对你提出的两个Node.js相关需求,我整理了详细的实现方案,分模块拆解如下:


一、Express + Handlebars 实现数据查询与视图渲染

这个需求核心是打通「数据库查询 → 路由转发 → 模板渲染」的链路,咱们一步步来:

1. 项目初始化与依赖安装

先创建项目并安装必要依赖:

mkdir express-data-view && cd express-data-view
npm init -y
npm install express express-handlebars pg

2. 项目结构规划

建议按模块拆分,方便维护:

express-data-view/
├── src/
│   ├── db/           # 数据库连接模块
│   │   └── connection.js
│   ├── routes/       # 路由逻辑
│   │   └── dataRoutes.js
│   ├── views/        # Handlebars模板
│   │   ├── layouts/
│   │   │   └── main.hbs  # 基础布局模板
│   │   └── template.hbs  # 数据展示模板
│   └── app.js        # Express主入口
└── package.json

3. 数据库连接配置(db/connection.js)

用pg模块连接PostgreSQL,记得替换成你的数据库信息:

const { Pool } = require('pg');

const pool = new Pool({
  user: '你的数据库用户名',
  host: 'localhost',
  database: '你的数据库名',
  password: '你的数据库密码',
  port: 5432, // 默认PostgreSQL端口
});

module.exports = pool;

4. 路由逻辑实现(routes/dataRoutes.js)

编写路由处理数据库查询,并把数据传给模板:

const express = require('express');
const router = express.Router();
const pool = require('../db/connection');

// 定义数据展示路由
router.get('/show-data', async (req, res) => {
  try {
    // 从数据库查询数据(替换成你的表名和字段)
    const queryResult = await pool.query('SELECT id, title, content FROM your_table');
    // 渲染template.hbs模板,传入查询到的数据
    res.render('template', { dataList: queryResult.rows });
  } catch (error) {
    console.error('数据库查询失败:', error);
    res.status(500).send('服务器内部错误');
  }
});

module.exports = router;

5. Express主应用配置(src/app.js)

配置模板引擎并挂载路由:

const express = require('express');
const { engine } = require('express-handlebars');
const dataRoutes = require('./routes/dataRoutes');

const app = express();
const PORT = 3000;

// 配置Handlebars模板引擎
app.engine('hbs', engine({ extname: '.hbs' }));
app.set('view engine', 'hbs');
app.set('views', './src/views');

// 挂载路由
app.use('/', dataRoutes);

// 启动服务
app.listen(PORT, () => {
  console.log(`Express服务已启动:http://localhost:${PORT}/show-data`);
});

6. Handlebars模板渲染(views/template.hbs)

编写模板展示数据,这里用列表举例:

<h1>数据库数据展示</h1>
<div class="data-container">
  {{#each dataList}}
    <div class="data-item">
      <h3>{{title}}</h3>
      <p>{{content}}</p>
      <span>ID: {{id}}</span>
    </div>
  {{else}}
    <p>暂无数据</p>
  {{/each}}
</div>

二、前后端拆分的爬虫+PostgreSQL+D3.js 可视化架构

这个需求拆分为后端爬虫服务(负责爬取、存储数据)和前端可视化应用(负责用D3.js展示数据),两个应用独立运行:

1. 后端爬虫服务实现

1.1 初始化与依赖安装

mkdir crawler-service && cd crawler-service
npm init -y
npm install axios cheerio pg node-schedule cors
  • axios:发送HTTP请求爬取网页
  • cheerio:解析HTML结构
  • pg:连接PostgreSQL
  • node-schedule:定时执行爬取任务
  • cors:允许前端跨域请求数据

1.2 项目结构

crawler-service/
├── src/
│   ├── db/
│   │   └── connection.js  # 数据库连接(和之前的配置一致)
│   ├── crawler/
│   │   └── scraper.js     # 网页爬取逻辑
│   ├── services/
│   │   └── dataService.js # 数据存储逻辑
│   └── app.js             # 主入口(含API接口+定时任务)
└── package.json

1.3 网页爬取逻辑(crawler/scraper.js)

替换成你要爬取的目标网站和元素选择器:

const axios = require('axios');
const cheerio = require('cheerio');

async function scrapeTargetSite() {
  try {
    // 发送请求获取网页内容
    const response = await axios.get('https://目标网站地址.com');
    const $ = cheerio.load(response.data);

    // 解析网页,提取需要的数据
    const scrapedData = [];
    $('.目标元素选择器').each((index, element) => {
      const itemName = $(element).find('.名称选择器').text().trim();
      const itemValue = $(element).find('.数值选择器').text().trim();
      scrapedData.push({ name: itemName, value: itemValue });
    });

    return scrapedData;
  } catch (error) {
    console.error('爬取失败:', error);
    return [];
  }
}

module.exports = { scrapeTargetSite };

1.4 数据存储逻辑(services/dataService.js)

把爬取到的数据存入PostgreSQL:

const pool = require('../db/connection');

async function saveScrapedData(data) {
  try {
    // 可选:清空旧数据(根据业务需求决定)
    await pool.query('TRUNCATE TABLE scraped_data RESTART IDENTITY');

    // 批量插入新数据
    const insertQuery = `
      INSERT INTO scraped_data (item_name, item_value)
      VALUES ($1, $2)
    `;

    for (const item of data) {
      await pool.query(insertQuery, [item.name, item.value]);
    }

    console.log('数据存储成功,共插入', data.length, '条数据');
  } catch (error) {
    console.error('数据存储失败:', error);
  }
}

module.exports = { saveScrapedData };

1.5 主入口:API接口+定时任务(src/app.js)

启动服务,提供数据接口,并配置定时爬取:

const express = require('express');
const cors = require('cors');
const { scrapeTargetSite } = require('./crawler/scraper');
const { saveScrapedData } = require('./services/dataService');
const schedule = require('node-schedule');
const pool = require('./db/connection');

const app = express();
const PORT = 3001;

// 允许跨域请求
app.use(cors());

// 提供数据API接口,给前端可视化用
app.get('/api/scraped-data', async (req, res) => {
  try {
    const result = await pool.query('SELECT * FROM scraped_data');
    res.json(result.rows);
  } catch (error) {
    console.error('获取数据失败:', error);
    res.status(500).json({ error: '服务器内部错误' });
  }
});

// 执行爬取并存储的函数
async function runCrawler() {
  const data = await scrapeTargetSite();
  if (data.length > 0) {
    await saveScrapedData(data);
  }
}

// 启动时立即执行一次爬取
runCrawler();

// 定时任务:每天凌晨3点自动爬取更新数据
schedule.scheduleJob('0 0 3 * * *', async () => {
  console.log('开始定时爬取更新...');
  await runCrawler();
});

// 启动服务
app.listen(PORT, () => {
  console.log(`爬虫服务已启动:http://localhost:${PORT}`);
});

2. 前端可视化应用实现

前端是独立的静态应用,用D3.js从后端API获取数据并渲染可视化图表:

2.1 项目初始化

mkdir visualization-frontend && cd visualization-frontend
npm init -y
# 可选:用Vite等构建工具,或者直接用静态HTML

2.2 可视化页面(index.html)

这里以柱状图为例,替换成你需要的可视化类型:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>数据可视化展示</title>
  <script src="https://d3js.org/d3.v7.min.js"></script>
  <style>
    #chart-container {
      width: 900px;
      margin: 20px auto;
    }
  </style>
</head>
<body>
  <div id="chart-container">
    <h2>爬取数据可视化</h2>
    <div id="bar-chart"></div>
  </div>

  <script>
    // 从后端API获取数据
    d3.json('http://localhost:3001/api/scraped-data')
      .then(data => {
        // 配置图表参数
        const width = 900;
        const height = 500;
        const margin = { top: 30, right: 30, bottom: 60, left: 60 };

        // 创建SVG容器
        const svg = d3.select('#bar-chart')
          .append('svg')
          .attr('width', width)
          .attr('height', height);

        // 定义X轴(分类轴)
        const xScale = d3.scaleBand()
          .domain(data.map(item => item.item_name))
          .range([margin.left, width - margin.right])
          .padding(0.2);

        // 定义Y轴(数值轴)
        const yScale = d3.scaleLinear()
          .domain([0, d3.max(data, item => parseInt(item.item_value))])
          .range([height - margin.bottom, margin.top]);

        // 绘制X轴
        svg.append('g')
          .attr('transform', `translate(0, ${height - margin.bottom})`)
          .call(d3.axisBottom(xScale))
          .selectAll('text')
          .attr('transform', 'rotate(-45)')
          .style('text-anchor', 'end');

        // 绘制Y轴
        svg.append('g')
          .attr('transform', `translate(${margin.left}, 0)`)
          .call(d3.axisLeft(yScale));

        // 绘制柱状图
        svg.selectAll('rect')
          .data(data)
          .enter()
          .append('rect')
          .attr('x', item => xScale(item.item_name))
          .attr('y', item => yScale(parseInt(item.item_value)))
          .attr('width', xScale.bandwidth())
          .attr('height', item => height - margin.bottom - yScale(parseInt(item.item_value)))
          .attr('fill', '#2ecc71')
          .on('mouseover', function() {
            d3.select(this).attr('fill', '#27ae60');
          })
          .on('mouseout', function() {
            d3.select(this).attr('fill', '#2ecc71');
          });
      })
      .catch(error => {
        console.error('获取数据失败:', error);
        document.getElementById('bar-chart').innerHTML = '<p>数据加载失败,请稍后重试</p>';
      });
  </script>
</body>
</html>

内容的提问来源于stack exchange,提问作者user3808470

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:35:21