Utiliser des conteneurs pour... suivre les prix des cadeaux de Noël ?

· 11 min de lecture

Noël approche à grands pas et avec lui vient la joyeuse tâche de trouver les cadeaux parfaits pour vos proches. Si vous êtes comme moi, vous aimez probablement faire de bonnes affaires, mais naviguer dans la montée en flèche des prix des articles populaires pendant la période des fêtes peut ressembler à une véritable promenade en traîneau : excitant mais un peu écrasant ! Cette année, au lieu de me rendre fou avec des contrôles de prix quotidiens, j’ai décidé de mettre à profit mes compétences techniques et d’automatiser le processus.

En tant que développeur de logiciels et passionné par l’utilisation de la technologie pour résoudre les problèmes quotidiens, j’ai trouvé un moyen d’automatiser le processus de vérification des prix. Au lieu de visiter manuellement plusieurs boutiques en ligne chaque jour pour comparer les prix, j’ai décidé de créer un système capable de le faire à ma place. Cela me fait non seulement gagner du temps, mais garantit également que j’obtiens les meilleures offres sans le stress des contrôles quotidiens.

Pourquoi automatiser la surveillance des prix ?

Automatiser la surveillance des prix, c’est comme avoir votre propre équipe de lutins du Père Noël travaillant 24 heures sur 24 pour vous assurer d’obtenir les meilleures offres sans lever le petit doigt. Voici pourquoi vous allez l’adorer :

  1. Articles coûteux : Nous savons tous que les cadeaux de Noël peuvent coûter assez cher, surtout lorsque les gadgets et les jouets figurent sur votre liste. Économiser de l’argent sur ceux-ci peut être aussi satisfaisant que de trouver la dernière cime d’arbre en stock.
  2. Contrôles quotidiens : Qui a le temps (ou la patience) de vérifier les prix chaque jour ? Pas moi !
  3. Automatisation : Adoptez l’esprit de générosité des Fêtes : offrez-vous le cadeau de l’efficacité.
  4. Précision : l’automatisation garantit que vos contrôles de prix sont aussi précis que le nez de Rudolph est brillant.

Technologies de base

Pour construire mon propre petit assistant du Père Noël, j’ai décidé d’utiliser plusieurs technologies clés :

Conteneurs

Les conteneurs sont comme l’emballage de Noël des logiciels. Ils regroupent vos applications avec tous leurs goodies (dépendances) pour que tout se déroule aussi bien qu’une promenade en traîneau dans la neige fraîche. Docker est notre référence pour créer ces conteneurs.

Blazeur

Blazor est un framework sympa pour créer des applications Web interactives à l’aide de .NET. C’est comme remplacer les chants de Noël génériques par votre propre playlist de vacances : sur mesure, efficace et tellement amusante.

Docker-Compose

Docker-Compose est le gestionnaire des opérations de notre pôle Nord. Cela nous aide à maintenir tous nos services, comme l’API et le front-end Blazor, fonctionnant ensemble en parfaite harmonie. Considérez-le comme le chef d’orchestre de notre symphonie des Fêtes.

Guide étape par étape

Maintenant, plongeons dans l’atelier du Père Noël et donnons vie à ce projet !

Étape 1 : Création de l’API

1.1 Mise en place du projet

Enfilez votre chapeau de Père Noël de codage et configurez un nouveau projet d’API Web ASP.NET Core. Ouvrez votre terminal (c’est un peu comme ouvrir votre calendrier de l’avent) et exécutez :

dotnet new webapi -o PriceMonitorApi
cd PriceMonitorApi

Cette commande crée un nouveau répertoire nommé PriceMonitorApi et configure un projet d’API Web de base. Imaginez que c’est comme créer une base solide pour votre maison en pain d’épice.

1.2 Ajout de HttpClient et de bibliothèques ScrapingEnsuite, ajoutez HttpClient et une bibliothèque pour analyser le HTML. Ce sera notre fidèle traîneau pour récupérer et lire les données sur les prix.

dotnet add package HtmlAgilityPack

HtmlAgilityPack est l’elfe qui nous aide à analyser les documents HTML.

1.3 Création de modèles

Les modèles sont comme le modèle de nos jouets. Créons un modèle pour représenter les informations sur le produit :

namespace PriceMonitorApi.Models
{
    public class ProductInfo
    {
        public string Title { get; set; }
        public decimal Price { get; set; }
        public DateTime Date { get; set; }
    }
}

Nous venons de créer le modèle parfait pour nos données.

