Автоматизация ВК через API | C# Request

Dmitriy Ka

Client
Регистрация
03.05.2016
Сообщения
584
Благодарностей
343
Баллы
63
Всем привет! С вами Дмитрий!
В данном кейсе я расскажу, как можно автоматизировать работу с VK через API без использования браузера. Работать все будет гораздо быстрей и стабильней, чем мы будем делать все то же самое, используя браузер и кликая по элементам. :dm:

Для работы нам поможет документация ВК.

Первым делом, чтобы начать создавать магию запросов, нам нужно получить access_token, без него не получится отправить ни один запрос к API ВК.
Если мы пойдем в документацию ВК в раздел "Ключи доступа и способы получения access_token", то тут нас встречает непонятная и сложная информация, прочитав которую мы понимаем, что для того, чтобы получить наш заветный access_token, нам нужно прилично потрудиться, а если еще и автоматизировать такое, то нужно будет потратить очень много времени ради заветного токена.

Но я вас сейчас обрадую, для получения access_token достаточно сделать один запрос - и ключ у нас в кармане.8-)

*В топике я буду выкладывать сниппеты запросов, для работы которых использую свой код. Чтобы у вас все заработало, нужно добавить данный код к себе в шаблон в раздел "Общий код".
C#:
public class ZennoRequest
{
    public int MaxRedirectCount { get; set; }
    public int Timeout { get; set; }
    public bool UseRedirect { get; set; }
    public bool UseOriginalUrl { get; set; }
    public bool ThrowExceptionOnError { get; set; }
    public bool RemoveDefaultHeaders { get; set; }
    public string ContentPostingType { get; set; }
    public string Proxy { get; set; }
    public string Encoding { get; set; }
    public string Cookies { get; set; }
    public string UserAgent { get; set; }
    public string DownloadPath { get; set; }
    public ZennoLab.InterfacesLibrary.Enums.Http.ResponceType ResponseType { get; set; }
    public ICookieContainer CookieContainer { get; set; }


    private readonly IZennoPosterProjectModel _project;

    public ZennoRequest(IZennoPosterProjectModel project)
    {
        _project = project;
        Proxy = _project.GetProxy();
        UserAgent = _project.Profile.UserAgent;
        ResponseType = ZennoLab.InterfacesLibrary.Enums.Http.ResponceType.BodyOnly;
        ContentPostingType = "application/x-www-form-urlencoded";
        Encoding = "UTF-8";
        Timeout = 30000;
        UseRedirect = true;
        MaxRedirectCount = 3;
        UseOriginalUrl = false;
        ThrowExceptionOnError = true;
        CookieContainer = project.Profile.CookieContainer;
        RemoveDefaultHeaders = false;
    }

    /// <summary>
    /// Get запрос.
    /// </summary>
    /// <param name="url">Url запроса</param>
    /// <param name="headers">Заголовки запроса</param>
    /// <returns></returns>
    public string Get(string url, string[] headers = null) => Request(ZennoLab.InterfacesLibrary.Enums.Http.HttpMethod.GET, url, "", headers);
    /// <summary>
    /// Post запрос.
    /// </summary>
    /// <param name="url">Url запроса</param>
    /// <param name="content">Тело запроса</param>
    /// <param name="headers">Заголовки запроса</param>
    /// <returns></returns>
    public string Post(string url, string content, string[] headers = null) => Request(ZennoLab.InterfacesLibrary.Enums.Http.HttpMethod.POST, url, content, headers);

    private string Request(ZennoLab.InterfacesLibrary.Enums.Http.HttpMethod httpMethod, string url, string content, string[] headers = null)
    {
        return ZennoPoster.HTTP.Request(
            method: httpMethod,
            url: url,
            content: content,
            AdditionalHeaders: headers,
            contentPostingType: ContentPostingType,
            proxy: Proxy,
            Encoding: Encoding,
            respType: ResponseType,
            Timeout: Timeout,
            Cookies: Cookies,
            UserAgent: UserAgent,
            UseRedirect: UseRedirect,
            MaxRedirectCount: MaxRedirectCount,
            DownloadPath: DownloadPath,
            UseOriginalUrl: UseOriginalUrl,
            throwExceptionOnError: ThrowExceptionOnError,
            cookieContainer: CookieContainer,
            removeDefaultHeaders: RemoveDefaultHeaders);
    }
}

Для получения токена я использую запрос, который используют все Android приложения ВК. В нем достаточно передать наш логин:пароль и получить токен со всеми правами и бессрочный.

