C# / .NET Core 調用javascript方法(適用於Windows/Linux平台)


使用背景

最近在使用c#(dotnetcore)編寫一些爬蟲進行實踐,在模擬網站請求的時候,往往在請求參數里含有一個根據請求內容實時生成的token,通過對前端js文件的調用,找到了用來生成token的js方法,但是將js代碼翻譯成c#代碼有點太費勁費時,於是想要找到一個這樣的框架,可以直接從c#調用js的方法並返回值。

嘗試的框架

我前前后后嘗試了好幾個框架,大致分為兩類1、瀏覽器內核/無頭瀏覽器,2、js引擎/框架。主要區別是一個是模擬瀏覽器去請求一個完整的網頁,另外的則是單單去計算調用一個純粹的js方法,我更傾向於后者。

瀏覽器內核/無頭瀏覽器

js引擎/框架

最后根據項目需求選擇了JavaScriptEngineSwitcher.ChakraCore,因為支持在linux平台運行,在Windows上運行的時候需要額外引用JavaScriptEngineSwitcher.ChakraCore.Native.win-x64,在linux上運行時需要額外引用JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64,這兩個包可以同時引用。

PuppeteerSharp時puppeteer的c#版本,由於我在使用時好像發現他在運行時需要額外下載內容,且下載失敗,故沒有做仔細研究便棄用。

Microsoft.AspNetCore.NodeServices,功能滿足需求,可以在linux上運行,但由於在使用Systemd運行應用時無法調用Nodejs,且暫未找到問題原因,且方法接口等已經標注過時,考慮以后的維護升級考慮,只作備選方案。

Microsoft.ClearScript.V8功能滿足需求,但是不滿足在linux上運行,故不選擇。

代碼示例
注:以下代碼實例均是使用目標框架 .Net Core 3.1,先在Windows平台測試,后在Linux下測試,使用到的框架應該也均有 .Net Framework 的版本支持,但對於此點並沒有做驗證。測試做的並不完全,如有疏漏或錯誤歡迎指正。

Microsoft.AspNetCore.NodeServices

先決條件:Nodejs環境,設置環境變量NODE_PATH,腳本需要按照Nodejs的模塊導出的格式將方法寫成導出的形式。

c#

using Microsoft.AspNetCore.NodeServices; using Microsoft.Extensions.DependencyInjection; namespace NodeServicesDemo { public class Demo { [Obsolete] private readonly INodeServices _nodeServices; private IServiceCollection _nodeServiceCollections = new ServiceCollection(); [Obsolete] public XieChengScrapyService(IServiceProvider services) { _nodeServiceCollections.AddNodeServices(options => { options.NodeInstanceOutputLogger = loggerFactory.CreateLogger("nodeservices"); options.ProjectPath = Environment.CurrentDirectory; // 設置項目為擋牆項目目錄 }); var sp = _nodeServiceCollections.BuildServiceProvider(); _nodeServices = sp.GetRequiredService<INodeServices>(); }
     
public async void Execute()
{
var result = await _nodeServices.InvokeAsync<string>("./Scripts/demo", input);  // 要調用的模塊的相對路徑,參數
} } }

demo.js(Nodejs模塊)

function m(t) { return p(v(t)) } module.exports = function (callback, t){ var output = m(t); callback(null, output); }

 

Microsoft.ClearScript

引入Nuget包,Microsoft.ClearScript

demo.js(原生javascript)

function m(t, e, r) { p(v(t)) }

注:除了NodeServices中,其他要調用的js文件均為以此為示例,后面不再復述。

引入Nuget包,Microsoft.ClearScript

using Microsoft.ClearScript.JavaScript; using Microsoft.ClearScript.V8;

初始化

using (var engine = new V8ScriptEngine()) { engine.DocumentSettings.AccessFlags = Microsoft.ClearScript.DocumentAccessFlags.EnableFileLoading; engine.DefaultAccess = Microsoft.ClearScript.ScriptAccess.Full; // 這兩行是為了允許加載js文件
    // do something
}

調用腳本有多種方案。

方案一:調用engine.ComplieDocument方法直接加載js文件,然后調用engine.Execute將引入的腳本執行一遍,這樣后面就可以調用js方法,m就是js的方法名,調用格式與js相同。

V8Script script = engine.CompileDocument(ScriptFilePath);   // 載入並編譯js文件, 然后Execute, 就可以直接調用。
engine.Execute(script); var result = engine.Script.m("SHAURCOnewayduew&^%5d54nc'KH");  

方案二:將要導入的js方法的代碼讀出來,然后執行一遍,再調用要執行的js方法

string scriptContent = string.Empty; using(FileStream fs = new FileStream(ScriptFilePath, FileMode.Open, FileAccess.Read)) { using(StreamReader sr = new StreamReader(fs)) { scriptContent = sr.ReadToEnd().Replace("\r\n", ""); } } engine.Execute(scriptContent); // 取得腳本里的所有內容,Execute一下,然后,調用engine.Script.func(x,y)執行一下。

var result = engine.Script.m("SHAURCOnewayduew&^%5d54nc'KH");

直接調用執行調用的方法的js代碼也是可以的

string scriptContent = string.Empty; using(FileStream fs = new FileStream(ScriptFilePath, FileMode.Open, FileAccess.Read)) { using(StreamReader sr = new StreamReader(fs)) { scriptContent = sr.ReadToEnd().Replace("\r\n", ""); } } scriptContent += "m(\"SHAURCOnewayduew&^%5d54nc'KH\");";  // 在js代碼的結尾加上執行的代碼
 engine.Execute(scriptContent); // 取得腳本里的所有內容,Execute一下,然后,調用engine.Script.func(x,y)執行一下。

var result = engine.Script.m("SHAURCOnewayduew&^%5d54nc'KH");

特殊情況,調用js全局方法,就是調用js的默認的那些方法

var result = engine.Invoke("encodeURIComponent", "SHAURCOnewayduew&^%5d54nc'KH"); //只能調用全局方法,如encodeURIComponent

 

JavaScriptEngineSwitcher.ChakraCore

引入Nuget包,JavaScriptEngineSwitcher.ChakraCore,JavaScriptEngineSwitcher.ChakraCore.Native.win-x64,JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64

using JavaScriptEngineSwitcher.ChakraCore; using JavaScriptEngineSwitcher.Core;

使用,同樣是先把js文件執行一遍,然后再去調用要使用的方法。

string ScriptPath = Path.Combine(Directory.GetCurrentDirectory(), "Scripts", "demo.js"); var switcher = JsEngineSwitcher.Current; switcher.EngineFactories.Add(new ChakraCoreJsEngineFactory()); switcher.DefaultEngineName = ChakraCoreJsEngine.EngineName; IJsEngine engine = JsEngineSwitcher.Current.CreateDefaultEngine(); engine.ExecuteFile(ScriptPath, Encoding.UTF8); string result = engine.CallFunction<string>("m", "SHAURCOnewayduew&^%5d54nc'KH");

 

參考資料:

webmote-org/netcore-javascript

microsoft/ChakraCore -- github

Microsoft/ClearScript -- V8ScriptEngine Class

koopla/NodeServices -- github

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM