View difference between Paste ID: pEJRuKTt and TJjBLLe5
SHOW: | | - or go back to the newest paste.
1
package main
2
3
import (
4
	"flag"
5
	"fmt"
6
	"github.com/opesun/goquery"
7
	"strings"
8
	"time"
9
	//Пакеты, которые пригодятся для работы с файлами и сигналами:
10
	"io"
11
	"os"
12
	"os/signal"
13
	//А вот эти - для высчитывания хешей:
14
	"crypto/md5"
15
	"encoding/hex"
16
)
17
18
var (
19
	WORKERS       int             = 2                     //кол-во "потоков"
20
	REPORT_PERIOD int             = 10                    //частота отчетов (сек)
21
	DUP_TO_STOP   int             = 500                   //максимум повторов до останова
22
	HASH_FILE     string          = "hash.bin"            //файл с хешами
23
	QUOTES_FILE   string          = "quotes.txt"          //файл с цитатами
24
	used          map[string]bool = make(map[string]bool) //map в котором в качестве ключей будем использовать строки, а для значений - булев тип.
25
)
26
27
func init() {
28
	//Задаем правила разбора:
29
	flag.IntVar(&WORKERS, "w", WORKERS, "количество потоков")
30
	flag.IntVar(&REPORT_PERIOD, "r", REPORT_PERIOD, "частота отчетов (сек)")
31
	flag.IntVar(&DUP_TO_STOP, "d", DUP_TO_STOP, "кол-во дубликатов для остановки")
32
	flag.StringVar(&HASH_FILE, "hf", HASH_FILE, "файл хешей")
33
	flag.StringVar(&QUOTES_FILE, "qf", QUOTES_FILE, "файл записей")
34
	//И запускаем разбор аргументов
35
	flag.Parse()
36
}
37
38
func grab() <-chan string { //функция вернет канал, из которого мы будем читать данные типа string
39
	c := make(chan string)
40
	for i := 0; i < WORKERS; i++ { //в цикле создадим нужное нам количество гоурутин - worker'oв
41
		go func() {
42
			for { //в вечном цикле собираем данные
43
				x, err := goquery.ParseUrl("http://vpustotu.ru/moderation/")
44
				if err == nil {
45
					if s := strings.TrimSpace(x.Find(".fi_text").Text()); s != "" {
46
						c <- s //и отправляем их в канал
47
					}
48
				}
49
				time.Sleep(100 * time.Millisecond)
50
			}
51
		}()
52
	}
53
	fmt.Println("Запущено потоков: ", WORKERS)
54
	return c
55
}
56
57
func check(e error) {
58
	if e != nil {
59
		panic(e)
60
	}
61
}
62
63
func readHashes() {
64
	//проверим файл на наличие
65
	if _, err := os.Stat(HASH_FILE); err != nil {
66
		if os.IsNotExist(err) {
67
			fmt.Println("Файл хешей не найден, будет создан новый.")
68
			return
69
		}
70
	}
71
72
	fmt.Println("Чтение хешей...")
73
	hash_file, err := os.OpenFile(HASH_FILE, os.O_RDONLY, 0666)
74
	check(err)
75
	defer hash_file.Close()
76
	//читать будем блоками по 16 байт - как раз один хеш:
77
	data := make([]byte, 16)
78
	for {
79
		n, err := hash_file.Read(data) //n вернет количество прочитанных байт, а err - ошибку, в случае таковой.
80
		if err != nil {
81
			if err == io.EOF {
82
				break
83
			}
84
			panic(err)
85
		}
86
		if n == 16 {
87
			used[hex.EncodeToString(data)] = true
88
		}
89
	}
90
91
	fmt.Println("Завершено. Прочитано хешей: ", len(used))
92
}
93
94
func main() {
95
	readHashes()
96
	//Открываем файл с цитатами...
97
	quotes_file, err := os.OpenFile(QUOTES_FILE, os.O_APPEND|os.O_CREATE, 0666)
98
	check(err)
99
	defer quotes_file.Close()
100
101
	//...и файл с хешами
102
	hash_file, err := os.OpenFile(HASH_FILE, os.O_APPEND|os.O_CREATE, 0666)
103
	check(err)
104
	defer hash_file.Close()
105
106
	//Создаем Ticker который будет оповещать нас когда пора отчитываться о работе
107
	ticker := time.NewTicker(time.Duration(REPORT_PERIOD) * time.Second)
108
	defer ticker.Stop()
109
110
	//Создаем канал, который будет ловить сигнал завершения, и привязываем к нему нотификатор...
111
	key_chan := make(chan os.Signal, 1)
112
	signal.Notify(key_chan, os.Interrupt)
113
114
	//...и все что нужно для подсчета хешей
115
	hasher := md5.New()
116
117
	//Счетчики цитат и дубликатов
118
	quotes_count, dup_count := 0, 0
119
120
	//Все готово, поехали!
121
	quotes_chan := grab()
122
	for {
123
		select {
124
		case quote := <-quotes_chan: //если "пришла" новая цитата:
125
			quotes_count++
126
			//считаем хеш, и конвертируем его в строку:
127
			hasher.Reset()
128
			io.WriteString(hasher, quote)
129
			hash := hasher.Sum(nil)
130
			hash_string := hex.EncodeToString(hash)
131
			//проверяем уникальность хеша цитаты
132
			if !used[hash_string] {
133
				//все в порядке - заносим хеш в хранилище, и записываем его и цитату в файлы
134
				used[hash_string] = true
135
				hash_file.Write(hash)
136
				quotes_file.WriteString(quote + "\n\n\n")
137
				dup_count = 0
138
			} else {
139
				//получен повтор - пришло время проверить, не пора ли закругляться?
140
				if dup_count++; dup_count == DUP_TO_STOP {
141
					fmt.Println("Достигнут предел повторов, завершаю работу. Всего записей: ", len(used))
142
					return
143
				}
144
			}
145
		case <-key_chan: //если пришла информация от нотификатора сигналов:
146
			fmt.Println("CTRL-C: Завершаю работу. Всего записей: ", len(used))
147
			return
148
		case <-ticker.C: //и, наконец, проверяем не пора ли вывести очередной отчет
149
			fmt.Printf("Всего %d / Повторов %d (%d записей/сек) \n", len(used), dup_count, quotes_count/REPORT_PERIOD)
150
			quotes_count = 0
151
		}
152
	}
153
}