1.4 Implémentation du service Scraper

Notre service Scraper est comme le petit assistant du Père Noël : il récupère et traite les informations pour nous :

using HtmlAgilityPack;
using PriceMonitorApi.Models;
using System.Globalization;

namespace PriceMonitorApi.Services
{
    public class ScraperService
    {
        private readonly HttpClient _httpClient;

        public ScraperService(HttpClient httpClient)
        {
            _httpClient = httpClient;
        }

        public async Task<ProductInfo> ScrapeProductInfoAsync(string url)
        {
            var response = await _httpClient.GetStringAsync(url);

            var htmlDoc = new HtmlDocument();
            htmlDoc.LoadHtml(response);

            var title = htmlDoc.DocumentNode.SelectSingleNode("//h1[@class='product-title']").InnerText.Trim();
            var priceString = htmlDoc.DocumentNode.SelectSingleNode("//span[@class='product-price']").InnerText.Trim();

            if (decimal.TryParse(priceString, NumberStyles.Currency, CultureInfo.InvariantCulture, out var price))
            {
                return new ProductInfo
                {
                    Title = title,
                    Price = price,
                    Date = DateTime.UtcNow
                };
            }

            throw new Exception("Unable to parse price");
        }
    }
}

Cet extrait transforme notre HtmlAgilityPack en une baguette magique pour les fêtes.

1.5 Création du contrôleur API

Créons un contrôleur qui agira comme le gardien de nos données :

using Microsoft.AspNetCore.Mvc;
using PriceMonitorApi.Models;
using PriceMonitorApi.Services;

namespace PriceMonitorApi.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class ScraperController : ControllerBase
    {
        private readonly ScraperService _scraperService;

        public ScraperController(ScraperService scraperService)
        {
            _scraperService = scraperService;
        }

        [HttpGet("productinfo")]
        public async Task<ActionResult<ProductInfo>> GetProductInfo(string url)
        {
            try
            {
                var productInfo = await _scraperService.ScrapeProductInfoAsync(url);
                return Ok(productInfo);
            }
            catch (Exception ex)
            {
                return BadRequest(new { Message = ex.Message });
            }
        }
    }
}

Notre contrôleur garantit que les données sont transmises plus rapidement que le Père Noël dans la cheminée.

1.6 Enregistrement des services dans le conteneur DI

Enfin, enregistrez votre ScraperService pour vous assurer qu’il est disponible en cas de besoin.

services.AddHttpClient<ScraperService>();

Votre API est désormais prête, comme le traîneau du Père Noël la veille de Noël !


Étape 2 : Création de l’application Blazor

Blazor nous aide à décorer notre projet comme un sapin de Noël, le rendant visuellement attrayant et interactif.

2.1 Mise en place du projet Blazor

Ensuite, nous créerons un projet Blazor qui servira d’interface pour notre promenade en traîneau de surveillance des prix.

dotnet new blazor -o PriceMonitorBlazor
cd PriceMonitorBlazor

Cette commande apporte un peu de magie des fêtes pour mettre en place un projet Blazor WebAssembly de base.

2.2 Ajout de modèles

Tout comme pour configurer des ornements, ajoutez un modèle ProductInfo dans le projet Blazor :

namespace PriceMonitorBlazor.Models
{
    public class ProductInfo
    {
        public string Title { get; set; }
        public decimal Price { get; set; }
        public DateTime Date { get; set; }
    }
}

2.3 Création du service pour les appels API

Créez un service pour récupérer les données de notre API. Considérez-le comme notre partenaire d’achat en ligne :

using PriceMonitorBlazor.Models;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;

namespace PriceMonitorBlazor.Services
{
    public class ScraperService
    {
        private readonly HttpClient _httpClient;

        public ScraperService(HttpClient httpClient)
        {
            _httpClient = httpClient;
        }

        public async Task<ProductInfo> GetProductInfoAsync(string url)
        {
            var response = await _httpClient.GetFromJsonAsync<ProductInfo>($"https://localhost:5001/api/scraper/productinfo?url={url}");
            return response;
        }
    }
}

2.4 Enregistrement des services dans le conteneur DI

Assurez-vous que ScraperService est enregistré afin que nous puissions l’injecter dans nos composants.

builder.Services.AddScoped<ScraperService>();

2.5 Création du composant Blazor

Mettez à jour Pages/Index.razor pour inclure une interface amusante et interactive :

@page "/"
@using PriceMonitorBlazor.Models
@using PriceMonitorBlazor.Services
@inject ScraperService ScraperService

