Команда Go for Devs подготовила перевод пошагового руководства: как написать собственное CLI-приложение прогноза погоды на Go. Проект охватывает всё — от HTTP-запросов и парсинга JSON до удобного интерфейса командной строки. Отличная практика для новичков и хороший повод освежить базовые навыки тем, кто уже работает с Go.


Сегодня мы займёмся увлекательным проектом — напишем CLI-приложение прогноза погоды на Go. В этой статье я проведу вас через все шаги создания собственного консольного приложения для получения погоды. Независимо от того, только ли вы начинаете изучать Go или ищете интересный проект для практики — этот туториал вам подойдёт.

Мы разберём всё: от настройки проекта и написания кода до тестирования приложения. И обещаю — каждую строчку кода я объясню так, чтобы вы точно понимали, что происходит «под капотом».

Так что берите любимый напиток, открывайте редактор — и поехали писать код!

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

Прежде чем приступить к написанию кода, давайте определимся с целью. Мы хотим создать CLI-приложение на Go, которое будет получать данные о погоде с онлайн-API и показывать их в терминале. В качестве источника данных используем OpenWeatherMap. Если у вас ещё нет API-ключа, обязательно зарегистрируйтесь и получите бесплатный ключ.

Наше приложение будет выполнять следующие шаги:

  • Принимать на вход название города.

  • Обращаться к API прогноза погоды.

  • Разбирать JSON-ответ.

  • Отображать текущую информацию о погоде.

Этот проект прост, но при этом полезен. Следуя инструкциям, вы получите практический опыт работы с HTTP-запросами, парсингом JSON и созданием консольных интерфейсов в Go.

Понимание работы CLI-приложения прогноза погоды

Прежде чем переходить к коду, разберёмся, что именно будет делать наше CLI-приложение на Go:

  1. Ввод пользователя. Приложение будет запрашивать у пользователя название города или принимать его как аргумент командной строки.

  2. HTTP-запрос. С помощью встроенного пакета Go net/http мы отправим HTTP GET-запрос к API OpenWeatherMap.

  3. Парсинг JSON. После получения ответа мы преобразуем JSON-данные в структуры Go.

  4. Отображение данных. В конце красиво выведем детали прогноза погоды (температуру, влажность и описание) в терминал.

Шаг 1: Создание проекта и инициализация модулей

Для начала создадим папку проекта и инициализируем модуль Go.

Создайте новую директорию для проекта:

mkdir weather-cli-app
cd weather-cli-app

Инициализируйте модуль Go:

go mod init weather-cli-app

Эта команда создаст файл go.mod, в котором будут отслеживаться зависимости проекта. Теперь у нас есть чистая основа для создания CLI-приложения прогноза погоды на Go.

Шаг 2: Настройка API-клиента

Теперь, когда проект готов, нужно создать API-клиент для получения данных о погоде. Мы напишем простую функцию, которая будет отправлять HTTP GET-запрос к OpenWeatherMap.

Создайте новый файл weather.go:

touch weather.go

Добавьте следующий код в weather.go:

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "os"
)

// WeatherData — структура под JSON-ответ OpenWeatherMap.
type WeatherData struct {
    Name string `json:"name"`
    Main struct {
        Temp     float64 `json:"temp"`
        Humidity int     `json:"humidity"`
    } `json:"main"`
    Weather []struct {
        Description string `json:"description"`
    } `json:"weather"`
}

// getWeatherData получает данные о погоде для указанного города.
func getWeatherData(city string, apiKey string) (*WeatherData, error) {
    // Формируем URL API. Используем метрическую систему (градусы Цельсия).
    url := fmt.Sprintf("http://api.openweathermap.org/data/2.5/weather?q=%s&appid=%s&units=metric", city, apiKey)

    // Отправляем HTTP-запрос.
    resp, err := http.Get(url)
    if err != nil {
        return nil, fmt.Errorf("failed to make request: %v", err)
    }
    defer resp.Body.Close()

    // Проверяем, что запрос прошёл успешно.
    if resp.StatusCode != http.StatusOK {
        bodyBytes, _ := ioutil.ReadAll(resp.Body)
        return nil, fmt.Errorf("error: received status code %d: %s", resp.StatusCode, string(bodyBytes))
    }

    // Читаем тело ответа.
    bodyBytes, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return nil, fmt.Errorf("failed to read response: %v", err)
    }

    // Декодируем JSON в структуру WeatherData.
    var weather WeatherData
    err = json.Unmarshal(bodyBytes, &weather)
    if err != nil {
        return nil, fmt.Errorf("failed to decode JSON: %v", err)
    }

    return &weather, nil
}

