Pull to refresh

Выкачиваем фотографии из каждого диалога ВК средствами API

Иногда бывает полезно выгрузить все фотографии из диалога ВКонтакте, руками это не сделать не просто, поэтому обратимся к API.

В документации присутствует метод messages.getHistoryAttachments — он нам и нужен. Возвращать он может не только фотографии, но и другого рода документы: аудио, видео и т.д. Но в этом примере будем рассматривать только фотографии.

Передавать в метод будем 4 параметра:

peer_id — собственно, ID юзера из диалога с которым нужно получить фотографии
media_type — тип необходимого документа, будем передавать photo
count — количество фотографий, которые будут возвращены. Мы хотим выкачать все, поэтому будет брать по максимуму — 200
start_from — это смещение, с помощью которого мы сможем получить все имеющиеся в диалоге фотографии

Нам требуется загрузить фотографии из всех диалогов, поэтому воспользуемся еще одним методом для получения списка всех диалогов — messages.getDialogs. Получив списки диалогов, мы получим идентификаторы юзеров, которые в последующем сможем передавать в метод для получения фотографий.

И, конечно же, не обойтись без библиотеки для работы с API вконтакте, которая умеет выполнять нужные функции. Я воспользуюсь своей — golang-vk-api

Диалогов, также как и фотографий. нельзя получить больше 200, поэтому напишем функцию для получения всех диалогов, где будем использовать смещение:

func getDialogs(client *vkapi.VKClient) ([]*vkapi.DialogMessage, error) {
	offset := 0
	params := url.Values{}
	messages := []*vkapi.DialogMessage{}

	for {
		params.Set("offset", strconv.Itoa(offset))
		dialogs, err := client.GetDialogs(200, params)
		if err != nil {
			return nil, err
		}

		if len(dialogs.Messages) > 0 {
			for _, msg := range dialogs.Messages {
				messages = append(messages, msg.Message)
			}
		}

		offset += 200

		if len(messages) >= dialogs.Count {
			break
		}
	}

	return messages, nil
}

После того, как получили все диалоги, можно получить непосредственно сами фотографии, используя метод, упомянутый в самом начале:

func getAttachments(client *vkapi.VKClient, UID int) ([]*vkapi.PhotoAttachment, error) {
	attachments := []*vkapi.PhotoAttachment{}
	params := url.Values{}

	for {
		att, err := client.GetHistoryAttachments(UID, "photo", 200, params)
		if err != nil {
			return nil, err
		}

		if len(att.Attachments) == 0 {
			break
		}

		for _, photo := range att.Attachments {
			attachments = append(attachments, photo.Attachment.Photo)
		}

		params.Set("start_from", att.NextFrom)
	}

	return attachments, nil
}

Прежде чем преступить к загрузке всех фотографий, напишем пару необходимых методов:

Проверка существования папки

func folderExists(name string) bool {
	if _, err := os.Stat(name); err != nil {
		if os.IsNotExist(err) {
			return false
		}
	}

	return true
}

Создание папки

func mkdir(name string) bool {
	err := os.MkdirAll(name, 0755)
	if err != nil {
		return false
	}

	return true
}

Получение имени, с которым будет сохраняться файл, из ссылки

func getFileName(url string) string {
	idx := strings.LastIndex(url, "/")
	return url[idx+1:]
}

У фотографии не всегда доступны все размеры, поэтому напишем метод для выборки наибольшего размера

func getBestLink(photo *vkapi.PhotoAttachment) string {
	if photo.Photo2560 != "" {
		return photo.Photo2560
	} else if photo.Photo1280 != "" {
		return photo.Photo1280
	}

	return photo.Photo604
}

Загрузка файла

func downloadFile(url string, name string) error {
	file, err := os.Create(name)
	if err != nil {
		return err
	}
	defer file.Close()

	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	_, err = io.Copy(file, resp.Body)
	if err != nil {
		return err
	}

	return nil
}

В основной функции получаем все диалоги, для каждого диалога получаем все фотографии и загружаем по 10 штук одновременно в отдельную для юзера папку

		dialogs, err := getDialogs(client)
		if err != nil {
			log.Printf("failed to get dialogs: %s\n", err)
			return
		}

		curUser := 0
		for _, d := range dialogs {
			photos, err := getAttachments(client, d.UID)
			if err != nil {
				log.Printf("failed to get attachments for UID %d\n", d.UID)
				continue
			}

			if len(photos) == 0 {
				continue
			}

			downloadPath := "photos/" + strconv.Itoa(d.UID)
			mkdir(downloadPath)

			curUser++
			downloaded := 0
			gocounter := 0
			limit := 10
			queue := len(photos)
			total := len(photos)
			totalUsers := len(dialogs)
			var wg sync.WaitGroup

			for _, p := range photos {
				link := getBestLink(p)
				path := downloadPath + "/" + getFileName(link)
				wg.Add(1)

				go func() {
					err := downloadFile(link, path)
					if err != nil {
						log.Printf("failed to download file: %s\n", err)
					}
					wg.Done()
					queue--
					downloaded++
					log.Printf("downloaded %d/%d photos. user %d/%d\n",
						downloaded, total, curUser, totalUsers)
				}()

				if queue < limit {
					limit = queue
				}

				gocounter++
				if gocounter == limit {
					wg.Wait()
					gocounter = 0
				}
			}
		}


Вот и все. Полный код по ссылке
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.