DEV Community

Vijay Vinoth
Vijay Vinoth

Posted on Originally published at artificial-inteligence.phptutorial.co.in

Automated Web Scraping and Data Visualization with Python and AI — Part 6: Integrating AI Models for Predictive Analytics and In

Automated Web Scraping and Data Visualization with Python and AI — Part 6: Integrating AI Models for Predictive Analytics and Insights

Based on my technical understanding as a Lead Programmer Analyst (PHP, Perl, Python, Shell) and on the latest 2026 AI‑driven data‑pipeline trends, this deep‑dive shows you how to turn the raw tables you harvested in the earlier parts into forward‑looking predictions and visual stories.

Quick recap (Parts 1‑5): We started by designing a robust scraper (BeautifulSoup + Playwright), added error‑handling and rotating proxies (Part 2), stored the results in PostgreSQL (Part 3), visualized static charts with Matplotlib and Seaborn (Part 4), and finally wrapped everything into a Docker‑compose stack with CI/CD (Part 5). Now we close the loop by feeding that clean data into AI models, extracting insights, and visualizing the forecasts.

Why AI‑Powered Predictive Analytics?

  • Speed. Modern LLM‑backed feature engineers (Claude 3.5 Sonnet, GPT‑4.5 Turbo) can suggest transformations in milliseconds, cutting the traditional data‑science “feature‑selection” phase by up to 70 %.
  • Adaptability. With parallel‑agent architectures you can spin up a dedicated “trend‑detector” agent, a “seasonality‑model” agent, and a “business‑rule” agent, all communicating via lightweight websockets or Redis streams.
  • Explainability. Prompt‑driven LLMs can generate natural‑language explanations of model outputs, turning a black‑box forecast into a stakeholder‑ready narrative.

Architecture Overview

Component
Technology (2026)
Responsibility


Web Scraper
Playwright + BrightData GPT‑Vision (image‑aware extraction)
Collect raw HTML/visual tables from target sites


Data Lake
PostgreSQL + MinIO (S3‑compatible)
Persist raw and cleaned data for downstream jobs


Feature‑Engineering Agent
Claude 3.5 Sonnet (Agentic Workflow)
Suggest & execute feature transformations via Python scripts


Model Training Agent
GPT‑4.5 Turbo (parallel inference)
Run LightGBM, Prophet, or PyTorch models in parallel containers


Insight Generator
OpenAI Function‑Calling + LangChain
Translate model predictions into human‑readable insights


Visualization Layer
Plotly Dash + Streamlit
Interactive dashboards for business users
Enter fullscreen mode Exit fullscreen mode

Step 1 – Pull the Cleaned Dataset

All previous parts stored the final tidy DataFrame in a PostgreSQL table called public.sales_daily. The following snippet uses SQLAlchemy and pandas to load the data into memory.

import os
import pandas as pd
from sqlalchemy import create_engine
from dotenv import load_dotenv

load_dotenv()  # .env contains DB credentials

DB_URL = (
    f"postgresql+psycopg2://{os.getenv('DB_USER')}:"
    f"{os.getenv('DB_PASS')}@{os.getenv('DB_HOST')}:"
    f"{os.getenv('DB_PORT')}/{os.getenv('DB_NAME')}"
)

engine = create_engine(DB_URL)
query = """
    SELECT
        date,
        product_id,
        region,
        units_sold,
        revenue,
        discount_pct
    FROM public.sales_daily
    WHERE date >= CURRENT_DATE - INTERVAL '180 days'
    ORDER BY date;
"""
df = pd.read_sql(query, engine)
df['date'] = pd.to_datetime(df['date'])
print(df.head())

Enter fullscreen mode Exit fullscreen mode

Step 2 – Let Claude 3.5 Sonnet Suggest Features

Claude’s Agentic Workflow lets you feed a prompt, get a JSON‑structured plan, and then execute it automatically. We’ll use the anthropic Python SDK (v0.6.0) to spin up a feature‑engineer agent that proposes transformations such as lag features, rolling averages, and holiday flags.

import anthropic
import json
from pathlib import Path

client = anthropic.Anthropic(api_key=os.getenv('ANTHROPIC_API_KEY'))