Пояснение к коду:

  • Мы объявляем структуру WeatherData, которая соответствует JSON-ответу OpenWeatherMap. Это позволяет удобно декодировать данные из API.

  • Функция принимает название города и API-ключ: она собирает URL запроса, отправляет HTTP GET и обрабатывает ответ. В случае успеха JSON разбирается в структуру WeatherData.

Следуя этим шагам, вы получили рабочий компонент API-клиента для вашего CLI-приложения погоды на Go.

Шаг 3: Создаём интерфейс командной строки

Теперь настроим интерфейс командной строки. Пользователь сможет передать название города прямо при запуске приложения. Если этого не сделать, программа запросит ввод интерактивно.

Создайте файл main.go:

touch main.go

Добавьте следующий код в main.go:

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

func main() {
    // Читаем API-ключ из переменной окружения.
    apiKey := os.Getenv("OPENWEATHER_API_KEY")
    if apiKey == "" {
        fmt.Println("Error: Please set the OPENWEATHER_API_KEY environment variable.")
        os.Exit(1)
    }

    // Проверяем, передано ли название города в аргументах командной строки.
    var city string
    if len(os.Args) > 1 {
        city = strings.Join(os.Args[1:], " ")
    } else {
        // Запрашиваем у пользователя название города, если аргумент не указан.
        fmt.Print("Enter the city name: ")
        reader := bufio.NewReader(os.Stdin)
        input, err := reader.ReadString('\n')
        if err != nil {
            fmt.Println("Error reading input:", err)
            return
        }
        city = strings.TrimSpace(input)
    }

    // Получаем данные о погоде.
    weather, err := getWeatherData(city, apiKey)
    if err != nil {
        fmt.Println("Error fetching weather data:", err)
        return
    }

    // Отображаем данные о погоде.
    displayWeatherData(weather)
}

Пояснение к коду:

  • API-ключ считывается из переменной окружения OPENWEATHER_API_KEY. Такой способ позволяет безопасно хранить чувствительные данные.

  • Программа проверяет, указал ли пользователь название города в аргументах командной строки. Если нет, она предложит ввести его вручную.

  • После получения названия города вызывается функция getWeatherData, а затем данные передаются для отображения.

Этот фрагмент объединяет воедино интерфейс командной строки нашего CLI-приложения прогноза погоды на Go.

Шаг 4: Парсим и отображаем данные о погоде

Теперь нам нужна функция, которая красиво отформатирует и выведет данные о погоде. Она будет принимать данные, полученные от API, и печатать их в терминале.

В том же файле main.go добавьте следующую функцию:

// displayWeatherData выводит информацию о погоде в удобном для пользователя формате.
func displayWeatherData(weather *WeatherData) {
    fmt.Printf("\nWeather for %s:\n", weather.Name)
    if len(weather.Weather) > 0 {
        fmt.Printf("Description: %s\n", strings.Title(weather.Weather[0].Description))
    }
    fmt.Printf("Temperature: %.2f°C\n", weather.Main.Temp)
    fmt.Printf("Humidity: %d%%\n\n", weather.Main.Humidity)
}

Пояснение к коду:

  • Функция выводит название города, описание погоды, температуру и влажность. Для аккуратного форматирования используется fmt.Printf.

  • С помощью strings.Title описание погоды приводится к красивому виду с заглавной буквы.

Эта функция завершает наш проект CLI-приложения прогноза погоды на Go — теперь пользователь получает понятную и полезную информацию о погоде прямо в терминале.

Шаг 5: Тестирование и запуск приложения

Перед тем как завершить работу, давайте протестируем приложение. Вот как запустить ваше CLI-приложение погоды на Go:

Установите API-ключ в переменную окружения:

Для Linux/Mac:

export OPENWEATHER_API_KEY=your_api_key_here

Для Windows (Command Prompt):

set OPENWEATHER_API_KEY=your_api_key_here

Запустите приложение, передав название города:

go run main.go London

Если вы не передадите название города, приложение предложит ввести его вручную.

Вы должны увидеть что-то вроде этого:

Weather for London:
Description: Clear Sky
Temperature: 15.00°C
Humidity: 67%

