CoderCastrov logo
CoderCastrov
Голанг

Парсинг стикеров LINE с помощью Golang

Парсинг стикеров LINE с помощью Golang
просмотров
5 мин чтение
#Голанг

Создание быстрого скрипта с использованием Golang для парсинга стикеров LINE

Давайте согласимся, что у WhatsApp нет отличных стикеров, но у LINE они есть. Для тех, кто не знает, что такое LINE, или задается вопросом, что такого особенного в их стикерах, посмотрите здесь. Кроме того, этот проект может быть полезен для парсинга информации, если вы работаете в этой области.

Вот код в моем репозитории Github

Парсинг веба

Большинство из нас знают, что в Python есть отличные библиотеки, такие как BeautifulSoup/bs4 и Selenium, а также расширения для Chrome, которые могут скачивать все медиафайлы на определенной странице, и я бы поощрял использование этих популярных подходов.

Однако сегодня вызов этого проекта состоит в следующем:

  • Сделать это без использования библиотеки
  • Попытаться достичь параллельности/асинхронности (скачивание нескольких стикеров одновременно без ожидания)
  • (По желанию) Упаковать это, чтобы наши не технически подкованные друзья могли использовать это

Что означает скачивание параллельно/асинхронно? Это означает, что мы не будем ждать, пока один стикер скачается и преобразуется, а затем перейдем к следующему. Как в школе, учительница (Мадам Чу) просто дает каждому ученику в классе задание для выполнения, пока Мадам Чу сидит и ждет, пока каждый ученик завершит свое задание и вернет его ей. В обычной программе Мадам Чу дала бы одному ученику задание, подождала бы, пока этот ученик закончит, а затем дала бы второе задание. В большинстве случаев это работает нормально, так как этот один ученик хорошо справляется с тем, что делает, однако Мадам Чу понимает, что у нее есть класс из 12 учеников, которые все это время ничего не делают. Если бы она могла дать задание всем 12 ученикам (потокам), ее задача была бы выполнена быстрее.

Почему Golang

Правда в том, что это можно сделать с помощью любого другого языка программирования [NodeJS, C++, Python...]. Поэтому на самом деле не должно быть никаких споров по этому поводу, но это просто для развлечения и обучения :)

Однако то, что выделяет Golang среди других, это возможность легко достичь второй и третьей точки. Более того, Golang создан для того, чтобы Мадам Чу могла легко назначить всем своим ученикам задание одновременно.

Speedrun tutorial

Вот код в моем [репозитории на Github]

Понимание нашей цели

Прежде чем мы начнем, нам нужно понять несколько вещей. Наш магазин наклеек, к счастью, не использует клиентский рендеринг с помощью JavaScript, поэтому базовая команда curl получит всю необходимую информацию без ожидания AJAX-запросов. Мы можем разбирать HTML-теги и получать нужные нам наклейки, однако мы заметили, что каждая наклейка содержит сырую информацию в пользовательском атрибуте HTML под названием data-preview=’{}’. Это позволяет нам разбирать эту информацию в формате JSON.

