Skip to content

Instantly share code, notes, and snippets.

@gigovich
Created January 18, 2017 12:56
Show Gist options
  • Save gigovich/915f36efb10038ba9bb66f0a203cdfd5 to your computer and use it in GitHub Desktop.
Save gigovich/915f36efb10038ba9bb66f0a203cdfd5 to your computer and use it in GitHub Desktop.
Golang - представьте это на Python.
package main
import (
"flag"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"os/signal"
"regexp"
"strings"
"time"
)
var (
flagParallel = flag.Int("p", 5, "количество одновременно работающих запросов")
)
func main() {
// спарсим аргси из команды запуска
flag.Parse()
// если ты не дебил, и указал хотя-бы один параметр, будем считать его за стартовый урл
if flag.NArg() == 0 {
fmt.Println("usage: crawl [OPTIONS] <START_URL>")
fmt.Println()
flag.PrintDefaults()
os.Exit(1)
}
// это пустой канал с пустой структурой, мы в него ничего писать не будем, заставим все горутины,
// читать из него, а в конце программы закроем, как только горутины попытаются прочитать из закрытого канала,
// заставим их завершиться.
live := make(chan struct{})
// запускаем карулер функцию, которая возвращает канал с будущими статусами, а внутри себя запускает
// асинхронную горутину которая и будет делать все дела, и слать данные в этот канал.
stat := start(live, flag.Arg(0), *flagParallel)
// сами запускаем асинхронно горутину котора печатает статистику каждую минуту
go printStat(live, stat)
// создаём пустой канал для того, что-бы ловить сигнал ОС об завершении
notifCh := make(chan os.Signal)
// просим чувака половить сигналы о завершении и отослать их по нашему каналу
signal.Notify(notifCh, os.Interrupt)
// ждём первой записи из канала с завершающимися сигналами, как только получаем значение, можем свободно выходить.
<-notifCh
close(live)
}
// начинает парсинг по урул, запускает нужное количество горутин воркеров и возвращает канал в который пишет
// количество найденных на странице урлов.
func start(live chan struct{}, startURL string, parCnt int) chan int {
// создаём канал, в который мы будем пихать количество найденных урлов, и потом это дело печатать
urlsCount := make(chan int, 1000)
// а это канал для внутреннего потребления, что-то типа ограниченной очереди, сюда мы пихаем урлы для
// дальнейшей обработки. Здесь же пихаем и здесь же читаем.
urls := make(chan string, 1000)
// компилируем регулярку, строго 1 раз, если не компилируется паникуем нах сразу при старте, почини себе руки.
match := regexp.MustCompile(`href\s*=\s*"(.*?)"`)
// ооо функции первого класса, так для примера, и удобства именнованных возвращаемых результатов, а так же,
// мне надо было обязательно закрывать респонс, что-бы коннекты не утекали, при помощи отдельной функции
// это делать удобней и красивее.
read := func(r io.ReadCloser) (data []byte, err error) {
// прочитали всё из тела ответа, и сразу присвоили их возвращаемым значениям
data, err = ioutil.ReadAll(r)
// и закрыли сразу нахер, даже если ошибка при чтении была
r.Close()
// вернули все значения которые были выставлены во время выполнения функции.
return
}
// а тут не просто первокласная функция, а замыкание, наверное в этом месте все ДЖ-с скриптеры обкончились.
worker := func() {
// ну собственно при помощи ренджа, можно читать последовательно значения присылаемые в канал,
// если значений в канале нет, он ждёт их не выполняя цикла, как только появляется присваивает u, и выполняет
// цикл.
for u := range urls {
// в го одна из лучших хттп библиотек. пытаемся сделать запрос, получаем сам запрос и ошибку.
resp, err := http.Get(u)
// если ошибка не нулевая, то надо её как-то обарботать, в нашем случае печатаем и идём к след. значению.
if err != nil {
fmt.Println("Ой блять ошибка:", err)
continue
}
// Процесс чтения данных из запроса отделён от процесса самого запроса. Но есть одно, но, не забываем
// закрывать тело, после чтения, иначе может остать много открытых соединений, и хоть об этом тысячи раз
// сказано в документации, товичкам хоть бы что. Читайте доки суки.
data, err := read(resp.Body)
if err != nil {
fmt.Println("блять при чтении ошибка:", err)
// Та же херь в случае ошибки сообщение и начинаем цикл заново
continue
}
// ищем урлы, не спрашивайте почему количество -1, это типа без ограничений искать. А почему нельзя было,
// 0? Опять в доки суки.
matches := match.FindAllSubmatch(data, -1)
// в го можно безопасно объявлять переменные, в смысле они всегда проиницилизированны своими дефолтными
// значениями.
var found int
// кстати := это тоже способ создать переменну. В данном случае первое элемент это индекс, второй значение.
// но нам плевать на индекс, и отправляем его в пустоту, для этого есть спец переменная _.
for _, group := range matches {
// ну если ничего не нашли, пропускаем.
if len(group) == 0 || len(group[1]) == 0 {
continue
}
// не забываем что у нас строгая типизация, поэтому последовательность байтов, надо явно привести
// к строке, и здесь кстати аллокация памяти ещё происходит с копированием.
u := string(group[1])
// отфильтруем мусор
if !strings.HasPrefix(u, "http") {
continue
}
// увеличиваем счётчик найденных урлов
found++
// наряду с горутинами краегольная конструкция, эта херня выполняет первое доступное действие.
select {
// пытаемся читать из этого канала, но в него никогда не будет записано, а если его закроют, то это
// эквивалентно чтению, так что выполнится return, как определить, пришло значение или закрылся канал,
// об этом чуток надо прочитать.
case <-live:
return
/// вот тут обратная ситуация, так как у нас очередь, а она не резиновая в качестве неёё обычный канал
// с буфером на 1000 элементов, очевидно, что он очень быстро заполниться, поэтому, здесь, мы пытаемся
// записать в него, не получилось, так как он заполнен, ну и хер с ним, идём дальше, а ссылку игнорим.
case urls <- u:
default:
// ну тут очевидно если ни один из выше изложенных кейсов не был выполнен, нахер, обарбатываем
// следующий урл.
continue
}
}
// опа, банально отправляем количество прочитанных урлов в канал, на том конце кто-то да прочитает ))),
// но если канал заполнен, тоесть на той строно никто данных не выгребает, здесь мы тормазнёмся, ровно
// до того момента, пока в канале не появится места, тоесть, кто-то из него не прочитает.
urlsCount <- found
}
}
// банально, классическим циклом, запускаем то количество паралелльных воркеров, которое нам нужно.
for i := 0; i < parCnt; i++ {
go worker()
}
// финальный аккорд, посылаем стартовый урл в канал, урлов, что-бы один из воркеров инициировал процесс краулинга
// и наполнил очередь, а там и все остальные получат урлы уже от этого воркера.
urls <- startURL
// ах да, возвращаем канал считающий урыл.
return urlsCount
}
// печатаем статистику выгребая данные из канала статистики
func printStat(live chan struct{}, urlsCount chan int) {
// общий счётчик, видите вместо var можно вот так делать, удобнее
foundedURLs := 0
// о классная функция, она возвращает канал, в который через указанное время придёт значение, супер вещь.
wait := time.After(time.Second * 2)
// бесконечный цикл, а хуле нам пацанам.
for {
// опять селект, кудаж без него.
select {
case <-live:
// закрыли канал, съёбываем из функции
return
case cnt := <-urlsCount:
// тут интереснее, но тоже банально, прочитали стату заинкрементили счётчик, так как делаем это только здесь,
// никаких мьютексов и атомарных изменений ненадо.
foundedURLs += cnt
case <-wait:
// оооо охренеть, в этом канале есть значение, значит прошло 2 секунды, печатаем стату.
fmt.Println("Найдено урлов:", foundedURLs)
// вежливо просим функцию ещё раз дать нам канал в который прилетит значение через 2 секунды.
wait = time.After(time.Second * 2)
}
}
}
@TrevorWaghela
Copy link

апишите строчку логики в Питоне как будет выглядить следующие условия : если op>70% и cl<20% И op<50% и cl>90% И cl )>hi ,что будет в какой переменной и с какого класса состоять и отпишитесь в лс t.me/Night_AngeI vk.com/hackman7872

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment