rediffusion
11/7/2017 - 8:30 PM

Работаем с API ok.ru

Добавить в папку C:\Program Files (x86)\ZennoLab\RU\ZennoPoster Standard\5.11.3.0\Progs\ExternalAssemblies\ - Newtonsoft.Json

Статические блоки: 1.Ссылки из GAC: System System.Core System.Drawing System.Data mscorlib System.Windows.Forms Microsoft.CSharp Newtonsoft.Json System.Xml 2.OwnCodeUsings 3.InputSettings 4.Таблица

//ПРИМЕЧАНИЯ //1. Перечисление методов по работе с группами - на этой странице: https://apiok.ru/dev/methods/rest/group/ //2. Метод для парсинга участников группы: group.getMembers: https://apiok.ru/dev/methods/rest/group/group.getMembers //3. Перечисление методов по работе с пользователями - на этой странице: https://apiok.ru/dev/methods/rest/users/ //4. Метод для получения информации об отдельном пользователе (users.getInfo): https://apiok.ru/dev/methods/rest/users/users.getInfo

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;
using System.Text.RegularExpressions;
using ZennoLab.CommandCenter;
using ZennoLab.InterfacesLibrary;
using ZennoLab.InterfacesLibrary.ProjectModel;
using ZennoLab.InterfacesLibrary.ProjectModel.Collections;
using ZennoLab.InterfacesLibrary.ProjectModel.Enums;
using ZennoLab.Macros;
using Global.ZennoExtensions;
using ZennoLab.Emulation;
using System.Security.Cryptography;
using System.Xml;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace ZennoLab.OwnCode
{
    /// <summary>
	/// A simple class of the common code
	/// </summary>
	public class CommonCode
	{
		/// <summary>
		/// Lock this object to mark part of code for single thread execution
		/// </summary>
		public static object SyncObject = new object();

		// Insert your code here
	}
}
List<string> lstUsersId = new List<string>(); //Список для хранения id-шников пользователей, состоящих в группе

//Парамерты, применяемые во всех запросах
string strAppKey = project.Variables["cfg_application_key"].Value;
string strFormat = "json"; //Возможные значения: json, xml
bool blnShowDataToLog = Convert.ToBoolean(project.Variables["bln_show_data_to_log"].Value);
string strGroupID = project.Variables["cfg_group_id"].Value; //id группы для парсинга участников (демонстрирую на группе https://ok.ru/yahudeiu)

//Параметры, примеряемые только при поиске пользователей
string strAccessToken = project.Variables["cfg_access_token"].Value;
string strSessionSecretKey = project.Variables["cfg_session_secret_key"].Value;
string strUsersPerStep = project.Variables["cfg_group_users_per_step"].Value;
int intMaxUsers = Convert.ToInt32(project.Variables["cfg_max_users"].Value);
string strDirection = "FORWARD"; //Возможные значения: FORWARD, BACKWARD, AROUND
string strStatuses = "ACTIVE,PASSIVE"; //Возможные значения: ACTIVE, ADMIN, BLOCKED, MAYBE, MODERATOR, PASSIVE; несколько статусов можно перечислять через запятую

//Параметры, применяемые только при парсинге пользователей
string strApplicationSecretKey = project.Variables["cfg_application_secret_key"].Value;
int intQueryUidsPerStep = Convert.ToInt32(project.Variables["cfg_users_query_per_step"].Value);
string strDataFields = "first_name,last_name,birthday,gender,last_online,location";

/*-------------------ТЕСТИРУЕМЫЙ КОД - НИЖЕ-------------------*/


string strAnchor = String.Empty;
string strQuery = String.Format("&application_key={0}&count={1}&direction={2}&format={3}&method=group.getMembers&statuses={4}&uid={5}", strAppKey, strUsersPerStep, strDirection, strFormat, strStatuses, strGroupID);

if (strAnchor!=String.Empty) strQuery = "anchor=" + strAnchor + strQuery; //добавляем анкор, если запрашиваем страницу вывода отличную от первой

//вычисляем MD5-подпись для запроса (не забудьте прописать using System.Security.Cryptography; в своём проекте)
MD5 md5 = MD5.Create();
string strMD5 = BitConverter.ToString(md5.ComputeHash(Encoding.UTF8.GetBytes(strQuery.Replace("&",String.Empty)+strSessionSecretKey))).Replace("-",string.Empty).ToLower();

//формируем итоговый запрос
strQuery = strQuery + "&sig=" + strMD5 + "&access_token=" + strAccessToken;