data-preview=”{ “type” : “popup_sound”, “id” : “312149456”, “staticUrl” : “[https://stickershop.line-scdn.net/stickershop/v1/sticker/312149456/iPhone/sticker@2x.png;compress=true](https://stickershop.line-scdn.net/stickershop/v1/sticker/312149456/iPhone/sticker@2x.png;compress=true)", “fallbackStaticUrl” : “[https://stickershop.line-scdn.net/stickershop/v1/sticker/312149456/iPhone/sticker@2x.png;compress=true](https://stickershop.line-scdn.net/stickershop/v1/sticker/312149456/iPhone/sticker@2x.png;compress=true)", “animationUrl” : “”, “popupUrl” : “[https://stickershop.line-scdn.net/stickershop/v1/sticker/312149456/android/sticker_popup.png;compress=true](https://stickershop.line-scdn.net/stickershop/v1/sticker/312149456/android/sticker_popup.png;compress=true)", “soundUrl” : “[https://stickershop.line-scdn.net/stickershop/v1/sticker/312149456/android/sticker_sound.m4a](https://stickershop.line-scdn.net/stickershop/v1/sticker/312149456/android/sticker_sound.m4a)" }”

С этим готово, давайте начнем.

1/ Сначала мы создаем точку входа, которая принимает URL.

// Точка входа
func main(){
 consoleReader := bufio.NewReader(os.Stdin)
 for {
  fmt.Println(“Введите URL магазина наклеек Line”)
  inputUrl, err := consoleReader.ReadString(‘\n’); if err != nil {
   log.Fatal(err)
  }  // Проверяем, содержит ли ввод хотя бы формат магазина Line
  if strings.Contains(inputUrl, “[https://store.line.me](https://store.line.me)") {
   inputUrl = strings.Replace(inputUrl, “\r\n”, “”, -1)
   err := scrap(inputUrl); if err != nil {
    log.Fatal(err)
   }
  } else {
   fmt.Println(“Неверный формат”)
  }
 }
}// Проверяем, содержит ли ввод хотя бы формат магазина Line
 if strings.Contains(inputUrl, “[https://store.line.me](https://store.line.me)") {
 inputUrl = strings.Replace(inputUrl, “\r\n”, “”, -1)
 err := scrap(inputUrl); if err != nil {
 log.Fatal(err)
 }
 } else {
 fmt.Println(“Неверный формат”)
 }
 }
}

2/ Затем мы создаем функцию парсинга, которая использует встроенную библиотеку http для загрузки веб-страницы.

resp, err := http.Get(scrapUrl); if err != nil {
 return err
}

3/ После завершения загрузки мы будем разбирать тело загруженной веб-страницы. Помните, мы сказали, что сырая информация может быть найдена в пользовательском атрибуте с именем data-preview, без необходимости усложнять, вызов регулярного выражения позволит извлечь каждое вхождение этого атрибута.

var rgx = regexp.MustCompile(`(data-preview=’.*?’)`)
tmpExtracted := rgx.FindAllStringSubmatch(inputHtml, -1)
for i := 0; i < len(tmpExtracted); i++ {
 // Разбираем JSON здесь

4/ Перед разбором JSON давайте создадим структуру на основе необходимой информации, которую мы получаем из атрибута data-preview.

type DataPreview struct {
 Id string `json:”id”`
 StickerType string `json:”type”`
 PopupUrl string `json:”popupUrl”`
 StaticUrl string `json:”staticUrl”`
 AnimationUrl string `json:”animationUrl”`
 SoundUrl string `json:”soundUrl”`
}

5/ Отлично, теперь мы просто разбираем JSON в структуру DataPreview.

6/ Далее создаем функцию для параллельной загрузки наклеек. В Golang это может быть так просто, как несколько строк.

var wg sync.WaitGroup
for i := 0; i < len(result); i++ {
 wg.Add(1)
 go downloadImage(result[i], &wg)
}

WaitGroup просто сообщает Мадам Чу, сколько студентов она назначила на задачу.


7/ Мы знаем, что есть 3 URL, которые мы можем использовать: PopupUrl для [больших анимированных наклеек]([https://store.line.me/stickershop/product/17300/en](https://store.line.me/stickershop/product/17300/en)), StaticUrl для [наклеек, которые не двигаются]([https://store.line.me/stickershop/product/2803/en](https://store.line.me/stickershop/product/2803/en)) и AnimationUrl для [нормальных анимированных наклеек]([https://store.line.me/stickershop/product/19770/en](https://store.line.me/stickershop/product/19770/en)). Создание простого правила переключения поможет нам определить, какой URL мы должны использовать, а затем снова использовать библиотеку HTTP для загрузки GIF.

8/ После загрузки GIF я использую [[APNG2GIF](https://sourceforge.net/projects/apng2gif/)], чтобы конвертировать APNG в GIF. Это не самое идеальное решение, но определенно самое простое.

9/ Прежде чем мы продолжим и просим пользователя ввести другой URL, Мадам Чу хочет подождать, пока все студенты завершат свою работу. Мы должны добавить это в асинхронную функцию, чтобы сообщить Мадам Чу, что ее работа завершена.

defer wg.Done()


И Мадам Чу будет ждать, пока все работы не будут завершены, прежде чем продолжить.

wg.Wait()


10/ И вот мы закончили! Мы можем быстро упаковать его для Windows и отправить скомпилированный бинарный файл нашим друзьям, просто запустив

go build main.go


# Финал

Вот, быстрая программа для парсинга GIF для вашего удовольствия. Хотя это может показаться тривиальным делом, это имеет большое значение, когда вы занимаетесь сбором данных для обучения машинного обучения или массового парсинга. Разница с использованием и без использования асинхронности влияет на время, необходимое для достижения нашей цели в масштабе.

Спасибо за чтение до конца, надеюсь, вам понравилась эта публикация!