Z-TehnOman Part3. Автоматическая установка и работа с NodeJS напрямую из проекта Zennoposter

semafor

Client
Регистрация
27.12.2016
Сообщения
289
Реакции
410
Баллы
63
Всем добра и мира!

Из названия статьи понятно, что мы будем прикручивать к шаблону ZP NodeJS — наш шаблон будет автоматически устанавливать ноду, автоматически устанавливать npm-пакет, с которым будем работать, ну и посылать данные на обработку и получать в ответах результаты. И все это — непосредственно из кода, без всяких батников.

91905


РАЗБОР ТЕМЫ ХОЧЕТСЯ НАЧАТЬ С БЛАГОДАРНОСТЕЙ:


  • @Astraport — за появление NodeJS в моем поле зрения (прочитав именно его статью, я сделал себе заметку о необходимости потыкать палочкой этот инструмент)
  • @Lord_Alfred за идею (Универсальный экстрактор контента) с использованием readability библиотеки, а так же за офигенные снипеты для очистки контента, проверки ответов на get-запросы, адские регулярки и все остальное, что присутствует в шаблоне, прикрепленном к указанной статье. Множество решений из нее не однажды использовал.
  • @marsht за замечательную статью о JS
НЕСКОЛЬКО СЛОВ О NODEJS, И ПОТЕНЦИАЛЕ ЕГО ИСПОЛЬЗОВАНИЯ В ПРОЕКТАХ ZP

NodeJS — это среда для выполнения JavaScript. Того самого js, который выполняется, пожалуй, на 99,9% страниц, загружаемых нами в браузере. А еще, того самого JS, с помощью которого владельцы сайтов и веб-сервисов получают информацию о браузере (и об ОС — fingerprint, timezone, язык системы, шрифты и т.п.), генерируют куки, определяют и отшивают ботов (тем самым нанося непоправимый вред тонкой душевной организации многих владельцев Zennoposter).

Хм... Есть JS, который мешает жить ботоводам, и есть среда для его запуска, с огромным количеством готовых к использованию инструментов, позволяющих всячески препарировать тот самый, злобный, JavaScript. Как по мне, потенциал у связки ноды с ZP огромен, и оценить его я не возьмусь, хотя бы потому, что я пока не знаю JavaScript.

Сразу напрашивается логичный вопрос — а как же пользовать такое богатство, если ключиков к нему нет?

Конечно же учить JS хотя бы на начальном уровне, учить NodeJS, чтобы понимать хотя бы примерно — что, куда и как. Но в принципе, можно обойтись — поиск пакетов по ключу cli выдает 4769 страниц. Омайнгот!!! 4769 страниц, содержащих пакеты под решение каких-то задач, с прикрученным к ним интерфейсом командной строки! Определенно, назначение многого из того, что находится на этих страницах будет пока абсолютно мутным, но, все же, я уверен, что в такой куче найдется несколько жемчужин для каждого владельца ZP.

Чтобы подтвердить утверждение о жемчужинах, я плавно перейду к шаблону, который будет прикреплен к статье. Мне понадобился функционал, который как я помнил, выкладывал @Lord_Alfred в одном из конкурсов, однако, найдя соотв. топик, оказалось, что обновилась библиотека, входящая в состав шаблона и написанная на Go, и карета превратилась в тыкву (без правок на неизвестном мне языке шаб не работал). Библиотека на C# с гитхаба с разбегу не запустилась, а нужно было срочно, и вот я нашел несколько разных вариантов библиотек под NodeJS — в том числе и та, которую интегрировал в шаблон.

Установить и запустить NodeJS вручную не сложнее чем поставить любой опенсорсный софт, еще часа-полтора проб, ошибок и чтения коротенького мануала к пакету с cli-интерфейсом и вуаля! Можно подключать к ZP.

ПАРА СЛОВ О NPM-ПЕКЕТЕ (БИБЛИОТЕКЕ) READABILITY-CLI, ИСПОЛЬЗУЕМОЙ В ПРИЛАГАЕМОМ ШАБЛОНЕ