prompt = f"""
You are a data‑science assistant. The DataFrame `df` has columns:
- date (datetime)
- product_id (int)
- region (str)
- units_sold (int)
- revenue (float)
- discount_pct (float)

Suggest a JSON list of feature engineering steps that would improve a time‑series forecast of `units_sold`. Include:
1. Lag features (e.g., units_sold_lag_7)
2. Rolling statistics (e.g., revenue_rolling_14)
3. Categorical encodings (e.g., region_one_hot)
4. Holiday flag (use US federal holidays)

Return ONLY the JSON, no prose.
"""

response = client.messages.create(
    model="claude-3-5-sonnet-20240620",
    max_tokens=800,
    temperature=0.0,
    messages=[{"role": "user", "content": prompt}]
)

# Extract JSON from the response
raw_json = response.content[0].text.strip()
features_plan = json.loads(raw_json)
print(json.dumps(features_plan, indent=2))

Enter fullscreen mode Exit fullscreen mode

Typical output (truncated for brevity):

[
  {"type":"lag","column":"units_sold","lag":7},
  {"type":"lag","column":"units_sold","lag":30},
  {"type":"rolling","column":"revenue","window":14,"agg":"mean"},
  {"type":"rolling","column":"discount_pct","window":7,"agg":"max"},
  {"type":"one_hot","column":"region"},
  {"type":"holiday_flag","country":"US","column":"date"}
]

Enter fullscreen mode Exit fullscreen mode

Step 3 – Execute the Feature Plan

We translate the JSON plan into concrete pandas operations. This block is deliberately modular so you can run it inside a Docker container that the parallel‑agent scheduler spawns.

import pandas as pd
import holidays
from sklearn.preprocessing import OneHotEncoder

def apply_feature_plan(df: pd.DataFrame, plan: list) -> pd.DataFrame:
    df = df.copy()
    for step in plan:
        if step["type"] == "lag":
            lag = step["lag"]
            col = step["column"]
            df[f"{col}_lag_{lag}"] = df.groupby('product_id')[col].shift(lag)
        elif step["type"] == "rolling":
            win = step["window"]
            col = step["column"]
            agg = step["agg"]
            rolled = df.groupby('product_id')[col].transform(
                lambda s: s.rolling(window=win, min_periods=1).agg(agg)
            )
            df[f"{col}_rolling_{win}"] = rolled
        elif step["type"] == "one_hot":
            encoder = OneHotEncoder(sparse=False, drop='first')
            ohe = encoder.fit_transform(df[[step["column"]]])
            ohe_cols = encoder.get_feature_names_out([step["column"]])
            df[ohe_cols] = ohe
            df.drop(columns=[step["column"]], inplace=True)
        elif step["type"] == "holiday_flag":
            us_holidays = holidays.US()
            df["is_holiday"] = df[step["column"]].apply(lambda d: d in us_holidays)
    return df

engineered_df = apply_feature_plan(df, features_plan)
print(engineered_df.head())

Enter fullscreen mode Exit fullscreen mode

Step 4 – Parallel Model Training with GPT‑4.5 Turbo

OpenAI’s Turbo Parallel Agents (released Q2 2026) let you dispatch several model‑training jobs simultaneously and then aggregate results. In our example we’ll train three lightweight models:

  • Prophet (seasonality‑aware)
  • LightGBM (gradient‑boosted trees)
  • Temporal Fusion Transformer (TFT) via PyTorch

Each model runs in its own container; a tiny orchestration script collects the MAE (Mean Absolute Error) and selects the best performer.

import subprocess, json, os, time
from pathlib import Path

# Directory layout:
# /models/prophet/train.py
# /models/lightgbm/train.py
# /models/tft/train.py
BASE_DIR = Path(__file__).parent

def launch_agent(script_path: Path, model_name: str):
    """Spawn a subprocess that runs the training script.
    The script must write a JSON file `metrics.json` to its cwd."""
    env = os.environ.copy()
    env["MODEL_NAME"] = model_name
    proc = subprocess.Popen(
        ["python", str(script_path)],
        cwd=script_path.parent,
        env=env,
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        text=True
    )
    return proc

