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("ES_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 | } |