 
        Проверка доступности сайта с помощью Golang
Проверка доступности сайта, используя Golang.
Al-Sher
Всем привет. Уже давно я пытаюсь изучить Golang, ну вот и решил написать небольшую программу для автоматической проверки сайтов из списка.
Внимание!Использование ПО разрешается лишь с вашем собственным сайтом!
Весь код программы на GitHub
Подготовка.
Для начала необходимо установить Golang. Думаю это не вызовет проблем, достаточно скачать инсталятор с официального сайта и запустить его. Но, если вдруг, это вызвало сложности, то можно почитать инструкцию. В программе используются лишь стандартные пакеты, поэтому всё должно работать и без установки доп. пакетов.
Код.
Вся программа поделена 4 функции:
- main - основная функция, выполняющаяся при запуске программы;
- getSitesFromFile - функция для поиска ссылок в файле. Все ссылки храним в оперативной памяти;
- getParamsFromArgs - функция для получения дополнительных параметров;
- testSite - функция для проверки доступности сайта.
Итак, начнем с сначала. А в начале нам необходимо объявить название пакета. Так как мы пишем приложение, то название пакета должно быть main:
package mainДалее импортируем необходимые пакеты и объявляем наши переменные:
import (
    "net/http"
    "net/url"
    "fmt"
    "io/ioutil"
    "os"
    "strings"
    "time"
    "strconv"
)
var timer int = 1
var proxy string = ""Не буду вдаваться в подробности данных пакетов. А вот переменные... Переменная timer предназначена для установки таймера. То есть время, спустя которое программа заходит на сайт. По-умолчанию стоит 1. Переменная proxy предназначена для работы приложения через прокси. Позволит проверять внутренние ресурсы.
Ну и затроним еще функцию main, то есть нашу главную функцию:
func main() {
    sites := getSitesFromFile("sites.txt")
    getParamsFromArgs()
    if timer > 1 {
        fmt.Println("use timer: ", timer)
    }
    if proxy != "" {
        fmt.Println("use proxy: ", proxy)
    }
    ch := make(chan byte, 1)
    for _, site := range sites {
        go func (site string) {
            for {
                testSite(site)
                time.Sleep(time.Duration(timer) * time.Second)
            }
            ch Итак, мы объявили функцию. В переменную sites мы сохраняем все необходимые нам дя проверки сайты, которые лежат в файле sites.txt:
sites := getSitesFromFile("sites.txt")Далее мы вызываем вспомогательную функцию getParamsFromArgs:
getParamsFromArgs()Если время таймера больше 1, то мы используем не стандартное время, а значит передали программе время таймера. Время, кстати, в секундах, но об этом позже. Так же проверяем использование прокси:
    if timer > 1 {
        fmt.Println("use timer: ", timer)
    }
    if proxy != "" {
        fmt.Println("use proxy: ", proxy)
    }Так же создаем канал ch для того, чтобы процесс main не завершался. Это позволит выводить сообщения в консоль:
ch := make(chan byte, 1)Итак, проходимся по массиву наших сайтов. В переменной site хранится один адрес:
for _, site := range sites {Реализуем многопоточность благодаря вызову анонимной функции через go. В эту функцию передаем значение переменной site:
go func (site string) {В данной функции мы осуществляем бесконечный цикл. По завершению мы передаем очереди, что всё готово. Так же именно тут вызывается функция testSite, которой мы передаем адрес сайта. А еще можно заметить и наш таймер:
            for {
                testSite(site)
                time.Sleep(time.Duration(timer) * time.Second)
            }После каждого выполнения testSite мы отправляем поток в "сон" на определенное время. Именно тут и указано, что мы используем секунды, благодаря умножению на time.Second:
time.Duration(timer) * time.SecondДалее мы используем наш канал ch, передавая в него единицу:
ch <- 1Вызываем нашу анонимную функцию с параметром site:
}(site)Выбрасываем в пустоту значение канала ch:
<- chФункция getSitesFromFile.
Далее мы рассматриваем getSitesFromFile, то есть получение сайтов из файла:
func getSitesFromFile(namefile string) []string {
    data, err := ioutil.ReadFile(namefile)
    if err != nil {
        fmt.Println("error: ", err)
        os.Exit(2)
    }
    result := string(data)
    test := strings.Split(result, "\r\n")
    return test
}В ней мы передаем название нашего файла и указываем, что вернется массив строк:
func getSitesFromFile(namefile string) []string {Далее мы загружаем содержимое файла в переменную data, а так же ошибку в err:
data, err := ioutil.ReadFile(namefile)Проверяем на ошибки:
    if err != nil {
        fmt.Println("error: ", err)
        os.Exit(2)
    }Если есть ошибки, то выводим ошибку и завершаем программу. Далее переводим содержимое файла из байт в строку:
result := string(data)Разделяем строку по символам \r\n, то есть по пропуску строки:
test := strings.Split(result, "\r\n")И возвращаем результат:
return testФункция getParamsFromArgs.
Как я говорил, мы принимаем дополнительные параметры из командой строки. Вот эта функция:
func getParamsFromArgs() {
    if len(os.Args) > 1 {
        for i, _ := range os.Args {
            if os.Args[i] == "proxy" {
                i++
                proxy = os.Args[i]
            }
            if os.Args[i] == "timer" {
                i++
                timer, _ = strconv.Atoi(os.Args[i])
            }
        }
    }
}В ней мы проверяем содержимое аргументов os.Args:
if len(os.Args) > 1 {Первый элемент всегда название исполняемого файла, поэтому используем проверку на больше 1. Далее проходимся циклом по данным аргументам:
for i, _ := range os.Args {Если находим необходимые нам параметры, то следующий параметр записываем в нужные места. Легко можно расширить возможности...
Функция testSite.
И вот мы дошли до самой интересной функции - testSite:
func testSite(u string) bool {
    if u == "" {
        return false
    }
    client := http.Client{}
    if proxy != "" {
        proxyUrl, _ := url.Parse(proxy)
        client = http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(proxyUrl)}}
    }
    req, _ := http.NewRequest("GET", u, nil)
    req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT x.y; Win64; x64; rv:10.0) Gecko/20100101 Firefox/10.0")
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("error: ", err)
        return false
    } 
    _, err := ioutil.ReadAll(resp.Body)
    if err == nil {
        if resp.StatusCode == 200 {
            fmt.Println(u + ": " + "ok")
        } else {
            fmt.Println("Site ", u, " returned error code: ", resp.StatusCode)   
        }
    }
    defer resp.Body.Close()
    return true
}Мы передаем данной функции необходимый url адес. Сама функция возвращает bool'евое значение:
func testSite(u string) bool {Проверяем не пустой ли url адрес:
    if u == "" {
        return false
    }Создаем http клиента:
client := http.Client{}Проверяем прокси. Если он имеется, то настраиваем наш клиент:
    if proxy != "" {
        proxyUrl, _ := url.Parse(proxy)
        client = http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(proxyUrl)}}
    }Создаем переменную Request. Она будет содержать все необходимые параметры, передаваемые клиенту, в том числе и метод, и url адрес:
req, _ := http.NewRequest("GET", u, nil)Задаем заголовок браузера:
req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT x.y; Win64; x64; rv:10.0) Gecko/20100101 Firefox/10.0")И исполняем запрос клиентом:
resp, err := client.Do(req)Функция вернет нам ответ сервера(resp) и ошибку(err). Проверяем на наличие ошибки:
    if err != nil {
        fmt.Println("error: ", err)
        return false
    } Проверяем ошибки чтения из Body:
    _, err := ioutil.ReadAll(resp.Body)
    if err == nil {Если ошибок нет, то проверяем статус ответа:
        if resp.StatusCode == 200 {
            fmt.Println(u + ": " + "ok")
        } else {
            fmt.Println("Site ", u, " returned error code: ", resp.StatusCode)   
        }Закрываем Body после того, как он нам не нужен:
defer resp.Body.Close()И возвращаем true:
return trueЗаключение.
Вот вам простенькая программа для проверки ваших сайтов на доступность, которую можно собрать как под linux, так и под windows :) Спасибо за внимание, надеюсь она послужит для достойных целей!