//выполняем запрос
string strAPIResponse = ZennoPoster.HttpGet("https://api.ok.ru/fb.do?" + strQuery, "30", "UTF-8", ZennoLab.InterfacesLibrary.Enums.Http.ResponceType.BodyOnly);

return strAPIResponse;
IZennoTable tblResult = project.Tables["tbl_result"];
tblResult.Clear();

List<string> lstUsersId = new List<string>(); //Список для хранения id-шников пользователей, состоящих в группе

//Парамерты, применяемые во всех запросах
string strAppKey = project.Variables["cfg_application_key"].Value;
string strFormat = "xml"; //Возможные значения: json, xml
bool blnShowDataToLog = Convert.ToBoolean(project.Variables["bln_show_data_to_log"].Value);
string strGroupID = project.Variables["cfg_group_id"].Value; //id группы для парсинга участников (демонстрирую на группе https://ok.ru/yahudeiu)
project.SendWarningToLog(String.Format("Начинаем парсить участников группы {0}", strGroupID), true);

//Параметры, примеряемые только при поиске пользователей
string strAccessToken = project.Variables["cfg_access_token"].Value;
string strSessionSecretKey = project.Variables["cfg_session_secret_key"].Value;
string strUsersPerStep = project.Variables["cfg_group_users_per_step"].Value;
int intMaxUsers = Convert.ToInt32(project.Variables["cfg_max_users"].Value);
string strDirection = "FORWARD"; //Возможные значения: FORWARD, BACKWARD, AROUND
string strStatuses = "ACTIVE,PASSIVE"; //Возможные значения: ACTIVE, ADMIN, BLOCKED, MAYBE, MODERATOR, PASSIVE; несколько статусов можно перечислять через запятую

string strAnchor = String.Empty;
while(true) {
    string strQuery = String.Format("&application_key={0}&count={1}&direction={2}&format={3}&method=group.getMembers&statuses={4}&uid={5}", 
		strAppKey, strUsersPerStep, strDirection, strFormat, strStatuses, strGroupID);
	if (strAnchor!=String.Empty) strQuery = "anchor=" + strAnchor + strQuery; //добавляем анкор, если запрашиваем страницу вывода отличную от первой
	//вычисляем MD5-подпись для запроса (не забудьте прописать using System.Security.Cryptography; в своём проекте)
	MD5 md5 = MD5.Create();
	string strMD5 = BitConverter.ToString(md5.ComputeHash(Encoding.UTF8.GetBytes(strQuery.Replace("&",String.Empty)+strSessionSecretKey))).Replace("-",string.Empty).ToLower();
	//формируем итоговый запрос
	strQuery = strQuery + "&sig=" + strMD5 + "&access_token=" + strAccessToken;
	//выполняем запрос
	string strAPIResponse = ZennoPoster.HttpGet("https://api.ok.ru/fb.do?" + strQuery, "", "UTF-8", ZennoLab.InterfacesLibrary.Enums.Http.ResponceType.BodyOnly);
	
	//помещаем результат запроса в поток, создаём объект xmlDataDocument, грузим в него данные из потока
	MemoryStream msXmlData = new MemoryStream(Encoding.UTF8.GetBytes(strAPIResponse)); //создаём поток из массива байтов, полученного из результата выполнения запроса
	XmlDataDocument xmldoc = new XmlDataDocument();
	xmldoc.Load(msXmlData);
	
	//формируем множество узлов (нод) xml-документа с заданным именем (member)
	XmlNodeList nodeList = xmldoc.GetElementsByTagName("member");
	project.SendWarningToLog(String.Format("Пользователей в ответе на запрос: {0} (не все будут добавлены в список)", nodeList.Count), true);
	
	//перебираем множество полученных нод
	foreach (XmlNode userNode in nodeList) {
		string strUserId = userNode.SelectSingleNode("userId")!=null ? userNode.SelectSingleNode("userId").InnerText : String.Empty; //тру-программисты ругаются на тернарные операторы, но пофиг :)
		string strUserStatus = userNode.SelectSingleNode("status")!=null ? userNode.SelectSingleNode("status").InnerText : String.Empty;
		
		if (blnShowDataToLog) project.SendInfoToLog(string.Format("{0} - {1}", strUserId, strUserStatus));
		
		if (strUserStatus=="ACTIVE") {
			lstUsersId.Add(strUserId); //добавляем в список для дальнейшего парсинга только "активных" пользователей
			//ВНИМАНИЕ! ограничить статусы, возвращаемые по запросу, мы могли ещё на этапе формирования запроса. Отдельный подсчёт собранных пользователей сделан для демонстрационных целей
			if (intMaxUsers>0&&lstUsersId.Count>=intMaxUsers) break;
		}		
	}
	
	strAnchor = xmldoc.GetElementsByTagName("anchor")[0].InnerText;
	bool blnHasMore = Convert.ToBoolean(xmldoc.GetElementsByTagName("has_more")[0].InnerText);
	
	if (!blnHasMore) {
		project.SendWarningToLog("Распарсили всех участников", true);
		break;
	}
	
	if (intMaxUsers>0&&lstUsersId.Count>=intMaxUsers) {
		project.SendWarningToLog("Распарсили максимальное количество пользователей, указанное в настройках", true);
		break;
	}
}