Поздравляем! Вы успешно создали CLI-приложение погоды на Go. Теперь ваша программа умеет получать и отображать актуальную информацию о погоде по запросу пользователя.

Полный исходный код

Для удобства ниже приведён полный исходный код CLI-приложения погоды на Go:

Weather App
// main.go
package main

import (
    "bufio"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "os"
    "strings"
)

// WeatherData — структура под JSON-ответ OpenWeatherMap.
type WeatherData struct {
    Name string `json:"name"`
    Main struct {
        Temp     float64 `json:"temp"`
        Humidity int     `json:"humidity"`
    } `json:"main"`
    Weather []struct {
        Description string `json:"description"`
    } `json:"weather"`
}

// getWeatherData получает данные о погоде для указанного города.
func getWeatherData(city string, apiKey string) (*WeatherData, error) {
    // Формируем URL API. Используем метрическую систему (градусы Цельсия).
    url := fmt.Sprintf("http://api.openweathermap.org/data/2.5/weather?q=%s&appid=%s&units=metric", city, apiKey)

    // Отправляем HTTP-запрос.
    resp, err := http.Get(url)
    if err != nil {
        return nil, fmt.Errorf("failed to make request: %v", err)
    }
    defer resp.Body.Close()

    // Проверяем, что запрос прошёл успешно.
    if resp.StatusCode != http.StatusOK {
        bodyBytes, _ := ioutil.ReadAll(resp.Body)
        return nil, fmt.Errorf("error: received status code %d: %s", resp.StatusCode, string(bodyBytes))
    }

    // Читаем тело ответа.
    bodyBytes, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return nil, fmt.Errorf("failed to read response: %v", err)
    }

    // Декодируем JSON в структуру WeatherData.
    var weather WeatherData
    err = json.Unmarshal(bodyBytes, &weather)
    if err != nil {
        return nil, fmt.Errorf("failed to decode JSON: %v", err)
    }

    return &weather, nil
}

// displayWeatherData выводит информацию о погоде в удобном для пользователя формате.
func displayWeatherData(weather *WeatherData) {
    fmt.Printf("\nWeather for %s:\n", weather.Name)
    if len(weather.Weather) > 0 {
        fmt.Printf("Description: %s\n", strings.Title(weather.Weather[0].Description))
    }
    fmt.Printf("Temperature: %.2f°C\n", weather.Main.Temp)
    fmt.Printf("Humidity: %d%%\n\n", weather.Main.Humidity)
}

func main() {
    // Читаем API-ключ из переменной окружения.
    apiKey := os.Getenv("OPENWEATHER_API_KEY")
    if apiKey == "" {
        fmt.Println("Error: Please set the OPENWEATHER_API_KEY environment variable.")
        os.Exit(1)
    }

    // Проверяем, передано ли название города в аргументах командной строки.
    var city string
    if len(os.Args) > 1 {
        city = strings.Join(os.Args[1:], " ")
    } else {
        // Запрашиваем у пользователя название города, если аргумент не указан.
        fmt.Print("Enter the city name: ")
        reader := bufio.NewReader(os.Stdin)
        input, err := reader.ReadString('\n')
        if err != nil {
            fmt.Println("Error reading input:", err)
            return
        }
        city = strings.TrimSpace(input)
    }

    // Получаем данные о погоде.
    weather, err := getWeatherData(city, apiKey)
    if err != nil {
        fmt.Println("Error fetching weather data:", err)
        return
    }

    // Отображаем данные о погоде.
    displayWeatherData(weather)
}

Русскоязычное Go сообщество

Друзья! Эту статью перевела команда «Go for Devs» — сообщества, где мы делимся практическими кейсами, инструментами для разработчиков и свежими новостями из мира Go. Подписывайтесь, чтобы быть в курсе и ничего не упустить!

Итоги

Создание CLI-приложения погоды на Go — это не только интересно, но и отличный способ прокачать навыки программирования. Вы научились настраивать проект, отправлять HTTP-запросы, парсить JSON и делать удобный инструмент для командной строки. Продолжая работу с Go, подумайте о расширении проекта, например:

  • Кэширование данных о погоде для быстрого доступа.

  • Поддержка нескольких городов в одном запросе.

  • Улучшенная обработка ошибок и логирование.

Надеюсь, вам понравился этот туториал. Дальше — экспериментируйте, дорабатывайте и, возможно, поделитесь своей версией CLI-приложения погоды с коллегами.

Удачного кодинга и пусть погода всегда радует!)

Комментарии (0)