<h3>Price Monitor</h3>

<p>
    <label for="urlInput">Product URL:</label>
    <input id="urlInput" @bind="productUrl" />
    <button @onclick="FetchProductInfo">Fetch Price</button>
</p>

@if (productInfos.Any())
{
    <table class="table">
        <thead>
            <tr>
                <th>Title</th>
                <th>Price</th>
                <th>Date</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var product in productInfos)
            {
                <tr>
                    <td>@product.Title</td>
                    <td>@product.Price</td>
                    <td>@product.Date</td>
                </tr>
            }
        </tbody>
    </table>
}

@code {
    private string productUrl;
    private List<ProductInfo> productInfos = new List<ProductInfo>();

    private async Task FetchProductInfo()
    {
        if (!string.IsNullOrEmpty(productUrl))
        {
            var productInfo = await ScraperService.GetProductInfoAsync(productUrl);
            productInfos.Add(productInfo);
        }
    }
}

C’est comme organiser une exposition festive pour les fêtes, rendant notre application interactive et agréable.


Étape 3 : Tout connecter à l’aide de Docker-Compose

Connectons maintenant le tout à l’aide de Docker-Compose, transformant ainsi notre projet en une balade en traîneau bien huilée.

3.1 Création de fichiers Docker

Créez des Dockerfiles pour les projets API et Blazor :

PriceMonitorApi/Dockerfile :

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["PriceMonitorApi/PriceMonitorApi.csproj", "PriceMonitorApi/"]
RUN dotnet restore "PriceMonitorApi/PriceMonitorApi.csproj"
COPY . .
WORKDIR "/src/PriceMonitorApi"
RUN dotnet build "PriceMonitorApi.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "PriceMonitorApi.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "PriceMonitorApi.dll"]

PriceMonitorBlazor/Dockerfile :

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["PriceMonitorBlazor/PriceMonitorBlazor.csproj", "PriceMonitorBlazor/"]
RUN dotnet restore "PriceMonitorBlazor/PriceMonitorBlazor.csproj"
COPY . .
WORKDIR "/src/PriceMonitorBlazor"
RUN dotnet build "PriceMonitorBlazor.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "PriceMonitorBlazor.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "PriceMonitorBlazor.dll"]

3.2 Création de docker-compose.yml

Connectez tous les points (lumières de Noël) avec un seul fichier docker-compose.yml :

version: '3.4'

services:
  api:
    image: pricemonitorapi
    build:
      context: ./PriceMonitorApi
      dockerfile: Dockerfile
    ports:
      - "5000:80"
    networks:
      - price-monitor-network

  blazor:
    image: pricemonitorblazor
    build:
      context: ./PriceMonitorBlazor
      dockerfile: Dockerfile
    ports:
      - "5001:80"
    depends_on:
      - api
    networks:
      - price-monitor-network

networks:
  price-monitor-network:
    driver: bridge

Diagrammes

Vous trouverez ci-dessous les diagrammes pour nous aider à visualiser les différents composants et le flux de données, cela nous aidera à comprendre ce qui se passe réellement dans notre monde du Père Noël :

Schéma d’architecture du système

Imgur

Diagramme de flux de données

Imgur

Diagramme de séquence

Imgur

Diagramme des composants pour la configuration de Docker

Imgur

Étape 4 : Gestion des URL, actualisation et actualisation périodique automatiséeDans cette démo, nous allons simplement le stocker en mémoire, mais dans une application réelle, vous utiliseriez une base de données. Pour ce projet amusant, restons-en au stockage en mémoire.

4.1 Modification du service pour gérer les URL

Tout d’abord, nous veillerons à ce que notre service puisse gérer l’ajout et la récupération d’URL, ainsi que la récupération d’informations sur les produits :

Mettre à jour Services/ScraperService.cs :

using PriceMonitorBlazor.Models;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;

namespace PriceMonitorBlazor.Services
{
    public class ScraperService
    {
        private readonly HttpClient _httpClient;
        private List<string> urls = new List<string>();

        public ScraperService(HttpClient httpClient)
        {
            _httpClient = httpClient;
        }

        public async Task<ProductInfo> GetProductInfoAsync(string url)
        {
            var response = await _httpClient.GetFromJsonAsync<ProductInfo>($"http://localhost:5000/api/scraper/productinfo?url={url}");
            return response;
        }

        public void AddUrl(string url)
        {
            if (!urls.Contains(url))
            {
                urls.Add(url);
            }
        }