//раскомментируйте строку ниже, если хотите сохранить собранные uid пользователей в текстовый файл в папке проекта (для тестирования)
//File.WriteAllLines(project.Path + "uids.txt", lstUsersId.GetRange(0, lstUsersId.Count-1));

project.SendWarningToLog(String.Format("Начинаем парсить пользователей. В списке {0} записей", lstUsersId.Count), true);

//Параметры, применяемые только при парсинге пользователей
string strApplicationSecretKey = project.Variables["cfg_application_secret_key"].Value;
int intQueryUidsPerStep = Convert.ToInt32(project.Variables["cfg_users_query_per_step"].Value);
string strDataFields = "first_name,last_name,birthday,gender,last_online,location";

int intCurrentListPosition = 0;
int intListElementsToTake = 0;
while(true) {
	string strUids = String.Empty;
	
	if (intCurrentListPosition>=lstUsersId.Count) {
		project.SendWarningToLog("Распарсили всех пользователей. Проверьте содержимое файла Result.xlsx", true);
		break;
	}
	
	if (intCurrentListPosition+intQueryUidsPerStep>lstUsersId.Count) {
		intListElementsToTake = lstUsersId.Count-intCurrentListPosition;
	}else{
		intListElementsToTake = intQueryUidsPerStep;
	}
	project.SendWarningToLog(String.Format("Получаем элементы списка. current: {0}, next: {1}, to take: {2}", intCurrentListPosition, intCurrentListPosition+intListElementsToTake, intListElementsToTake));
	
	strUids = String.Join(",", lstUsersId.GetRange(intCurrentListPosition,intListElementsToTake));
	intCurrentListPosition = intCurrentListPosition+intListElementsToTake;
	
	string strQuery = String.Format("application_key={0}&fields={1}&format={2}&method=users.getInfo&uids={3}", 
		strAppKey, strDataFields, strFormat, strUids
	);
	
	//вычисляем MD5-подпись для запроса (не забудьте прописать using System.Security.Cryptography; в своём проекте)
	MD5 md5 = MD5.Create();
	string strMD5 = BitConverter.ToString(md5.ComputeHash(Encoding.UTF8.GetBytes(strQuery.Replace("&",String.Empty)+strApplicationSecretKey))).Replace("-",string.Empty).ToLower();
	//формируем итоговый запрос
	strQuery = strQuery + "&sig=" + strMD5;
	
	//выполняем запрос
	string strAPIResponse = ZennoPoster.HttpGet("https://api.ok.ru/fb.do?" + strQuery, "", "UTF-8", ZennoLab.InterfacesLibrary.Enums.Http.ResponceType.BodyOnly);

	MemoryStream msXmlData = new MemoryStream(Encoding.UTF8.GetBytes(strAPIResponse)); //создаём поток из массива байтов, полученного из результата выполнения запроса
	XmlDataDocument xmldoc = new XmlDataDocument();
	xmldoc.Load(msXmlData);
	
	XmlNodeList nodeList = xmldoc.GetElementsByTagName("user");
	project.SendWarningToLog(String.Format("Получены данные по {0} пользователей в ответе на запрос", nodeList.Count), true);
	
	foreach (XmlNode userNode in nodeList) {
		string strId = userNode.SelectSingleNode("uid")!=null ? userNode.SelectSingleNode("uid").InnerText : String.Empty;
		string strFirstName = userNode.SelectSingleNode("first_name")!=null ? userNode.SelectSingleNode("first_name").InnerText : String.Empty;
		string strLastName = userNode.SelectSingleNode("last_name")!=null ? userNode.SelectSingleNode("last_name").InnerText : String.Empty;
		string strBirthDay = userNode.SelectSingleNode("birthday")!=null ? userNode.SelectSingleNode("birthday").InnerText : String.Empty;
		string strGender = userNode.SelectSingleNode("gender")!=null ? userNode.SelectSingleNode("gender").InnerText : String.Empty;
		//обратите внимание: ниже получаем подсвойство
		string strCountry = userNode.SelectSingleNode("location/country_name")!=null ? userNode.SelectSingleNode("location/country_name").InnerText : String.Empty;
		string strCity = userNode.SelectSingleNode("location/city")!=null ? userNode.SelectSingleNode("location/city").InnerText : String.Empty;
		
		if (blnShowDataToLog) project.SendInfoToLog(string.Format("{0} - {1} {2}", strId, strFirstName, strLastName), true);
		tblResult.AddRow(new String[]{strId, strFirstName, strLastName, strBirthDay, strGender, strCountry, strCity});
		}
}
IZennoTable tblResult = project.Tables["tbl_result"];
tblResult.Clear();