Эта библиотека как раз и выполняет основную работу по извлечению статей из html-страниц, и делает это очень неплохо, а с учетом наработок от Lord_Alfred — отлично. Взаимодействие с пакетом происходит через командную строку (что немаловажно, с учетом нубства в js). К тому же, есть возможность получить не только очищенный текст, но и текст статьи в виде очищенного html, из которого замечательно извлекаются анкоры ссылок и информация об изображениях. Пакет не имеет никаких регалий на https://www.npmjs.com, у него совсем немного скачиваний, и к тому же, у последней версии что-то изменилось (после пары месяцев использования решил обновить либу), и часть работавшего функционала перестала работать — пришлось откатывать на предыдущий билд. Но тем не менее, я быстро получил готовое решение, которое смог интегрировать в шаблон, и после уже не спеша размышлять, оставить этот вариант или искать что-то поинтересней.

ИНФОРМАЦИЯ О ШАБЛОНЕ

Шаблон, прикрепленый к статье, представляет ценность скорее как обучающий, хотя весь заявленный функционал выполняет и работает в многопотоке. По-сути, это чуть расширенная версия «Универсального экстрактора» от Lord_Alfred, только с использованием NodeJS (и скорее всего, более медленнная версия). Итак, о функционале:

  • Автоматическая установка NodeJS и npm-пакетов
  • Парсинг материалов с сайтов и их очистка от безполезной инфы — реклама, менюхи, ссылки и прочие лишние данные.
  • Получение и сохранение форматированного, очищенного html статьи
  • Получение и сохранение текста статьи
  • Опционально — получение и сохранение анкоров и url ссылок, присутствующих в статьях
  • Опционально — получение и сохранение alt, title и href картинок статей
  • Опционально же — сохранение изображений, присутствующих в статье
  • Сохранение в SQLite информации о полученных материалах (можно было и сами материалы туда же, но решил не заморачиваться)
Шаб работает без использования браузера, внутри него крутится несколько внешних библиотек (будут прикреплены к шаблону):

  1. HtmlAgilityPack. Очень люблю эту либу за огромные возможности при парсинге (отдельно за отсутствие глюков при обработке всевозможных кастомных атрибутов, с которыми натерпелся при использовании штатных возможностей ZP). В принципе, она избыточна здесь, но выпиливать ее не стал.
  2. System.Data.SQLite - библиотека для работы с БД SQLite
  3. Dapper, Dapper.Contrib — либы, позволяющие пользовать ОРМ Dapper, о них я писал в прошлом конкурсе статей — MySQL, SQLite.
91906


Чтобы эти либы функционировали, нужно поместить их в папку ExternalAssemblies Zennoposter.

Нелишним будет и небольшой обзор директив using:
  • using System.Xml — для работы с HtmlAgilityPack,
  • using HtmlAgilityPack — собственно сам HtmlAgilityPack
  • using Dapper; using Dapper.Contrib; using Dapper.Contrib.Extensions — работа с Dapper
  • using System.Data.SQLite — работа с БД SQLite
  • using NodejsHelper; using Scrapping; using ZHelper — мои сборки, их внутренности подробно откомментированы в общем коде
  • System.Web — не смог вспомнить, для чего он тут, выпиливать не стал
  • И есть еще одно пространство имен, используя которое мы и будем автоматизировать установку ноды и общаться с ней через командную строку — using System.Diagnostics. Подробности далее.
91907



ОБЩЕНИЕ С КОМАНДНОЙ СТРОКОЙ ИЗ КОДА C#

Класс Process из сборки System.Diagnostics, позволяет запускать и останавливать любой процесс ОС (т.е. в том числе и исполняемый файл). Используя этот класс, мы будем запускать интерфейс командной строки cmd.exe и выполнять в нем команды, а также запустим инсталятор msi с аргументамми, позволяющими установить NodeJS. Код для вызова командной строки и выполнения команды (например, опредилить битность системы), выглядит вот так:

C#:
Развернуть Свернуть Копировать
string request = string.Empty;
    using(System.Diagnostics.Process process = new System.Diagnostics.Process())
    {
        process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
        process.StartInfo.FileName = "cmd.exe";
        process.StartInfo.Arguments = "/C wmic os get osarchitecture";
        process.StartInfo.UseShellExecute = false;
        process.StartInfo.CreateNoWindow = true;
        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.RedirectStandardInput = true;
        process.Start();
           
        while(!process.HasExited)
        {
            //сохранили ответ cmd
            request += process.StandardOutput.ReadToEnd();    
        }              
    }

Данный код позволяет выполнить в cmd однострочную команду, и получить в переменную результат ее выполнения. Здесь в параметр process.StartInfo.FileName мы передаем имя процесса (в нашем случае cmd.exe), а параметру process.StartInfo.Arguments команду, которую нужно выполнить - "/C wmic os get osarchitecture" (вернуть битность ОС), где ключ /C обозначет, что после выполнени команды, процесс должен быть завершен. А как быть если нужно выполнить последовательность команд? Решение имеется:

C#:
Развернуть Свернуть Копировать
//Здесь я пишу пустой список, но в реале в нем команды для выполнения
List<string> cmd = new List<string>();
    string request = string.Empty;
    using(System.Diagnostics.Process process = new System.Diagnostics.Process())
    {
        process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
        process.StartInfo.FileName = "cmd.exe";
        process.StartInfo.UseShellExecute = false;
        process.StartInfo.CreateNoWindow = true;
        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.RedirectStandardInput = true;              
        process.Start();
        using (StreamWriter sw = process.StandardInput)
        {
            if (sw.BaseStream.CanWrite)
            {
                foreach(string str in cmd)
                {
                    sw.WriteLine(str);
                }
            }
        }
               
        while(!process.HasExited)
        {
            //сохранили ответ cmd
            request += process.StandardOutput.ReadToEnd();  
        }
    }

Так как общения с cmd в шабе дофига, я создал в общем коде статический класс, и запихал все это хозяйство туда, см. сборку ZHelper. Там же добавлен метод, позволяющий запустить инсталлер msi(а в принципе, любой процесс Windows), и передать ему в качестве аргумента команду установить NodeJS из указанного url:

C#:
Развернуть Свернуть Копировать
    /// <summary>
    /// Статический класс, содержащий методы для запуска соманд в командной строке windows
    /// </summary>
    public static class ZHelperExt
    {
        /// <summary>
        /// Метод запускает командную строку windows и выполняет однострочную команду, возвращает строку, в которую сохраняет результат выполнения (если команда которая выполняется имеет его)
        /// </summary>
        /// <param name="cmd">Команда, котороую надо выполнить в командной строке</param>
        /// <returns></returns>
        public static string RunSingleCmdReturnToString(string cmd)
        {
            string request = string.Empty;
            using(System.Diagnostics.Process process = new System.Diagnostics.Process())
            {
                process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
                process.StartInfo.FileName = "cmd.exe";
                process.StartInfo.Arguments = cmd;
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.CreateNoWindow = true;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardInput = true;
                process.Start();
                   
                while(!process.HasExited)
                {
                    //сохранили ответ cmd
                    request += process.StandardOutput.ReadToEnd(); //русский язык кракозябрами, потом надо подумать    
                }              
                return request;
            }
        }
       
        /// <summary>
        /// Метод запускает командную строку windows и выполняет несколько команд в той последовательности, в которой они указаны во входном списке cmd
        /// </summary>
        /// <param name="cmd">Список команд для выполнения в командной строке</param>
        /// <returns>Строка, содержащая результаты исполнения команд</returns>
        public static string RunListCmdReturnToString(List<string> cmd)
        {
            string request = string.Empty;
            using(System.Diagnostics.Process process = new System.Diagnostics.Process())
            {
                process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
                process.StartInfo.FileName = "cmd.exe";
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.CreateNoWindow = true;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardInput = true;              
                process.Start();
                using (StreamWriter sw = process.StandardInput)
                {
                    if (sw.BaseStream.CanWrite)
                    {
                        foreach(string str in cmd)
                        {
                            sw.WriteLine(str);
                        }
                    }
                }
                       
                while(!process.HasExited)
                {
                    //сохранили ответ cmd
                    request += process.StandardOutput.ReadToEnd(); //русский язык кракозябрами, потом надо подумать    
                }
               
                return request;
            }
        }
        /// <summary>
        /// Метод позволяет запускать любые программы windows
        /// </summary>
        /// <param name="progName">Имя программы, которую надо запустить</param>
        /// <param name="workingDir">Рабочая директория для запуска процесса (НЕ директория где находится исполняемый файл!)</param>
        /// <param name="cmd">Аргументы запуска программы</param>
        public static void RunSomeProgramm(string progName, string workingDir, string cmd)
        {
            using(System.Diagnostics.Process process = new System.Diagnostics.Process())
            {
                process.StartInfo.FileName = progName;
                process.StartInfo.WorkingDirectory = workingDir;
                process.StartInfo.Arguments = cmd;
                process.StartInfo.Verb = "runas";
                process.Start();
                process.WaitForExit(100000);
            }
        }
       
    }