agents = {
    "prophet": launch_agent(BASE_DIR / "models/prophet/train.py", "prophet"),
    "lightgbm": launch_agent(BASE_DIR / "models/lightgbm/train.py", "lightgbm"),
    "tft": launch_agent(BASE_DIR / "models/tft/train.py", "tft")
}

# Simple poll loop (in production you'd use Celery/Redis)
results = {}
while agents:
    for name, proc in list(agents.items()):
        if proc.poll() is not None:  # finished
            out, _ = proc.communicate()
            print(f"--- {name} output ---")
            print(out)
            metrics_path = proc.cwd / "metrics.json"
            if metrics_path.exists():
                results[name] = json.load(metrics_path.open())
            else:
                results[name] = {"status":"failed"}
            del agents[name]
    time.sleep(2)

# Pick the best model based on MAE
best_model = min(results, key=lambda k: results[k].get("mae", float("inf")))
print(f"Best model: {best_model} with MAE={results[best_model]['mae']}")

Enter fullscreen mode Exit fullscreen mode

Below are the minimal training scripts for each agent. They all read the engineered CSV from a shared volume (/data/engineered.csv) and write metrics.json back to the same folder.

Prophet Trainer (models/prophet/train.py)

import pandas as pd
from prophet import Prophet
import json
from pathlib import Path

DATA_PATH = Path("/data/engineered.csv")
df = pd.read_csv(DATA_PATH, parse_dates=["date"])

# Prophet expects columns ds (date) and y (target)
prophet_df = df.rename(columns={"date":"ds", "units_sold":"y"})[["ds","y"]]

model = Prophet(yearly_seasonality=True, weekly_seasonality=True, daily_seasonality=False)
model.fit(prophet_df)

future = model.make_future_dataframe(periods=30)
forecast = model.predict(future)

# Simple MAE on the validation slice (last 30 days of historic)
val = forecast.tail(30)
actual = df.tail(30)["units_sold"].reset_index(drop=True)
mae = (val["yhat"].reset_index(drop=True) - actual).abs().mean()

metrics = {"mae": mae, "model":"prophet"}
Path("metrics.json").write_text(json.dumps(metrics))

Enter fullscreen mode Exit fullscreen mode

LightGBM Trainer (models/lightgbm/train.py)

import pandas as pd
import lightgbm as lgb
import json
from pathlib import Path
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error

df = pd.read_csv("/data/engineered.csv", parse_dates=["date"])
X = df.drop(columns=["units_sold", "date"])
y = df["units_sold"]

X_train, X_val, y_train, y_val = train_test_split(
    X, y, test_size=0.2, shuffle=False  # preserve temporal order
)

train_set = lgb.Dataset(X_train, label=y_train)
val_set = lgb.Dataset(X_val, label=y_val, reference=train_set)

params = {
    "objective": "regression",
    "metric": "mae",
    "learning_rate": 0.05,
    "num_leaves": 31,
    "verbosity": -1,
}

gbm = lgb.train(params, train_set, valid_sets=[val_set], early_stopping_rounds=30, verbose_eval=False)

preds = gbm.predict(X_val, num_iteration=gbm.best_iteration)
mae = mean_absolute_error(y_val, preds)

metrics = {"mae": mae, "model":"lightgbm"}
Path("metrics.json").write_text(json.dumps(metrics))

Enter fullscreen mode Exit fullscreen mode

Temporal Fusion Transformer (TFT) Trainer (models/tft/train.py)

import pandas as pd
import torch
import json
from pathlib import Path
from pytorch_forecasting import TimeSeriesDataSet, TemporalFusionTransformer, Baseline
from pytorch_forecasting.metrics import MAE

df = pd.read_csv("/data/engineered.csv", parse_dates=["date"])
df["time_idx"] = (df["date"] - df["date"].min()).dt.days

max_encoder_length = 30
max_prediction_length = 7

training = TimeSeriesDataSet(
    df,
    time_idx="time_idx",
    target="units_sold",
    group_ids=["product_id"],
    max_encoder_length=max_encoder_length,
    max_prediction_length=max_prediction_length,
    static_categoricals=["region"],
    static_reals=[],
    time_varying_known_categoricals=[],
    time_varying_known_reals=["revenue", "discount_pct", "is_holiday"],
    time_varying_unknown_categoricals=[],
    time_varying_unknown_reals=["units_sold"]
)

val = TimeSeriesDataSet.from_dataset(training, df, predict=True, stop_randomization=True)

train_loader = torch.utils.data.DataLoader(training, batch_size=64, shuffle=True)
val_loader = torch.utils.data.DataLoader(val, batch_size=64, shuffle=False)

tft = TemporalFusionTransformer.from_dataset(
    training,
    learning_rate=0.03,
    hidden_size=16,
    attention_head_size=1,
    dropout=0.1,
    loss=MAE(),
    optimizer="adam",
    reduce_on_plateau_patience=4,
)

trainer = torch.optim.Adam(tft.parameters(), lr=0.03)
epochs = 8
for epoch in range(epochs):
    tft.fit(train_loader, val_loader, max_epochs=1, verbose=False)

# Validation MAE
actuals = torch.cat([y for x, y in iter(val_loader)])
predictions = tft.predict(val_loader)
mae = MAE()(predictions, actuals).item()

metrics = {"mae": mae, "model":"tft"}
Path("metrics.json").write_text(json.dumps(metrics))

Enter fullscreen mode Exit fullscreen mode

Step 5 – Generate AI‑Driven Insights

Once the best model is identified (let’s say LightGBM), we can ask GPT‑4.5 Turbo to translate the raw forecast into a concise business narrative. The function‑calling feature ensures the output follows a predefined JSON schema, which our dashboard can consume directly.

import openai
import os
import json

openai.api_key = os.getenv("OPENAI_API_KEY")

# Load the 30‑day forecast produced by the best model
forecast_path = Path(f"/models/{best_model}/forecast.csv")
forecast_df = pd.read_csv(forecast_path)

prompt = f"""
You are an analytics consultant. Using the following forecast for `units_sold` (next 30 days) and the historical context (average daily sales = {df['units_sold'].mean():.1f}), write a short (max 150 words) executive summary that includes:

1. Expected growth/decline trend.
2. Any dates where sales exceed the 95th percentile of historic daily sales.
3. Recommendations for inventory or promotion actions.

Return the answer in JSON with fields: `summary`, `high_volume_dates`, `recommendations`.
"""

response = openai.ChatCompletion.create(
    model="gpt-4.5-turbo-202406",
    messages=[{"role":"user","content":prompt}],
    temperature=0.2,
    max_tokens=300,
    functions=[{
        "name":"insight_schema",
        "description":"Schema for predictive insight",
        "parameters":{
            "type":"object",
            "properties":{
                "summary":{"type":"string"},
                "high_volume_dates":{"type":"array","items":{"type":"string"}},
                "recommendations":{"type":"array","items":{"type":"string"}}
            },
            "required":["summary","high_volume_dates","recommendations"]
        }
    }],
    function_call={"name":"insight_schema"}
)

insight = response.choices[0].message.function_call.arguments
insight_json = json.loads(insight)
print(json.dumps(insight_json, indent=2))

Enter fullscreen mode Exit fullscreen mode

Sample output:

{
  "summary":"The model predicts a modest 3 % upward trend in daily units sold over the next month, driven primarily by the upcoming promotional week (June 12‑18). Sales are expected to peak on June 15, crossing the historic 95th‑percentile threshold.",
  "high_volume_dates":["2026-06-15"],
  "recommendations":[
    "Increase inventory for product #42 in the West region before June 14.",
    "Launch a targeted discount on June 12‑14 to smooth demand spikes."
  ]
}

Enter fullscreen mode Exit fullscreen mode

Step 6 – Interactive Dashboard with Plotly Dash

Finally, we embed the forecast and AI‑generated narrative into a lightweight Dash app. The dashboard pulls the latest CSV, renders a line chart with confidence bands, and shows the natural‑language summary beneath.

import dash
from dash import dcc, html, Input, Output
import plotly.express as px
import pandas as pd
import json

app = dash.Dash(name)

Load data once (in production you’d cache or stream)

forecast = pd.read_csv("/models/lightgbm/


Originally published at https://artificial-inteligence.phptutorial.co.in

Top comments (0)