В переменную ZP кладем аккаунт формат логин:пароль
Также у нас должны быть переменные в ZP accessToken и userId . Сюда будут добавлен токен и юзер ИД после успешного запроса.
C#:
var account = project.Variables["account"].Value.Split(':');

var login = account[0];
var password = account[1];

var req = new ZennoRequest(project);

var url = "https://oauth.vk.com/token";
var content = $"client_id=2274003" +
    $"&client_secret=hHbZxrka2uZ6jB1inYsH" +
    $"&https=1" +
    $"&libverify_support=1" +
    $"&scope=all" +
    $"&grant_type=password" +
    $"&username={login}" +
    $"&password={password}" +
    $"&2fa_supported=1" +
    $"&v=5.229" +
    $"&lang=ru" +
    $"&sak_version=1.121" +
    $"&api_id=2274003";


var resp = req.Post(url, content);

var json = JObject.Parse(resp);

project.Variables["accessToken"].Value = json.SelectToken("access_token").ToString();
project.Variables["userId"].Value = json.SelectToken("user_id").ToString();

Самую сложную часть прошли.:D
Теперь пора научиться немного автоматизировать работу с ВК через API.

Для тренировки работы с API предлагаю сделать:
- Загрузку аватара
- Размещение постов на личной странице
- Удаление всего вышенаписанного безобразия:ah:

Для загрузки картинок ВК используется 3 запроса (примеры будут для аватара):
1) Получение URL для загрузки аватара.
2) Загрузка картинки на сервер.
3) Сохранение картинки.

C#:
var dirAvatar = project.ExecuteMacro(project.Variables["dirAvatar"].Value);
var userId = project.Variables["userId"].Value;
var accessToken = project.Variables["accessToken"].Value;

var req = new ZennoRequest(project);
var rnd = new Random();
const string api = "https://api.vk.com/method/";
string url, content, uploadServer;


#region[1 Запрос: получаем сервер]
url = api + "photos.getOwnerPhotoUploadServer";
content = $"owner_id={userId}" +
    $"&v=5.229" +
    $"&access_token={accessToken}";

var resp = req.Post(url, content);
if (resp.StartsWith("{\"response"))
{
    uploadServer = JObject.Parse(resp)["response"].SelectToken("upload_url").ToString();
}
else throw new Exception("Не получили сервер для загрузки аватара.");
#endregion

#region[2 Запрос: загружаем аватар на сервер]
//Получаем файл из директории для загрузки
var lstAvatar = Directory.EnumerateFiles(dirAvatar)
            .Where(x => x.EndsWith("jpg") || x.EndsWith("jpeg") || x.EndsWith("png"))
            .ToList();
var pathAvatar = lstAvatar[rnd.Next(lstAvatar.Count())];

//Подготовка аватара
var bitmap = new Bitmap(pathAvatar);
var imgWidth = bitmap.Width.ToString();
uploadServer += $"&_square_crop=0,0,{imgWidth}";

var boundaryId = rnd.Next(1000000, 9999990);
var fileName = Path.GetFileName(pathAvatar);
var fileFormat = Path.GetExtension(pathAvatar);

//Загрузка аватара
var reqMultipat = new ZennoRequest(project) { ContentPostingType = "multipart/form-data" };

content = $"--VK-FILE-UPLOAD-BOUNDARY-{boundaryId}{boundaryId}\r\n" +
        $"Content-Disposition: form-data; name=\"photo\"; filename=\"{fileName}\"\r\n" +
        $"Content-Type: image/{fileFormat}\r\n\r\n" +
        $"{pathAvatar}\r\n" +
        $"--VK-FILE-UPLOAD-BOUNDARY-{boundaryId}{boundaryId}--";

resp = reqMultipat.Post(uploadServer, content);

string server, photo, hash;
if(resp.StartsWith("{\"server"))
{
    var json = JObject.Parse(resp);
    server = json.SelectToken("server").ToString();
    photo = json.SelectToken("photo").ToString();
    hash = json.SelectToken("hash").ToString();
}
else throw new Exception("Не удалось загрузить Аватар на сервер.");
#endregion

#region[3 Запрос: Сохраняем аватар]
url = api + "photos.saveOwnerPhoto";

content = "skip_post=1" +
        $"&server={server}" +
        $"&hash={hash}" +
        $"&photo={photo}" +
        "&v=5.229" +
        $"&access_token={accessToken}";

resp = req.Post(url, content);

if (resp.StartsWith("{\"response"))
{
    project.SendInfoToLog("Аватар Загружен!", true);
}
else throw new Exception("Не удалось сохранить Аватар");
#endregion