УСТАНОВКА NODEJS ИЗ ШАБЛОНА

Что нам нужно, чтобы установить NodeJS:
  • Разрядность ОС
  • Ссылка на скачивание NodeJS
  • Также, стоит проверить, не установлен ли уже в системе NodeJS, а если установлен, соответствует ли его билд требованиям npm-пакета
Для этих целей в общем коде создан класс jsHelper, при создании объекта которого в конструкторе получается вся нужная информация:

C#:
Развернуть Свернуть Копировать
/// <summary>
    /// Класс получает информацию о разрядности ОС, наличии в системе установленного NodeJS и его версии,
    /// а так же получает ссыль на скачивание актуальной версии соотв. разрядности
    /// </summary>
    public class jsHelper
    {
        public int bitness;
        public const string osBitnessReg = @"\d{2}";
        public string downloadUrl{get;}
        public int installedNodejsVersion;
        public int minVersionNodejs = 122000;
        private string installerDownluadXpath = @"//th[contains(text(), 'Windows Installer (.msi)')]/following-sibling::td/a";
        private string installedNodejsVersionReg = @"(?<=v).*";
        private string getOsBitnessCommand = "/C wmic os get osarchitecture";
        public string getNodeVersionCommand = "/C node -v";
       
        /// <summary>
        /// Конструктор класса. При вызове, получает разрядность системы, проверяет наличие nodejs и его версию, получает сссылки на загрузку инсталлера
        /// </summary>
        /// <param name="hdoc"></param>
        public jsHelper(HtmlAgilityPack.HtmlDocument hdoc)
        {
            #region Get OS bitness
                string request = ZHelper.ZHelperExt.RunSingleCmdReturnToString(this.getOsBitnessCommand);
                bitness = Int32.Parse(Regex.Match(request, osBitnessReg).Value); //OS bitness value — 32 or 64
            #endregion
               
            #region Get nodejs downloadUrl
               
                var nodes = hdoc.DocumentNode.SelectNodes(installerDownluadXpath);
                foreach(var node in nodes)
                {
                    string innerhtml = node.InnerText;
                    int linkBit = Int32.Parse(Regex.Match(innerhtml, osBitnessReg).Value);
                    if(bitness == linkBit)
                    {
                        downloadUrl = node.Attributes["href"].Value;
                    }
                }
               
            #endregion
               
            #region Check is NodeJS installed and get installed version              
               
                request = ZHelper.ZHelperExt.RunSingleCmdReturnToString(this.getNodeVersionCommand);
                if(string.IsNullOrEmpty(request) || request.Substring(0, 1) != "v")
                {
                    installedNodejsVersion = 0;
                   
                }
                else if(!string.IsNullOrEmpty(request) || request.Substring(0, 1) == "v")
                {
                   
                    try
                    {
                        request = Regex.Match(request, installedNodejsVersionReg).Value.Replace(".", "").Trim();
                        if(request.Length == 5) request = request + "0";
                        installedNodejsVersion = Int32.Parse(request);
                       
                    }
                    catch{installedNodejsVersion = 0;}
                }
                else installedNodejsVersion = 0;
            #endregion
        }
    }

В кубике мы делаем get-запрос к странице скачивания NodeJS и передаем полученный html в документ HtmlAgilityPack, создаем экземпляр класса jsHelper (в свойства которого при создании передается разрядность ОС, версия NodeJS если установлен и 0 если нет). В качестве параметра мы передаем в метод документ HtmlAgilityPack, из которого, при создании экземпляра класса, в свойство downloadUrl записывается url для скачивания последней стабильной версии NodeJS нужной разрядности. И далее, если NodeJS не установлен, или его версия ниже требуемой, происходит установка. После завершения установки мы проверяем что установка выполнена:

