Иногда бывает полезно выгрузить все фотографии из диалога ВКонтакте, руками это не сделать не просто, поэтому обратимся к API.
В документации присутствует метод messages.getHistoryAttachments — он нам и нужен. Возвращать он может не только фотографии, но и другого рода документы: аудио, видео и т.д. Но в этом примере будем рассматривать только фотографии.
Передавать в метод будем 4 параметра:
peer_id — собственно, ID юзера из диалога с которым нужно получить фотографии
media_type — тип необходимого документа, будем передавать photo
count — количество фотографий, которые будут возвращены. Мы хотим выкачать все, поэтому будет брать по максимуму — 200
start_from — это смещение, с помощью которого мы сможем получить все имеющиеся в диалоге фотографии
Нам требуется загрузить фотографии из всех диалогов, поэтому воспользуемся еще одним методом для получения списка всех диалогов — messages.getDialogs. Получив списки диалогов, мы получим идентификаторы юзеров, которые в последующем сможем передавать в метод для получения фотографий.
И, конечно же, не обойтись без библиотеки для работы с API вконтакте, которая умеет выполнять нужные функции. Я воспользуюсь своей — golang-vk-api
Диалогов, также как и фотографий. нельзя получить больше 200, поэтому напишем функцию для получения всех диалогов, где будем использовать смещение:
После того, как получили все диалоги, можно получить непосредственно сами фотографии, используя метод, упомянутый в самом начале:
Прежде чем преступить к загрузке всех фотографий, напишем пару необходимых методов:
Проверка существования папки
Создание папки
Получение имени, с которым будет сохраняться файл, из ссылки
У фотографии не всегда доступны все размеры, поэтому напишем метод для выборки наибольшего размера
Загрузка файла
В основной функции получаем все диалоги, для каждого диалога получаем все фотографии и загружаем по 10 штук одновременно в отдельную для юзера папку
Вот и все. Полный код по ссылке
В документации присутствует метод 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
}
}
}
Вот и все. Полный код по ссылке