How to Price Intelligence with JavaScript — Tutorial
Price intelligence has become a critical component of competitive e-commerce strategies. In today's dynamic marketplace, businesses need real-time insights into competitor pricing to optimize their own pricing strategies. This tutorial will guide you through implementing a price intelligence system using JavaScript, helping you gather, analyze, and act on pricing data from across the web.
Key Takeaways
- Understand the fundamentals of price intelligence and why it matters for e-commerce
- Learn how to set up a JavaScript environment for web scraping and data collection
- Implement a price monitoring system using modern JavaScript and APIs
- Analyze pricing data to identify trends and opportunities
- Create automated alerts for significant price changes
- Build a dashboard to visualize pricing intelligence
Prerequisites for Price Intelligence Implementation
Before diving into the code, ensure you have the following prerequisites:
- Basic knowledge of JavaScript (ES6+ features)
- Node.js installed on your machine
- A code editor (VS Code recommended)
- Understanding of REST APIs and HTTP requests
- Familiarity with JSON data structures
For this tutorial, we'll use the SearchHive platform, which provides powerful APIs for web scraping and data collection. Specifically, we'll leverage the ScrapeForge API to extract pricing data from e-commerce websites.
Setting Up Your Development Environment
First, let's set up our project structure and install necessary dependencies:
mkdir price-intelligence-tool
cd price-intelligence-tool
npm init -y
npm install node-fetch cheerio express moment
Create a .env file to store your API credentials:
SEARCHHIVE_API_KEY=your_api_key_here
Now, let's create our main application files:
price-intelligence-tool/
├── .env
├── package.json
├── index.js
├── scraper.js
├── analyzer.js
├── server.js
└── public/
├── index.html
└── script.js
Step 1: Authenticating with SearchHive API
Create a config.js file to handle API authentication:
// config.js
require('dotenv').config();
const config = {
apiKey: process.env.SEARCHHIVE_API_KEY,
baseUrl: 'https://api.searchhive.dev',
timeout: 30000
};
module.exports = config;
This configuration will be used throughout our application to authenticate with the SearchHive API.
Step 2: Implementing Web Scraping with ScrapeForge API
Let's create a scraper module using the ScrapeForge API to extract pricing data from e-commerce websites:
// scraper.js
const fetch = require('node-fetch');
const config = require('./config');
class PriceScraper {
constructor() {
this.baseUrl = `${config.baseUrl}/scrapeforge`;
}
async scrapeProduct(url, selectors) {
try {
const response = await fetch(`${this.baseUrl}/scrape`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${config.apiKey}`
},
body: JSON.stringify({
url,
selectors,
render: true,
wait: 3000
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return this.extractPriceData(data, selectors);
} catch (error) {
console.error('Error scraping product:', error);
throw error;
}
}
extractPriceData(scrapedData, selectors) {
if (!scrapedData.success || !scrapedData.result) {
throw new Error('Failed to scrape data from the provided URL');
}
const result = scrapedData.result;
const priceData = {
url: result.url,
timestamp: new Date().toISOString(),
prices: []
};
// Extract price using provided selectors
selectors.forEach(selector => {
const element = result.querySelector(selector);
if (element) {
const priceText = element.textContent.trim();
const price = this.parsePrice(priceText);
if (price) {
priceData.prices.push({
source: selector,
value: price,
currency: this.extractCurrency(priceText)
});
}
}
});
return priceData;
}
parsePrice(priceText) {
// Extract numeric value from price text
const priceMatch = priceText.match(/[\d,]+\.?\d*/);
return priceMatch ? parseFloat(priceMatch[0].replace(',', '')) : null;
}
extractCurrency(priceText) {
// Extract currency symbol from price text
const currencyMatch = priceText.match(/[^\d\s](?=\d)/);
return currencyMatch ? currencyMatch[0] : null;
}
}
module.exports = PriceScraper;
This scraper class provides methods to extract pricing data from e-commerce websites using the ScrapeForge API, which handles JavaScript rendering and anti-bot measures.
Step 3: Building a Price Monitoring System
Now, let's create a price monitoring system that tracks changes over time:
// analyzer.js
const moment = require('moment');
const fs = require('fs').promises;
const path = require('path');
class PriceAnalyzer {
constructor(dataDir = './data') {
this.dataDir = dataDir;
this.ensureDataDir();
}
async ensureDataDir() {
try {
await fs.access(this.dataDir);
} catch {
await fs.mkdir(this.dataDir, { recursive: true });
}
}
async savePriceData(productUrl, priceData) {
const filename = this.getFilename(productUrl);
const filepath = path.join(this.dataDir, filename);
try {
let existingData = [];
try {
const data = await fs.readFile(filepath, 'utf8');
existingData = JSON.parse(data);
} catch (error) {
// File doesn't exist yet, which is fine
}
existingData.push(priceData);
await fs.writeFile(filepath, JSON.stringify(existingData, null, 2));
} catch (error) {
console.error('Error saving price data:', error);
throw error;
}
}
async getPriceHistory(productUrl, days = 30) {
const filename = this.getFilename(productUrl);
const filepath = path.join(this.dataDir, filename);
try {
const data = await fs.readFile(filepath, 'utf8');
const priceHistory = JSON.parse(data);
const cutoffDate = moment().subtract(days, 'days').toISOString();
return priceHistory.filter(entry => entry.timestamp >= cutoffDate);
} catch (error) {
console.error('Error reading price history:', error);
return [];
}
}
analyzePriceTrends(productUrl, days = 30) {
return this.getPriceHistory(productUrl, days).then(history => {
if (history.length < 2) {
return { trend: 'insufficient_data', change: 0 };
}
const sortedHistory = history.sort((a, b) =>
new Date(a.timestamp) - new Date(b.timestamp)
);
const firstEntry = sortedHistory[0];
const lastEntry = sortedHistory[sortedHistory.length - 1];
const firstPrice = firstEntry.prices[0].value;
const lastPrice = lastEntry.prices[0].value;
const change = ((lastPrice - firstPrice) / firstPrice) * 100;
let trend = 'stable';
if (Math.abs(change) > 5) {
trend = change > 0 ? 'increasing' : 'decreasing';
}
return {
trend,
change: parseFloat(change.toFixed(2)),
firstPrice,
lastPrice,
dataPoints: history.length
};
});
}
getFilename(productUrl) {
// Create a safe filename from URL
const safeUrl = productUrl.replace(/https?:\/\//, '').replace(/[^\w\s-]/g, '-');
return `${safeUrl}.json`;
}
}
module.exports = PriceAnalyzer;
This analyzer class manages price data storage and provides methods to analyze pricing trends over time.
Step 4: Creating a Web Server for the Dashboard
Let's create an Express server to serve our price intelligence dashboard:
// server.js
const express = require('express');
const path = require('path');
const PriceScraper = require('./scraper');
const PriceAnalyzer = require('./analyzer');
const config = require('./config');
const app = express();
const port = process.env.PORT || 3000;
const scraper = new PriceScraper();
const analyzer = new PriceAnalyzer();
// Serve static files
app.use(express.static('public'));
app.use(express.json());
// API endpoints
app.get('/api/products/:url', async (req, res) => {
try {
const { url } = req.query;
const selectors = req.body.selectors || ['.price', '.product-price'];
const priceData = await scraper.scrapeProduct(url, selectors);
await analyzer.savePriceData(url, priceData);
res.json({ success: true, data: priceData });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
app.get('/api/analysis/:url', async (req, res) => {
try {
const { url } = req.params;
const days = parseInt(req.query.days) || 30;
const analysis = await analyzer.analyzePriceTrends(url, days);
res.json({ success: true, data: analysis });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
app.listen(port, () => {
console.log(`Price intelligence server running at http://localhost:${port}`);
});
This server provides API endpoints for scraping product prices and analyzing price trends.
Step 5: Building the Frontend Dashboard
Create the HTML file for our dashboard:
<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Price Intelligence Dashboard</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: #333;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
.container {
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 30px;
}
h1 {
color: #2c3e50;
margin-bottom: 20px;
text-align: center;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input, button, select {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
background-color: #3498db;
color: white;
border: none;
cursor: pointer;
font-size: 16px;
transition: background-color 0.3s;
}
button:hover {
background-color: #2980b9;
}
.result {
margin-top: 20px;
padding: 15px;
border-radius: 4px;
background-color: #f8f9fa;
}
.trend {
font-weight: bold;
margin: 10px 0;
}
.trend.increasing {
color: #e74c3c;
}
.trend.decreasing {
color: #27ae60;
}
.trend.stable {
color: #f39c12;
}
.price-history {
margin-top: 20px;
}
.price-item {
padding: 10px;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
}
.loading {
display: none;
text-align: center;
margin: 20px 0;
}
.error {
color: #e74c3c;
margin-top: 10px;
}
.tabs {
display: flex;
margin-bottom: 20px;
}
.tab {
padding: 10px 20px;
background-color: #f1f1f1;
cursor: pointer;
border-radius: 4px 4px 0 0;
margin-right: 5px;
}
.tab.active {
background-color: #3498db;
color: white;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
</style>
</head>
<body>
<div class="container">
<h1><i class="fas fa-chart-line"></i> Price Intelligence Dashboard</h1>
<div class="tabs">
<div class="tab active" onclick="openTab(event, 'monitor-tab')">Monitor Prices</div>
<div class="tab" onclick="openTab(event, 'analysis-tab')">Price Analysis</div>
</div>
<div id="monitor-tab" class="tab-content active">
<div class="form-group">
<label for="product-url">Product URL:</label>
<input type="text" id="product-url" placeholder="https://example.com/product">
</div>
<div class="form-group">
<label for="price-selector">CSS Selector for Price:</label>
<input type="text" id="price-selector" placeholder=".price" value=".price">
</div>
<button onclick="scrapeProduct()">Scrape Product Price</button>
<div class="loading" id="loading">
<i class="fas fa-spinner fa-spin"></i> Scraping price data...
</div>
<div id="scrape-result"></div>
</div>
<div id="analysis-tab" class="tab-content">
<div class="form-group">
<label for="analysis-url">Product URL:</label>
<input type="text" id="analysis-url" placeholder="https://example.com/product">
</div>
<div class="form-group">
<label for="time-range">Time Range (days):</label>
<select id="time-range">
<option value="7">Last 7 days</option>
<option value="30" selected>Last 30 days</option>
<option value="90">Last 90 days</option>
<option value="365">Last year</option>
</select>
</div>
<button onclick="analyzePrice()">Analyze Price Trends</button>
<div class="loading" id="analysis-loading">
<i class="fas fa-spinner fa-spin"></i> Analyzing price data...
</div>
<div id="analysis-result"></div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
Step 6: Implementing Frontend JavaScript
Create the client-side JavaScript to interact with our API:
// public/script.js
const API_BASE = 'http://localhost:3000/api';
function openTab(evt, tabName) {
const tabContents = document.getElementsByClassName('tab-content');
for (let i = 0; i < tabContents.length; i++) {
tabContents[i].classList.remove('active');
}
const tabs = document.getElementsByClassName('tab');
for (let i = 0; i < tabs.length; i++) {
tabs[i].classList.remove('active');
}
document.getElementById(tabName).classList.add('active');
evt.currentTarget.classList.add('active');
}
async function scrapeProduct() {
const url = document.getElementById('product-url').value;
const selector = document.getElementById('price-selector').value;
if (!url) {
showError('scrape-result', 'Please enter a product URL');
return;
}
showLoading('loading');
hideError('scrape-result');
try {
const response = await fetch(`${API_BASE}/products?url=${encodeURIComponent(url)}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
selectors: [selector]
})
});
const data = await response.json();
if (data.success) {
displayScrapeResult(data.data);
} else {
showError('scrape-result', data.error || 'Failed to scrape product price');
}
} catch (error) {
showError('scrape-result', 'Network error: ' + error.message);
} finally {
hideLoading('loading');
}
}
async function analyzePrice() {
const url = document.getElementById('analysis-url').value;
const days = document.getElementById('time-range').value;
if (!url) {
showError('analysis-result', 'Please enter a product URL');
return;
}
showLoading('analysis-loading');
hideError('analysis-result');
try {
const response = await fetch(`${API_BASE}/analysis/${encodeURIComponent(url)}?days=${days}`);
const data = await response.json();
if (data.success) {
displayAnalysisResult(data.data);
} else {
showError('analysis-result', data.error || 'Failed to analyze price trends');
}
} catch (error) {
showError('analysis-result', 'Network error: ' + error.message);
} finally {
hideLoading('analysis-loading');
}
}
function displayScrapeResult(data) {
const resultDiv = document.getElementById('scrape-result');
let html = '<div class="result"><h3>Current Price Data</h3>';
html += `<p><strong>URL:</strong> ${data.url}</p>`;
html += `<p><strong>Scraped at:</strong> ${new Date(data.timestamp).toLocaleString()}</p>`;
if (data.prices.length > 0) {
html += '<