C#:
Развернуть Свернуть Копировать
#region get page nodejs download
string get = ZennoPoster.HTTP.Request(
                method:ZennoLab.InterfacesLibrary.Enums.Http.HttpMethod.GET,
                url:jsConst.nodejsPageDownloadUrl,
                Encoding:@"UTF-8",
                respType:ZennoLab.InterfacesLibrary.Enums.Http.ResponceType.HeaderAndBody,
                Timeout:30000,
                throwExceptionOnError:true
    );
#endregion


//HtmlAgilityPack new document
HtmlDocument hdoc = new HtmlDocument();
hdoc.LoadHtml(get);

//Create new object JHelper
jsHelper helper = new jsHelper(hdoc);

#region check and install NodeJS
    //Если установленная версия меньше минимальной или если nodejs не установлен, устанавливаем его из url
    if(helper.minVersionNodejs > helper.installedNodejsVersion)
    {
        //setup to default folder (c:\Program Files)
        string workingDir = @"C:\temp\";
        string progName = "msiexec";
        string cmd = string.Format(@" /quiet /i {0}", helper.downloadUrl);
        //if need install nodejs to custom folder
        //string cmd = string.Format(@" /quiet /i {0} INSTALLDIR=F:\Zenno_tmpl\contentor\nodejs", helper.downloadUrl);
        ZHelperExt.RunSomeProgramm(progName, workingDir, cmd);
        project.SendInfoToLog(helper.minVersionNodejs.ToString()+" - "+helper.installedNodejsVersion.ToString(), false);
       
        Thread.Sleep(200);
       
        //check that installation is complete
        string res = ZHelper.ZHelperExt.RunSingleCmdReturnToString(helper.getNodeVersionCommand);
        if(!string.IsNullOrEmpty(res))project.SendInfoToLog(string.Format("Install NodeJS version {0}", res.Trim()), true);
    }
    else
    {
        string res = ZHelper.ZHelperExt.RunSingleCmdReturnToString(helper.getNodeVersionCommand);
        project.SendInfoToLog(string.Format("NodeJS is already installed, NodeJS version = {0}", res.Trim()), true);
    }
#endregion

Вот и все, наш шаблон выполнил установку NodeJS.


УСТАНОВКА NPM-ПАКЕТА READABILITY-CLI

Тут все просто — при установке, NodeJS автоматически подтягивает необходимые для работы модули, в том числе и собственный интерфейс командной строки (доступный из cmd), и менеджер пакетов npm.
Поэтому мы проверяем, установлен ли нужный нам пакет, и если нет, устанавливаем (опять же, с использованием методов статического класса ZHelperExt). Подробную справку по cli-интерфесу NodeJS можно вызвать командой node -h. Кубик установки выглядит так:

C#:
Развернуть Свернуть Копировать
//устанавливаем пакет readability-cli
string cmd = @"/C npm install -g readability-cli@2.3.5";//инсталим не последнюю, а выборочную версию пакета, ключ -g задает глобальную установку пакета
string request = ZHelper.ZHelperExt.RunSingleCmdReturnToString(cmd);
Thread.Sleep(100);

//Проверяем, что установка выполнена успешно
cmd = @"/C readable -V";
request = ZHelper.ZHelperExt.RunSingleCmdReturnToString(cmd);
if(request.Trim().Contains("readability-cli v"))return true;
else throw new Exception("npm pacage is not install");

Теперь в нашем NodeJS стоит пакет, позволяющий очищать html-исходник от информационного мусора и служебных данных.

Весь процесс получения web-страницы, ее подготовки и обработки я показывать не буду, покажу лишь, кубик запроса к NodeJS, для получения очищенного html статьи:

