Используете контейнеры, чтобы... отслеживать цены на рождественские подарки?

· 9 мин чтения

Рождество не за горами, а вместе с ним и радостная задача найти идеальные подарки для близких. Если вы чем-то похожи на меня, вы, вероятно, любите заключать выгодные сделки, но ориентироваться в стремительно растущих ценах на популярные товары во время курортного сезона может показаться настоящей поездкой на санях — захватывающей, но немного ошеломляющей! В этом году вместо того, чтобы сводить себя с ума ежедневными проверками цен, я решил найти хорошее применение своим техническим навыкам и автоматизировать процесс.

Как разработчик программного обеспечения и человек, увлеченный использованием технологий для решения повседневных проблем, я нашел способ автоматизировать процесс проверки цен. Вместо того, чтобы каждый день вручную посещать несколько интернет-магазинов для сравнения цен, я решил создать систему, которая сможет сделать это за меня. Это не только экономит время, но и гарантирует, что я получу лучшие предложения без стресса от ежедневных проверок.

Зачем автоматизировать мониторинг цен?

Автоматизировать мониторинг цен — это все равно, что ваша собственная команда эльфов Санты работает круглосуточно, чтобы гарантировать, что вы получите лучшие предложения, не пошевелив и пальцем. Вот почему вам это понравится:

  1. Дорогие предметы. Мы все знаем, что рождественские подарки могут оказаться довольно дорогими, особенно если в вашем списке есть гаджеты и игрушки. Экономия на них может быть столь же приятной, как и поиск последней вершины дерева на складе.
  2. Ежедневные проверки. У кого есть время (или терпение) проверять цены каждый день? Не я!
  3. Автоматизация. Ощутите праздничный дух щедрости — подарите себе эффективность.
  4. Точность. Автоматизация гарантирует, что ваши проверки цен будут такими же точными, как у Рудольфа блестящий нос.

Основные технологии

Чтобы создать своего маленького помощника Санты, я решил использовать несколько ключевых технологий:

Контейнеры

Контейнеры — это своего рода рождественская упаковка для программного обеспечения. Они объединяют ваши приложения со всеми их вкусностями (зависимостями), так что все работает так же гладко, как поездка на санях по свежему снегу. Docker — наш инструмент для создания таких контейнеров.

Блазор

Blazor — это отличная платформа для создания интерактивных веб-приложений с использованием .NET. Это все равно что заменить обычные рождественские гимны собственным праздничным плейлистом — адаптированным, эффективным и очень веселым.

Docker-Compose

Docker-Compose — менеджер по эксплуатации нашего Северного полюса. Это помогает нам поддерживать идеальную совместную работу всех наших сервисов, таких как API и интерфейс Blazor. Думайте о нем как о дирижере нашей праздничной симфонии.

Пошаговое руководство

А теперь давайте окунемся в мастерскую Санты и воплотим этот проект в жизнь!

Шаг 1. Создание API

1.1 Настройка проекта

Наденьте шляпу Санты-кодировщика и настройте новый проект веб-API ASP.NET Core. Откройте терминал (это немного похоже на открытие адвент-календаря) и запустите:

[[[ТОК_1]]]

Эта команда создает новый каталог с именем PriceMonitorApi и настраивает базовый проект веб-API. Представьте, что это похоже на создание прочной основы для пряничного домика.

1.2 Добавление HttpClient и библиотек парсингаЗатем добавьте HttpClient и библиотеку для анализа HTML. Это будут наши верные сани для получения и чтения данных о ценах.

[[[ТОК_4]]]

HtmlAgilityPack — это эльф, который помогает нам анализировать HTML-документы.

1.3 Создание моделей

Модели — это как чертежи наших игрушек. Давайте создадим модель для представления информации о продукте:

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

Мы только что создали идеальный шаблон для наших данных.

1.4 Реализация службы парсера

Наш парсерный сервис похож на маленького помощника Санты: он собирает и обрабатывает для нас информацию:

[[[ТОК_6]]]

Этот фрагмент превращает наш HtmlAgilityPack в праздничную волшебную палочку.

1.5 Создание API-контроллера

Давайте создадим контроллер, который будет действовать как привратник наших данных:

[[[ТОК_8]]]

Наш контроллер обеспечивает доставку данных быстрее, чем Санта в дымоход.

1.6 Регистрация сервисов в DI-контейнере

Наконец, зарегистрируйте свой ScraperService, чтобы он был доступен при необходимости.

[[[ТОК_10]]]

Теперь ваш API готов — как сани Санты в канун Рождества!


Шаг 2. Создание приложения Blazor

Blazor помогает нам украсить наш проект как рождественскую елку, сделав его визуально привлекательным и интерактивным.