List<string> lstUsersId = new List<string>(); //Список для хранения id-шников пользователей, состоящих в группе

//Парамерты, применяемые во всех запросах
string strAppKey = project.Variables["cfg_application_key"].Value;
string strFormat = "json"; //Возможные значения: json, xml
bool blnShowDataToLog = Convert.ToBoolean(project.Variables["bln_show_data_to_log"].Value);
string strGroupID = project.Variables["cfg_group_id"].Value; //id группы для парсинга участников (демонстрирую на группе https://ok.ru/yahudeiu)
project.SendWarningToLog(String.Format("Начинаем парсить участников группы {0}", strGroupID), true);

//Параметры, примеряемые только при поиске пользователей
string strAccessToken = project.Variables["cfg_access_token"].Value;
string strSessionSecretKey = project.Variables["cfg_session_secret_key"].Value;
string strUsersPerStep = project.Variables["cfg_group_users_per_step"].Value;
int intMaxUsers = Convert.ToInt32(project.Variables["cfg_max_users"].Value);
string strDirection = "FORWARD"; //Возможные значения: FORWARD, BACKWARD, AROUND
string strStatuses = "ACTIVE,PASSIVE"; //Возможные значения: ACTIVE, ADMIN, BLOCKED, MAYBE, MODERATOR, PASSIVE; несколько статусов можно перечислять через запятую

string strAnchor = String.Empty;
while(true) {
    string strQuery = String.Format("&application_key={0}&count={1}&direction={2}&format={3}&method=group.getMembers&statuses={4}&uid={5}", 
		strAppKey, strUsersPerStep, strDirection, strFormat, strStatuses, strGroupID);
	if (strAnchor!=String.Empty) strQuery = "anchor=" + strAnchor + strQuery; //добавляем анкор, если запрашиваем страницу вывода отличную от первой
	//вычисляем MD5-подпись для запроса (не забудьте прописать using System.Security.Cryptography; в своём проекте)
	MD5 md5 = MD5.Create();
	string strMD5 = BitConverter.ToString(md5.ComputeHash(Encoding.UTF8.GetBytes(strQuery.Replace("&",String.Empty)+strSessionSecretKey))).Replace("-",string.Empty).ToLower();
	//формируем итоговый запрос
	strQuery = strQuery + "&sig=" + strMD5 + "&access_token=" + strAccessToken;
	//выполняем запрос
	string strAPIResponse = ZennoPoster.HttpGet("https://api.ok.ru/fb.do?" + strQuery, "", "UTF-8", ZennoLab.InterfacesLibrary.Enums.Http.ResponceType.BodyOnly);
	
	//Создаём объект Json и грузим в него контент, формируем множество узлов (токенов) с заданным именем
	JObject objParsedJson = JObject.Parse(strAPIResponse);
	IEnumerable<JToken> userTokens = objParsedJson.SelectTokens("members[*]", false);
	project.SendWarningToLog(String.Format("Пользователей в ответе на запрос: {0} (не все будут добавлены в список)", userTokens.Count()), true);
	
	//Перебираем множество токенов members
	foreach (JToken userToken in userTokens) {
		string strUserId = (string)userToken.SelectToken("userId");
		string strUserStatus = (string)userToken.SelectToken("status"); //примечание: можно отсекать "плохие" статусы на уровне JSONPath - при вызове метода SelectTokens
		
		if (blnShowDataToLog) project.SendInfoToLog(string.Format("{0} - {1}", strUserId, strUserStatus));
		
		if (strUserStatus=="ACTIVE") {
			lstUsersId.Add(strUserId); //добавляем в список для дальнейшего парсинга только "активных" пользователей.
			//ВНИМАНИЕ! ограничить статусы, возвращаемые по запросу, мы могли ещё на этапе формирования запроса. Отдельный подсчёт собранных пользователей сделан для демонстрационных целей
			if (intMaxUsers>0&&lstUsersId.Count>=intMaxUsers) break;
		}
	}
	
	strAnchor = (string)objParsedJson.SelectToken("anchor");
	bool blnHasMore = (bool)objParsedJson.SelectToken("has_more");
	
	if (!blnHasMore) {
		project.SendWarningToLog("Распарсили всех участников", true);
		break;
	}
	
	if (intMaxUsers>0&&lstUsersId.Count>=intMaxUsers) {
		project.SendWarningToLog("Распарсили максимальное количество пользователей, указанное в настройках", true);
		break;
	}
}