C#:
Развернуть Свернуть Копировать
#region Get current working folder and and the current drive
    string request = ZHelper.ZHelperExt.RunSingleCmdReturnToString("/C cd");
    string folderCurrent = request.Split(new string[]{@"\"}, StringSplitOptions.RemoveEmptyEntries)[0];
    string folderNeeded = project.Variables["FOLDER_page"].Value.Split(new string[]{@"\"}, StringSplitOptions.RemoveEmptyEntries)[0];
#endregion

#region Create list of command to cmd
    List<string> cmd = new List<string>();
    //If the current and desired disks are different
    if(folderCurrent != folderNeeded)
    {
        string mess = string.Format("Current folder disk: {0}; Needed folder disk: {1}", folderCurrent, folderNeeded);
       
        cmd.Add("/C");
        cmd.Add(string.Format("cd /d {0}", project.Variables["FOLDER_page"].Value));
        cmd.Add(string.Format("readable input.html > output.html -p html-title,html-content -b {0}", project.Variables["url"].Value));
    }
    //if the current and desired disks are equal
    else if(folderCurrent == folderNeeded)
    {
        cmd.Add("/C");
        cmd.Add(string.Format("cd {0}", project.Variables["FOLDER_page"].Value));
        cmd.Add(string.Format("readable input.html > output.html -p html-title,html-content -b {0}", project.Variables["url"].Value));
    }
#endregion

Thread.Sleep(500);

#region Send command to cmd and check result
    string result = ZHelper.ZHelperExt.RunListCmdReturnToString(cmd);
    string[] resultArr = result.Split(new string[]{"\r\n"}, StringSplitOptions.RemoveEmptyEntries);
   
    foreach(string str in resultArr)
    {
        string s = str.Trim();
        project.SendInfoToLog(s, false);      
    }

#endregion

Что происходит в кубике:
  • Сначала мы проверяем, соответствует текущий диск диску, в котором находится целевая папка текущей страницы
  • После, создаем список команд, которые будем передавать в cmd:
  • Переходим в папку текущей страницы, в которой лежит html-исходник
  • В строке cmd.Add(string.Format("readable input.html > output.html -p html-title,html-content -b {0}", project.Variables["url"].Value)); указываем readability-cli, что нужно обработать файл input.html и после обработки передать html-title и html-content в файл output.html. C ключом -b передается url страницы, чтобы алгоритмы readability сохранили в атрибутах href и src валидные абслоютные пути.
  • Ну и собственно передаем в cmd список команд на исполнение


ЗАКЛЮЧЕНИЕ

Можно заметить, что мы использовали среду для выполнения js и запускали в ней вполне прикладные задачи совсем не используя JavaScript-код. До того, как остановиться на варианте с cli-интерфейсом, я пытался запустить пакеты с необходимостью написания js, но получалось небыстро и не просто, и как по-мне, cli-вариант совсем не плох, пока не хватает знаний на большее.


ССЫЛКИ

Страница скачивания NodeJS
Репозиторий с npm-пакетами для NodeJS
Страница пакета readability-cli на npmjs.com
Описание всех команд, параметров и ключей принимемых readability-cli на gitlab
Подробную справку по cli-интерфесу NodeJS можно вызвать командой node -h.

P.S. Прилагаемый шаблон написан в версии ZP 7.4.0
 
Номер конкурса статей
  1. Семнадцатый конкурс статей
Тема статьи
  1. Нестандартные хаки

Вложения

Последнее редактирование модератором:
Спасибо за статью! А можно версию понизить до 7.3.1.1 ?
 
  • Спасибо
Реакции: semafor
Прикол. Прямо сегодня днем понадобилось как раз в сторону NodeJS посмотреть. А тут такой подгон в тему )))
 
  • Спасибо
Реакции: semafor
Статья очень даже годная, молодчина!:ay:

Кстати, да, отдельно - HtmlAgilityPack - избыточен, так, как в зенку он уже встроен, если что:al:

C#:
Развернуть Свернуть Копировать
using Global.Zennolab.HtmlAgilityPack;
 
Полезная статья, всегда хотел NodeJS попробовать применять для своих целей.
 
  • Спасибо
Реакции: semafor
Спасибо за статью! А можно версию понизить до 7.3.1.1 ?
Понизил до 7.2.0. (прикрепил к этому посту)

Прикол. Прямо сегодня днем понадобилось как раз в сторону NodeJS посмотреть. А тут такой подгон в тему )))
Здорово, что так метко вышло )))
Кстати, да, отдельно - HtmlAgilityPack - избыточен, так, как в зенку он уже встроен, если что:al:
Спасибо. А я все по-олдскульному, да )))
 

Вложения

  • Спасибо
Реакции: zxz27 и SHILY
  • Спасибо
Реакции: semafor и Брат
..."тем самым нанося непоправимый вред тонкой душевной организации многих владельцев Zennoposter " Классно сказано :D :ay:
 
  • Спасибо
Реакции: lzlmrf и semafor
только сегодня идея посетила, а у вас такого же на Pythone нету? )
Тут бы с js разобраться...