2.1 Настройка проекта Blazor

Далее мы создадим проект Blazor, который будет служить интерфейсом для нашей поездки на санях для мониторинга цен.

[[[ТОК_11]]]

Эта команда добавляет немного праздничного волшебства для настройки базового проекта Blazor WebAssembly.

2.2 Добавление моделей

Так же, как и при настройке орнаментов, добавьте модель ProductInfo в проект Blazor:

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

2.3 Создание службы для вызовов API

Создайте сервис для получения данных из нашего API — думайте о нем как о нашем приятеле по онлайн-шоппингу:

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 Регистрация служб в DI-контейнере

Убедитесь, что ScraperService зарегистрирован, чтобы мы могли внедрить его в наши компоненты.

builder.Services.AddScoped<ScraperService>();

2.5 Создание компонента Blazor

Обновите Pages/Index.razor для включения интересного и интерактивного интерфейса:

@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);
        }
    }
}

Это похоже на праздничную презентацию — сделать наше приложение интерактивным и восхитительным.


Шаг 3. Соединяем все с помощью Docker-Compose

Теперь давайте соединим все с помощью Docker-Compose, превратив наш проект в отлаженную поездку на санях.

3.1 Создание Dockerfiles

Создайте файлы Dockerfile для проектов API и 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 Создание docker-compose.yml

Соедините все точки (рождественские гирлянды) одним файлом 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

Диаграммы

Ниже приведены диаграммы, помогающие визуализировать различные компоненты и поток данных. Это поможет нам понять, что на самом деле происходит в нашем мире Санты:

Схема архитектуры системы

Изображение

Схема потока данных

Изображение

Диаграмма последовательности

Изображение

Схема компонентов для настройки Docker

Изображение

Шаг 4. Управление URL-адресами, обновление и автоматическое периодическое обновлениеВ этой демонстрации мы просто сохраним его в памяти, но в реальном приложении вы будете использовать базу данных. В этом забавном проекте давайте остановимся на хранении в памяти.

4.1 Изменение службы для управления URL-адресами

Во-первых, мы убедимся, что наш сервис может обрабатывать добавление и получение URL-адресов, а также получение информации о продукте:

Обновление 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 Обновление компонента Blazor для управления URL-адресами и обновления

Затем обновите Pages/Index.razor, чтобы добавить управление URL-адресами, обновление и автоматическое периодическое обновление:

@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();
    }
}

В этом обновленном компоненте:

  • Мы используем Timer для периодического вызова метода RefreshPrices каждую минуту.
  • Метод StartTimer инициализирует таймер, который запускается немедленно, а затем срабатывает каждые 60 секунд.
  • Метод жизненного цикла OnInitialized вызывает StartTimer при инициализации компонента для запуска периодического обновления.

4.3 Запуск решения

Чтобы запустить обновленное приложение Blazor с новыми функциями, перестройте и перезапустите контейнеры Docker:

docker-compose build
docker-compose up

После загрузки http://localhost:5001 в браузере приложение Blazor теперь должно автоматически обновлять цены на продукты каждую минуту, а также разрешать ручное обновление и управление URL-адресами.

Заключение

Создание этой системы мониторинга цен было настоящим праздником! Это не только избавило меня от стресса, связанного с ежедневными проверками цен, но и продемонстрировало волшебство современных веб-технологий.

Праздничный технический календарь на 2024 год

Я создал этот пост в рамках мероприятия Festive Tech Calendar 2024, которое объединяет энтузиастов технологий, новаторов и цифровых мечтателей, чтобы поделиться знаниями и отпраздновать слияние праздничного духа и технологических чудес. Эта инициатива направлена не только на обучение и общение, но и на отдачу.

Праздничный технологический календарь на 2024 год в этом году поддерживает благотворительную организацию Beatson Cancer Charity. Благотворительная организация Beatson Cancer Charity занимается поддержкой людей, страдающих раком, их семей и медицинских работников, которые о них заботятся. Более подробную информацию об их невероятной работе можно найти на сайте https://www.beatsoncancercharity.org/.

Посетите веб-сайт Festive Tech Calendar по адресу https://festivetechcalendar.com, где можно найти часто задаваемые вопросы и получить более подробную информацию о мероприятии.

ХО ХО ХО!

Создание этого проекта стало прекрасным способом внести свой вклад в праздничное технологическое сообщество и в то же время поддержать великое дело.

Я надеюсь, что это руководство оказалось для вас полезным и вдохновило вас на изучение новых способов использования технологий для упрощения повседневных задач.

Если у вас есть какие-либо вопросы или вам нужна дополнительная помощь, пожалуйста, не стесняйтесь обращаться к нам.

Приятного кодирования!