sam65536

Parser

Sep 7th, 2020
58
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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. }
Add Comment
Please, Sign In to add comment