        public List<string> GetUrls()
        {
            return urls;
        }

        public void ClearUrls()
        {
            urls.Clear();
        }
    }
}

4.2 Mise à jour du composant Blazor pour la gestion et l’actualisation des URL

Ensuite, mettez à jour Pages/Index.razor pour ajouter la gestion des URL, l’actualisation et l’actualisation périodique automatisée :

@page "/"
@using PriceMonitorBlazor.Models
@using PriceMonitorBlazor.Services
@inject ScraperService ScraperService

<h3>Price Monitor</h3>

<p>
    <label for="urlInput">Product URL:</label>
    <input id="urlInput" @bind="productUrl" />
    <button @onclick="AddUrl">Add URL</button>
</p>

<p>
    <button @onclick="RefreshPrices">Refresh All Prices</button>
</p>

@if (productInfos.Any())
{
    <table class="table">
        <thead>
            <tr>
                <th>Title</th>
                <th>Price</th>
                <th>Date</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var product in productInfos)
            {
                <tr>
                    <td>@product.Title</td>
                    <td>@product.Price</td>
                    <td>@product.Date</td>
                </tr>
            }
        </tbody>
    </table>
}

@code {
    private string productUrl;
    private List<ProductInfo> productInfos = new List<ProductInfo>();
    private Timer _timer;

    protected override void OnInitialized()
    {
        StartTimer();
    }

    private void StartTimer()
    {
        // Set up the timer to call RefreshPrices every minute (60000 ms)
        _timer = new Timer(async _ => await InvokeAsync(RefreshPrices), null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
    }

    private void AddUrl()
    {
        if (!string.IsNullOrEmpty(productUrl))
        {
            ScraperService.AddUrl(productUrl);
            productUrl = string.Empty;
        }
    }

    private async Task RefreshPrices()
    {
        productInfos.Clear();
        var urls = ScraperService.GetUrls();
        
        foreach (var url in urls)
        {
            var productInfo = await ScraperService.GetProductInfoAsync(url);
            productInfos.Add(productInfo);
        }
        StateHasChanged();
    }
}

Dans ce composant mis à jour :

  • Nous utilisons un Timer pour appeler périodiquement la méthode RefreshPrices toutes les minutes.
  • La méthode StartTimer initialise le timer pour qu’il démarre immédiatement puis se déclenche toutes les 60 secondes.
  • La méthode de cycle de vie OnInitialized appelle StartTimer lorsque le composant est initialisé pour démarrer l’actualisation périodique.

4.3 Exécuter la solution

Pour exécuter l’application Blazor mise à jour avec les nouvelles fonctionnalités, reconstruisez et redémarrez vos conteneurs Docker :

docker-compose build
docker-compose up

Après avoir chargé http://localhost:5001 dans votre navigateur, l’application Blazor devrait désormais actualiser automatiquement les prix des produits toutes les minutes en plus de permettre les actualisations manuelles et la gestion des URL.

Conclusion

Construire ce système de surveillance des prix a été une explosion de joie ! Non seulement cela m’a évité le stress des vérifications quotidiennes des prix, mais cela a également mis en valeur la magie des technologies Web modernes.

Calendrier Tech Festif 2024

J’ai créé cet article dans le cadre de l’événement Festive Tech Calendar 2024, qui rassemble des passionnés de technologie, des innovateurs et des rêveurs numériques pour partager leurs connaissances et célébrer la fusion de l’esprit festif et des merveilles technologiques. Cette initiative ne vise pas seulement à apprendre et à se connecter, mais aussi à redonner.

Festive Tech Calendar 2024 soutient cette année la Beatson Cancer Charity. La Beatson Cancer Charity se consacre à soutenir les personnes touchées par le cancer, leurs familles et les professionnels de la santé qui s’en occupent. Plus d’informations sur leur incroyable travail peuvent être trouvées sur https://www.beatsoncancercharity.org/.

Consultez le site Web du calendrier technique festif à l’adresse https://festivetechcalendar.com pour consulter les questions fréquemment posées et plus de détails sur l’événement.

HO HO HO!

Créer ce projet a été une merveilleuse façon de contribuer à la communauté technologique festive et de soutenir en même temps une grande cause.

J’espère que vous avez trouvé ce guide utile et qu’il vous incitera à explorer d’autres façons d’utiliser la technologie pour simplifier les tâches quotidiennes.

Si vous avez des questions ou avez besoin d’aide supplémentaire, n’hésitez pas à nous contacter.

Bon codage !