..."тем самым нанося непоправимый вред тонкой душевной организации многих владельцев Zennoposter " Классно сказано :D :ay:
Ну слов ведь из песни не выбросишь :D
 
Добавлено видео от автора
 
  • Спасибо
Реакции: SHILY и semafor
Дошли руки позапускать разные приложения, используя класс Process. Несколько примеров:

Запускаем Memu.
У меня в диспетчере создано несколько виртуалок Memu. Чтобы запустить одну из них, я выполнил код:

C#:
Развернуть Свернуть Копировать
List<string> cmd = new List<string>();
string request = string.Empty;

cmd.Add(@"cd /d D:\Program Files\Microvirt\MEmu");
cmd.Add(@"memuc.exe start -i 1");

using(System.Diagnostics.Process process = new System.Diagnostics.Process())
{
    process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
    process.StartInfo.FileName = "cmd.exe";
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.CreateNoWindow = true;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.RedirectStandardInput = true;               
    process.Start();
    using (StreamWriter sw = process.StandardInput)
    {
        if (sw.BaseStream.CanWrite)
        {
            foreach(string str in cmd)
            {
                sw.WriteLine(str);
            }
        }
    }
            
    while(!process.HasExited)
    {
        //сохранили ответ cmd
        request += process.StandardOutput.ReadToEnd();     
    }
    
    return request;
}

Где cmd.Add(@"cd /d D:\Program Files\Microvirt\MEmu"); — переход в папку, в которой находится exe-шник MEmuConsole.exe, а cmd.Add(@"memuc.exe start -i 1"); запуск из MEmuConsole.exe виртуалки под номером 1 (там еще есть возможность запускать по имени, но в моей системе оно не заработало).

Запуск Nox.
Так же, имеется несколько виртуалок Nox. Чтобы запустить какую-либо из них по имени — нужно перейти в директорию с Nox.exe и выполнить следующую команду nox.exe "title:NoxPlayer1", где NoxPlayer1 - имя виртуалки:

C#:
Развернуть Свернуть Копировать
List<string> cmd = new List<string>();
string request = string.Empty;

cmd.Add(@"cd C:\Program Files (x86)\Nox\bin");
cmd.Add(@"nox.exe ""title:NoxPlayer1""");

using(System.Diagnostics.Process process = new System.Diagnostics.Process())
{
    process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
    process.StartInfo.FileName = "cmd.exe";
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.CreateNoWindow = true;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.RedirectStandardInput = true;               
    process.Start();
    using (StreamWriter sw = process.StandardInput)
    {
        if (sw.BaseStream.CanWrite)
        {
            foreach(string str in cmd)
            {
                sw.WriteLine(str);
            }
        }
    }
            
    while(!process.HasExited)
    {
        //сохранили ответ cmd
        request += process.StandardOutput.ReadToEnd();     
    }
    
    return request;
}
 
  • Спасибо
Реакции: GreenWay, SHILY и volody00
я тоже заинтересовался node после статьи @Astraport
какое то время потратил решение вопроса - как поставить ноду в докер контейнер. на данный момент ставлю её в ручнном режиме на впску/ки из интерфейса портейнера. неудобно, хотя достаточно быстро и работает. интересно было почитать спасибо за статью.
 
  • Спасибо
Реакции: semafor
я тоже заинтересовался node после статьи @Astraport
какое то время потратил решение вопроса - как поставить ноду в докер контейнер. на данный момент ставлю её в ручнном режиме на впску/ки из интерфейса портейнера. неудобно, хотя достаточно быстро и работает. интересно было почитать спасибо за статью.
Да, докер интересная штука. Пока не погружался, стоит в очереди
 
  • Спасибо
Реакции: seodamage
отличная тех. статья, однозначно в закладки кину для более подробного изучения:bf:
 
  • Спасибо
Реакции: semafor
Отличная статья! Приятно читать! Тоже положу в закладки, думаю пригодиться!
 
  • Спасибо
Реакции: semafor
Статья очень даже годная, молодчина!:ay:

Кстати, да, отдельно - HtmlAgilityPack - избыточен, так, как в зенку он уже встроен, если что:al:

C#:
Развернуть Свернуть Копировать
using Global.Zennolab.HtmlAgilityPack;
Спасибо за уточнение не знал ,либу постоянно подгружать приходилось
 
Как быть если кодировка страницы не UTF-8?
 

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