//раскомментируйте строку ниже, если хотите сохранить собранные uid пользователей в текстовый файл в папке проекта (для тестирования)
//File.WriteAllLines(project.Path + "uids.txt", lstUsersId.GetRange(0, lstUsersId.Count-1));

project.SendWarningToLog(String.Format("Начинаем парсить пользователей. В списке {0} записей", lstUsersId.Count), true);

//Параметры, применяемые только при парсинге пользователей
string strApplicationSecretKey = project.Variables["cfg_application_secret_key"].Value;
int intQueryUidsPerStep = Convert.ToInt32(project.Variables["cfg_users_query_per_step"].Value);
string strDataFields = "first_name,last_name,birthday,gender,last_online,location";

int intCurrentListPosition = 0;
int intListElementsToTake = 0;
while(true) {
	string strUids = String.Empty;
	
	if (intCurrentListPosition>=lstUsersId.Count) {
		project.SendWarningToLog("Распарсили всех пользователей. Проверьте содержимое файла Result.xlsx", true);
		break;
	}
	
	if (intCurrentListPosition+intQueryUidsPerStep>lstUsersId.Count) {
		intListElementsToTake = lstUsersId.Count-intCurrentListPosition;
	}else{
		intListElementsToTake = intQueryUidsPerStep;
	}
	project.SendWarningToLog(String.Format("Получаем элементы списка. current: {0}, next: {1}, to take: {2}", intCurrentListPosition, intCurrentListPosition+intListElementsToTake, intListElementsToTake));
	
	strUids = String.Join(",", lstUsersId.GetRange(intCurrentListPosition,intListElementsToTake));
	
	intCurrentListPosition = intCurrentListPosition+intListElementsToTake;
	
	string strQuery = String.Format("application_key={0}&fields={1}&format={2}&method=users.getInfo&uids={3}", 
		strAppKey, strDataFields, strFormat, strUids
	);
	
	//вычисляем MD5-подпись для запроса (не забудьте прописать using System.Security.Cryptography; в своём проекте)
	MD5 md5 = MD5.Create();
	string strMD5 = BitConverter.ToString(md5.ComputeHash(Encoding.UTF8.GetBytes(strQuery.Replace("&",String.Empty)+strApplicationSecretKey))).Replace("-",string.Empty).ToLower();
	//формируем итоговый запрос
	strQuery = strQuery + "&sig=" + strMD5;
	
	//выполняем запрос
	string strAPIResponse = ZennoPoster.HttpGet("https://api.ok.ru/fb.do?" + strQuery, "", "UTF-8", ZennoLab.InterfacesLibrary.Enums.Http.ResponceType.BodyOnly);
		
	JObject objParsedJson = JObject.Parse("{users:" + strAPIResponse + "}"); //на лету приводим получаемый json к "каноническому" виду
	IEnumerable<JToken> userTokens = objParsedJson.SelectTokens("users[*]", false);
	project.SendWarningToLog(String.Format("Получены данные по {0} пользователей в ответе на запрос.", userTokens.Count()), true);
	
	foreach (JToken userToken in userTokens) {
		string strId = (string)userToken.SelectToken("uid");
		string strFirstName = (string)userToken.SelectToken("first_name")  ?? String.Empty;
		string strLastName = (string)userToken.SelectToken("last_name")  ?? String.Empty;
		string strBirthDay = (string)userToken.SelectToken("birthday")  ?? String.Empty; //конструкция ?? проверяет значение слева, и если оно равно null, то присваивает значение справа
		string strGender = (string)userToken.SelectToken("gender")  ?? String.Empty;
		string strCountry = (string)userToken.SelectToken("location.countryName") ?? String.Empty; //обратите внимание: получаем подсвойство
		string strCity = (string)userToken.SelectToken("location.city") ?? String.Empty;
		
		if (blnShowDataToLog) project.SendInfoToLog(string.Format("{0} - {1} {2}", strId, strFirstName, strLastName), true);
		tblResult.AddRow(new String[]{strId, strFirstName, strLastName, strBirthDay, strGender, strCountry, strCity});
	}
}
using System.Security.Cryptography;
using System.Xml;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;