Так как мы будем добавлять пост с картинкой, нам нужно загрузить картинку для поста, она делается похожим образом, только меняется первый и третий запросы:
1) Получение URL для загрузки картинки.
2) Загрузка картинки на сервер.
3) Сохранение картинки.
4) Создаем пост

C#:
var userId = project.Variables["userId"].Value;
var accessToken = project.Variables["accessToken"].Value;
var dirImg = project.ExecuteMacro(project.Variables["dirPostImg"].Value);
var fileText = project.ExecuteMacro(project.Variables["filePostText"].Value);

var req = new ZennoRequest(project);
var rnd = new Random();
const string api = "https://api.vk.com/method/";
string url, content, text, uploadUrl;


#region[#1 Подготовка картинки и текста для поста]
//Получаем путь для картинки.
var lstImg = Directory.EnumerateFiles(dirImg)
                        .Where(x => x.EndsWith("jpg") || x.EndsWith("jpeg") || x.EndsWith("png"))
                        .ToList();
var pathImg = lstImg[rnd.Next(lstImg.Count())];

//Получаем текст.
var tempLst = project.Lists["temp"];
lock (CommonCode.SyncObject)
{
    tempLst.Bind(fileText);

    text = tempLst[0];
    tempLst.RemoveAt(0);
    tempLst.Add(text);
}
#endregion

#region[#2 Запрос: получаем сервер для загрузки картинки]
url = api + "photos.getWallUploadServer";

content = $"group_id={userId}" +
    $"&v=5.229" +
    $"&access_token={accessToken}";

var resp = req.Post(url, content);

if (resp.StartsWith("{\"response"))
{
    uploadUrl = JObject.Parse(resp)["response"].SelectToken("upload_url").ToString();
}
else throw new Exception("Не удалось получить сервер для загрузки картинки");
#endregion

#region[#3 Запрос: загружаем картинку на сервер]

var boundaryId = rnd.Next(1000000, 9999990);
var fileName = Path.GetFileName(pathImg);
var fileFormat = Path.GetExtension(pathImg);

//Загрузка аватара
var reqMultipat = new ZennoRequest(project) { ContentPostingType = "multipart/form-data" };

content = $"--VK-FILE-UPLOAD-BOUNDARY-{boundaryId}{boundaryId}\r\n" +
        $"Content-Disposition: form-data; name=\"photo\"; filename=\"{fileName}\"\r\n" +
        $"Content-Type: image/{fileFormat}\r\n\r\n" +
        $"{pathImg}\r\n" +
        $"--VK-FILE-UPLOAD-BOUNDARY-{boundaryId}{boundaryId}--";

resp = reqMultipat.Post(uploadUrl, content);

string server, photo, hash;
if (resp.StartsWith("{\"server"))
{
    var json = JObject.Parse(resp);
    server = json.SelectToken("server").ToString();
    photo = json.SelectToken("photo").ToString();
    hash = json.SelectToken("hash").ToString();
}
else throw new Exception("Не удалось загрузить Картинку на сервер");
#endregion

#region[#4 Запрос: Сохраняем Картинку]
url = api + "photos.saveWallPhoto";

content = $"group_id={userId}" +
        $"&photo={photo}" +
        $"&server={server}" +
        $"&hash={hash}" +
        $"&v=5.229" +
        $"&access_token={accessToken}";

resp = req.Post(url, content);

string photoId;
if (resp.StartsWith("{\"response"))
{
    var json = JObject.Parse(resp)["response"][0];
    var id = json.SelectToken("id").ToString();
    var ownerId = json.SelectToken("owner_id").ToString();

    photoId = $"photo{ownerId}_{id}";

}
else throw new Exception("Не удалось сохранить Картинку");
#endregion

#region[#5 Запрос: Добавляем пост]
url = api + "wall.post";

content = $"owner_id={userId}" +
    $"&message={text}" +
    $"&attachments={photoId}" +
    "&v=5.229" +
    $"&access_token={accessToken}";

resp = req.Post(url, content);

if (resp.StartsWith("{\"response"))
{
    project.SendInfoToLog("Пост добавлен");
}
else throw new Exception("Не удалось добавить пост");
#endregion

Мы научились загружать фотографии, теперь было бы не плохо научиться их удалять, чтобы очищать свой аккаунт от старых фото. Это может понадобиться, если вы купили аккаунты, а они заполнены, и вам это не нравится. Или вы использовали аккаунт для одной задачи, а потом вам нужно стало использовать его для другой задачи.

Для очистки фото, нам понадобится 2 запроса:
1) Получить все фото
2) Удалить фото

C#:
var userId = project.Variables["userId"].Value;
var accessToken = project.Variables["accessToken"].Value;

var req = new ZennoRequest(project);
const string api = "https://api.vk.com/method/";
string url, content;


#region[1 Запрос: получаем все фото]
var lstPhoto = new List<string>();
url = api + "photos.getAll";

content = $"owner_id={userId}" +
         $"&v=5.229" +
         $"&access_token={accessToken}";

var resp = req.Post(url, content);

if (resp.StartsWith("{\"response"))
{
    var dataPhotos = JObject.Parse(resp)["response"]["items"];
    foreach (var photo in dataPhotos)
    {
        var id = photo.SelectToken("id").ToString();
        var owner = photo.SelectToken("owner_id").ToString();
        lstPhoto.Add($"photo{owner}_{id}");
    }
}
else throw new Exception("Не удалось собрать фото");
#endregion

#region[2 Запрос: Удаляем фото]
url = api + "photos.delete";

foreach (var photo in lstPhoto)
{
    var id = photo.Split('_')[1];
    content = $"owner_id={userId}" +
         $"&photo_id={id}" +
         $"&v=5.229" +
         $"&access_token={accessToken}";

    resp = req.Post(url, content);
}
#endregion

Для очистки постов используется такая же логика, нам понадобится 2 запроса:
1) Получить все посты
2) Удалить Пост

Поздравляю! Теперь вы умеете автоматизировать работу с ВК, не используя браузер, а так же знаете про документацию ВК и можете попробовать сами автоматизировать создание и заполнение групп, лайкинг, фоловинг и многое другое.


#####################
Мой Телеграм Канал
Мой Youtube Канал
Личный блог на Форуме

Поддержка автора: Донат
#####################
 
Последнее редактирование модератором:

Shakeway

Client
Регистрация
03.02.2017
Сообщения
36
Благодарностей
9
Баллы
8

garikn

Client
Регистрация
03.08.2021
Сообщения
104
Благодарностей
13
Баллы
18
На китайском получилось
 

Dmitriy Ka

Client
Регистрация
03.05.2016
Сообщения
584
Благодарностей
343
Баллы
63
  • Спасибо
Реакции: garikn

garikn

Client
Регистрация
03.08.2021
Сообщения
104
Благодарностей
13
Баллы
18

slavyn8213

Client
Регистрация
28.10.2018
Сообщения
177
Благодарностей
40
Баллы
28
122175
в общий код добавил-но ошибка-где я накосячил?
 
  • Спасибо
Реакции: Dmitriy Ka

Sambo7

Client
Регистрация
23.05.2018
Сообщения
299
Благодарностей
13
Баллы
18
Благодарю за видео.
Какую пользу дает шаб?
Про посты всё понятно, много трафика удаётся поднять на свеже опубликованных постах?
Прошло время когда можно было залогиниться в вк при помощи логина и пароля... Сейчас уже ведь вход в вк при помощи смс...
Или я что-то путаю?
 
  • Спасибо
Реакции: Dmitriy Ka

Dmitriy Ka

Client
Регистрация
03.05.2016
Сообщения
584
Благодарностей
343
Баллы
63
Посмотреть вложение 122175в общий код добавил-но ошибка-где я накосячил?
Добавьте еще эту строчку в Общий код вкладка Директивы Using:
using Global.ZennoLab.Json.Linq;

Должно получиться так:
122201

Почему-то разработчики не добавляют ее чтобы все работало из коробки(
 
  • Спасибо
Реакции: slavyn8213

Dmitriy Ka

Client
Регистрация
03.05.2016
Сообщения
584
Благодарностей
343
Баллы
63
Благодарю за видео.
Какую пользу дает шаб?
Про посты всё понятно, много трафика удаётся поднять на свеже опубликованных постах?
Прошло время когда можно было залогиниться в вк при помощи логина и пароля... Сейчас уже ведь вход в вк при помощи смс...
Или я что-то путаю?
"Какую пользу дает шаб?" - Автоматизация рутины, через API можно автоматизировать почти все в ВК.

"Много трафика удаётся поднять на свеже опубликованных постах?" - сложный вопрос, много факторов, зависит от метода работы.

"Сейчас уже ведь вход в вк при помощи смс..." - на запросах можно это обойти, есть свой регер, который регает по типу логин:пасс без смс.
 
  • Спасибо
Реакции: Sambo7

Кто просматривает тему: (Всего: 1, Пользователи: 0, Гости: 1)