efSharp是一個基于Chromium的開源.NET庫,可以在C#應用程序中嵌入Web瀏覽器。以下是使用CefSharp內嵌網頁的步驟:
1. 安裝CefSharp NuGet包:在Visual Studio中打開NuGet包管理器,搜索并安裝CefSharp.WinForms或CefSharp.Wpf,根據應用程序的類型選擇相應的包。
2. 創建CefSharp瀏覽器控件:在Windows Forms或WPF應用程序中,創建一個WinForms或WPF控件,并將其設置為CefSharp的瀏覽器控件。例如,在Windows Forms應用程序中,可以使用以下代碼創建一個CefSharp瀏覽器控件:
```csharp
using CefSharp;
using CefSharp.WinForms;
public partial class Form1 : Form
{
private ChromiumWebBrowser browser;
public Form1()
{
InitializeComponent();
Cef.Initialize(new CefSettings());
browser=new ChromiumWebBrowser("https://www.google.com");
browser.Dock=DockStyle.Fill;
this.Controls.Add(browser);
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
Cef.Shutdown();
base.OnFormClosing(e);
}
}
```
3. 加載網頁:使用瀏覽器控件的Load方法加載網頁。例如,可以使用以下代碼加載本地HTML文件:
```csharp
browser.Load("file:///C:/path/to/index.html");
```
4. 與JavaScript交互:使用CefSharp提供的方法,可以在C#代碼和JavaScript代碼之間進行交互。例如,可以使用以下代碼在JavaScript中調用C#方法:
```csharp
public class JsObject
{
public void ShowMessage(string message)
{
MessageBox.Show(message);
}
}
browser.RegisterJsObject("myObj", new JsObject());
```
在JavaScript中,可以使用以下代碼調用C#方法:
```javascript
myObj.ShowMessage("Hello from JavaScript!");
```
上面的代碼會在C#中彈出一個消息框,顯示“Hello from JavaScript!”。
注意,為了保證安全性,CefSharp默認禁用了跨域請求。如果需要在CefSharp中進行跨域請求,可以在CefSharp的初始化代碼中添加以下選項:
```csharp
var settings=new CefSettings();
settings.CefCommandLineArgs.Add("disable-web-security", "1");
Cef.Initialize(settings);
```
這樣就可以在CefSharp中進行跨域請求了。
ello,親愛的小伙伴們,歡迎瀏覽“使用CefSharp和Javascript實現網絡爬蟲”。
最近我學習使用CefSharp和Javascript實現網絡爬蟲,自動獲取“悟空問答”站點的問題標題和地址信息,以及實現了“Bilibili”直播網站自定義彈幕自動回復,并做成了兩個工具小軟件,通過自動化程序提高效率。我準備將具體實現過程記錄下來,并分享給感興趣的小伙伴。
網絡爬蟲是一種按照一定規則,自動抓取互聯網上站點頁面信息的程序或腳本。說到網絡爬蟲,大家首先想到的是用Python實現,既高效又方便。不過對于不太熟悉Python,而對于熟悉C#或Javascript的小伙伴們,可以嘗試使用CefSharp和Javascript實現網絡爬蟲。此外,使用CefSharp和Javascript做成的工具軟件,具有方便安裝、發布,使用界面友好的優點,如果您還增加了不愿意公開的代碼邏輯,可以使用C#語言編譯加以保護。
首先我們簡單了解一下CefSharp和Javascript。CefSharp可以簡單理解為基于Google Chrome的開源版本—ChromiumEmbeddedFramework(CEF)實現的瀏覽器控件,CefSharp瀏覽器控件功能豐富且強大。 因為基于CEF,CefSharp支持Webkit & Chrome中實現的HTML5特性,并且在性能上,也接近Chrome。CefSharp是在C#應用程序中嵌入瀏覽器的最優選擇,支持WinForms和WPF兩種類型應用程序,英文好的小伙伴可以訪問 : http://cefsharp.github.io/ 網址進行深入學習。
而Javascript是一種廣泛使用的 Web前端編程語言,使用CefSharp和Javascript實現網絡爬蟲需要使用Javascript進行DOM操作,DOM(Document Object Model ,文檔對象模型)是一種用于操作XML、HTML文檔常用方式,JavaScript都能通過DOM接口操作到每個HTML節點。下一篇我將對使用CefSharp和Javascript實現網絡爬蟲過程中會使用到的C#和Javascript主要知識進行介紹 。
WPF 或 WinForms 應用程序選擇瀏覽器組件,對于那些搜索基于Chrome的解決方案的人來說, DotNetBrowser[1]和CefSharp[2]是最明顯的選擇。
本文是我們的客戶在考慮其項目的開源庫和商業庫時提出的最常見比較點的匯編。
CefSharp 實際上是 Chromium Embedded Framework[3] (CEF) 的 .NET 包裝器。包裝通過 C++/CLI 完成。
DotNetBrowser 在底層不使用 CEF 或 C++/CLI。相反,它采用了自己的方法直接與 Chromium 集成。它啟動一個功能齊全的 Chromium 引擎,并通過進程間通信 (IPC) 與其進行通信。
在 CefSharp 中,Chromium 引擎直接在您的 .NET 進程中初始化[4]。初始化和關閉都必須在主應用程序線程(通常是 UI 線程)中執行。在不同的線程中調用它們通常會導致凍結。
此外,每個進程可以執行一次初始化和關閉。這個限制來自 CEF 本身。在執行關閉后嘗試重新初始化 CefSharp 將導致錯誤。
CefSharp architecture
在 DotNetBrowser 中,Chromium 引擎在單獨的本機進程中進行初始化。不需要在主 UI 線程上執行此操作——即使在工作線程中也可以執行此操作。
您可以同時初始化和使用具有不同配置的多個 Chromium 引擎,這在 CefSharp 中是不可能的。您可以在不再需要 Chromium 時將其關閉并隨時重新初始化。
DotNetBrowser architecture
在單獨的進程中運行 Chromium 有更多優點:
對于 DotNetBrowser,Chromium 內部的錯誤不會導致 .NET 應用程序崩潰。此外,甚至可以在托管代碼中正確檢測和處理這一切。例如,如果發生這種情況,那么您可以重新初始化 Chromium 并恢復用戶會話。
由于其架構,CefSharp 不能在非默認 AppDomain 中使用[5]。因此,它不能用于通過 VSTO 插件或 Excel-DNA 將 Chromium 嵌入到 Office 應用程序中[6]。Office VSTO 將加載項加載到單獨的 AppDomain 中以進行隔離。DotNetBrowser 在非默認 AppDomain 中運行。事實上,可以在不同的 AppDomain 中創建多個 Chromium 引擎并同時使用它們。因此,DotNetBrowser 可用于創建 VSTO 加載項。
在針對 AnyCPU 的應用程序中使用 CefSharp 時,您會發現它在這些應用程序的 64 位環境中無法正常工作。
這兒有幾個選項[7]可以解決這個問題。其中之一是讓您的應用程序始終在 32 位模式下運行,另一個更復雜,需要修改項目文件(.csproj
或 .vbproj
)和代碼。
在 DotNetBrowser 中,AnyCPU 支持開箱即用。因此,不需要類似的調整。
視頻和音頻通常使用專有編解碼器進行編碼,例如 H.264 和 AAC。此媒體無法在 CefSharp 中播放。
要在 CefSharp 中啟用這些編解碼器,您需要在啟用專有編解碼器的情況下自行重建 CEF。這是一項相當復雜的任務,可能需要長達一個月的時間[8]。
在 DotNetBrowser 中默認禁用專有編解碼器??梢酝ㄟ^編程方式啟用它們,而無需重建庫:
InitializeCodecs.cs
IEngine engine=EngineFactory.Create(new EngineOptions.Builder
{
ProprietaryFeatures=ProprietaryFeatures.H264 | ProprietaryFeatures.Aac
}.Build());
Chromium 通過利用操作系統為它們提供的安全性來限制其渲染器和實用程序進程。此功能稱為 Chromium沙箱[9]。其主要目的是防止第三方代碼對計算機進行持久更改或訪問機密信息。
CefSharp 不支持 Chromium 沙箱[10]。這個限制來自 CEF 本身。
DotNetBrowser 支持沙箱并默認啟用。如有必要,可以在初始化期間將其禁用[11]。
CefSharp 在 .NET 進程中啟動 Chromium。這使您的應用程序容易受到 CEF 和 Chromium 中的漏洞的影響。如果惡意軟件獲得了對 Chromium 內存的訪問權,它也會獲得對 .NET 內存的訪問權。
DotNetBrowser 在單獨的進程中啟動 Chromium。
Chromium 漏洞保留在 Chromium 中。
現代 WPF 和 Windows 窗體應用程序通常是在設計器的幫助下在 Visual Studio 中創建的。這種方法總體上簡化了 UI 創建并節省了大量時間和精力。
CefSharp 提供有限的設計器支持[12]。如果應用程序本身以 x86 為目標,則其控件將在設計器中正確處理。AnyCPU 可能會工作,但尚未經過徹底測試。
DotNetBrowser 控件是純 UI 控件,它們在代碼中顯式初始化。您可以在設計器中不受任何限制地使用它們。安裝 NuGet 包或 VSIX 擴展后,BrowserView 控件出現在工具箱中。它可以像任何其他常規 UI 控件一樣被拖到窗體或窗口上。
CefSharp 提供 WPF 和 Windows 窗體支持。但是,它的 WPF 實現只能在離屏渲染模式[13]下工作。此實現具有有限的觸摸屏和 IME[14] 支持。
DotNetBrowser 在兩種渲染模式下同時支持 WPF 和 Windows 窗體。在硬件加速模式下,觸摸、手勢和 IME 由 Chromium 自行處理,因此它們開箱即用。在離屏模式下,存在一些已知的限制[15]。
以下是將 CefSharp 嵌入 WPF 窗口的方法:
<Window x:Class="CefSharpWpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpf="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"
Title="MainWindow" Height="450" Width="800">
<Grid>
<wpf:ChromiumWebBrowser Address="https://www.google.com"/>
</Grid>
</Window>
就是這樣,在最簡單的情況下,不再需要編寫代碼。但是,在這種情況下,CefSharp 初始化和關閉是隱式執行的,很難確定它是否已經在某個點初始化。
將 DotNetBrowser 嵌入 WPF 窗口的過程需要額外的步驟。
例如:
MainWindow.xaml
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WPF="clr-namespace:DotNetBrowser.Wpf;assembly=DotNetBrowser.Wpf"
x:Class="Embedding.Wpf.MainWindow"
Title="MainWindow" Height="480" Width="800" Closed="Window_Closed">
<Grid>
<WPF:BrowserView Name="browserView" />
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
private const string Url="https://www.google.com";
private readonly IBrowser browser;
private readonly IEngine engine;
public MainWindow()
{
// Create and initialize the IEngine instance.
EngineOptions engineOptions=new EngineOptions.Builder
{
RenderingMode=RenderingMode.HardwareAccelerated
}.Build();
engine=EngineFactory.Create(engineOptions);
// Create the IBrowser instance.
browser=engine.CreateBrowser();
InitializeComponent();
// Initialize the WPF BrowserView control.
browserView.InitializeFrom(browser);
browser.Navigation.LoadUrl(Url);
}
private void Window_Closed(object sender, EventArgs e)
{
browser?.Dispose();
engine?.Dispose();
}
}
在這里,大部分代碼都與 Chromium 實例和 IBrowser
實例的顯式初始化和關閉有關。UI 控件初始化是通過調用 InitializeFrom()
顯式執行的。這種方法可以更好地控制初始化和關閉過程,并且更容易自定義初始 Chromium 配置。
在 CefSharp 中,瀏覽器子進程的 默認 DPI 感知[16] 是 Per-Monitor。因此,桌面應用程序應具備 DPI 感知功能,才能在高 DPI 顯示器(DPI 比例設置大于 100% 的顯示器)上正確運行。在其他情況下,瀏覽器內容可能無法正確呈現,例如:
DotNetBrowser 以不同的方式支持高 DPI。在初始化過程中,它會檢查當前進程的 DPI 感知,并為相應的 Chromium 引擎設置匹配的 DPI 感知。因此,無需讓您的應用程序顯式識別 DPI 以避免在高 DPI 顯示上呈現偽影。
DotNetBrowser 和 CefSharp 都可以在沒有 UI 的應用程序中使用。
在 CefSharp 中,CefSharp.OffScreen.ChromiumWebBrowser
用于此目的。初始化過程通常保持不變。但是,如果您的代碼使用 async/await 模式,則需要使用同步上下文來確保在主線程上而不是在不同的工作線程上執行初始化和關閉。
要在沒有 UI 的應用程序中使用 DotNetBrowser,您需要像往常一樣執行初始化。在這種情況下,沒有需要初始化的 BrowserView
。即使您的代碼使用async/await模式,也無需創建和使用同步上下文。
這兩種產品都有許多可用的功能。在本文中,我將比較幾個最重要的,以展示 API 的不同之處。
在 CefSharp 中,您只能通過執行 JavaScript 調用來訪問 DOM。
例如:
CefSharpDom.cs
var script=@"
document.getElementsByName('question')[0].value='CefSharp Example';
document.getElementsByName('btn')[0].click();
";
browser.ExecuteScriptAsync(script);
DotNetBrowser 提供了豐富的 DOM API,可用于直接從 .NET 執行以下操作:
例如,以下是如何在 DotNetBrowser 中的網頁上執行相同的操作:
DotNetBrowserDom.cs
IDocument document=browser.MainFrame.Document;
(document.GetElementByName("question") as IInputElement).Value="DotNetBrowser Example";
document.GetElementByName("btn").Click();
因此,在 DotNetBrowser 中與網頁執行復雜的交互要方便得多。無需編寫難以調試和支持的復雜 JavaScript 代碼。DotNetBrowser 中的 DOM API 不是一組 JavaScript 調用的包裝器。它直接對 Blink 引擎進行 IPC 調用。
CefSharp 和 DotNetBrowser 都提供了在網頁上執行 JavaScript 的能力。
在 CefSharp 中,有兩種方法可用于此目的,ExecuteJavaScriptAsync
和 EvaluateScriptAsync
。兩者都可用于瀏覽器本身(通過擴展方法)或其中的一個框架:
CefSharpExecuteJs.cs
// Execute JavaScript without returning a result. The method returns
// before the script has actually been executed.
browser.ExecuteJavaScriptAsync("alert('All Resources Have Loaded');");
// Evaluate some Javascript code. The script will be executed asynchronously
// and the method returns a Task encapsulating the response from the
// JavaScript.
JavascriptResponse response=await browser.EvaluateScriptAsync(script);
然后使用 JavascriptResponse.Result
獲取執行結果。
可能的結果類型有 bool
, int
, long
, double
, string
, List<object>``,
IDictionary<string, object>``, 和 IJavascriptCallback
。這里的集合是 JavaScript 集合的快照表示,而 IJavascriptCallback
是一種 JavaScript 函數表示,可用于從 .NET 端執行它。
在 DotNetBrowser 中,有 IFrame.ExecuteJavaScript()
用于此目的。此方法的通用版本可用于顯式指定預期的返回類型:
DotNetBrowserJavaScript.cs
string title=await browser.MainFrame.ExecuteJavaScript<string>("document.title");
IJsObject window=await browser.MainFrame.ExecuteJavaScript<IJsObject>("window");
IElement body=await browser.MainFrame.ExecuteJavaScript<IElement>("document.body");
這里的主要區別是可以將 JavaScript 對象表示為IJsObject
。使用此接口,您可以訪問和修改 JavaScript 對象的屬性并調用其方法。在 .NET 端對 IJsObject
所做的所有更改都將立即反映在 JavaScript 端。此外, ExecuteJavaScript
調用可以返回一個 IElement
,這是一個 DOM 元素的表示,您可以使用它來訪問和修改 DOM 屬性或訂閱 DOM 事件。
CefSharp 和 DotNetBrowser 都可以使網頁上的 JavaScript 可以訪問 .NET 對象,但是,CefSharp 存在一些特定的限制。
CefSharp JavaScript 綁定可用于 JavaScript 和 .NET 之間的通信。但是,CefSharp 不允許[17]將Form, Window 或任何Control注入 JavaScript。另外,CefSharp 只支持調用注入對象的方法。如果需要設置屬性,則必須修改類并創建 Get/Set 方法。
在 DotNetBrowser 中,您可以將任何對象注入 JavaScript,包括 Form, Window 和 Control 對象。執行注入后,您可以訪問注入的 .NET 對象的公共字段、屬性和方法。此外,DotNetBrowser 支持從 JavaScript 訪問索引屬性(使用字符串或數字索引器)。如果您需要從 JavaScript 訪問 .NET 集合,這會很有幫助。
兩種解決方案都支持在瀏覽器不可見時進行截屏。但是,API 有明顯不同。以下是代碼片段:
CefSharpScreenshot.cs
// Take a screenshot
var bitmapAsByteArray=await browser.CaptureScreenshotAsync();
// Save the screenshot as PNG
var screenshotPath=Path.GetFullPath("screenshot.png");
File.WriteAllBytes(screenshotPath, bitmapAsByteArray);
DotNetBrowserScreenshot.cs
// Take a screenshot
DotNetBrowser.Ui.Bitmap image=browser.TakeImage();
// Convert the screenshot to System.Drawing.Bitmap and save it as PNG
System.Drawing.Bitmap bitmap=image.ToBitmap();
bitmap.Save("screenshot.png", ImageFormat.Png);
主要的 DotNetBrowser DLL 不使用 System.Drawing 中的類型,因為它的限制[18],因此,它提供了自己的類型。然后可以通過 DotNetBrowser.Wpf
或 DotNetBrowser.WinForms
中提供的擴展方法將此類型轉換為常規 System.Drawing.Bitmap。
CefSharp 需要 Microsoft Visual C++ 運行時存在于環境中[19]。Visual C++ 2015 是最低版本,但所需的確切版本取決于 Chromium 版本。因此,需要在您希望運行基于 CefSharp 的應用程序的每臺機器上預安裝 Microsoft Visual C++ Redistributable Package,將其設置為安裝程序的依賴項,或將其 DLL 打包為應用程序的一部分,并確保 CefSharp 正確找到它們。
在 DotNetBrowser 中,所有必需的 Chromium 二進制文件和 DLL 都已打包到 DotNetBrowser DLL 中,并且可以在執行期間自動提取。您無需預先安裝 Microsoft Visual C++ Runtime 即可使用 DotNetBrowser。
CefSharp 是一個開源項目。如果您發現錯誤或缺少功能,您可以提出建議[20]。
DotNetBrowser是為使用.NET開發軟件的商業公司設計和創建的商業產品,對集成第三方解決方案的質量和支持有很高的要求。自 2015 年以來,TeamDev 開發并支持 DotNetBrowser。
我們所有已訂閱有效標準支持[21]的客戶都可免費使用所有DotNetBrowser 新版本并獲得技術支持。如果您發現錯誤或缺少功能,我們將應用修復程序,實施所需功能,并根據任務的復雜性在幾天或幾周內為您提供新版本的庫。
幾乎每個月都會發布一個新版本的 DotNetBrowser。我們會在 Chromium 正式發布后的 3-4 周內將 Chromium 升級到最新的穩定版本(帶有最新的安全補丁和修復的漏洞)。
因為開源和免費,CefSharp 被廣泛使用。它很容易為基本案例進行配置,并且擁有廣泛的文檔和活躍的開源開發者社區。
但是,它具有來自其設計和架構的限制。缺少沙盒支持使其安全性降低,并且進程內方法會影響穩定性并增加應用程序的內存使用量。
它也不能用于暗示在單獨的 AppDomain(如 VSTO)中運行代碼的環境。對于其他一些情況,例如播放使用專有編解碼器編碼的內容,您必須自己構建、更新和維護 CEF。這需要大量額外的工作和基礎設施。
與加載的網頁的復雜交互會通過 JavaScript 注入執行,這使得生成的代碼更難調試和支持。
DotNetBrowser 簡化了所有這些案例的開發過程——由于它的進程外架構,它可以用于創建更穩定和安全的解決方案。使用 DotNetBrowser,您可以與需要在單獨的 AppDomain 中運行代碼的應用程序集成,并在網頁上執行復雜的操作,而無需進行大量的 JavaScript 注入。
除此之外,如果您覺得缺少哪些功能、發現問題或有其他任何疑問,您可以隨時與我們聯系并獲得幫助[22]。
DotNetBrowser: https://www.teamdev.com/dotnetbrowser?utm_campaign=dotnetbrowser-articles&utm_medium=article&utm_source=medium
[2]CefSharp: http://cefsharp.github.io/
[3]Chromium Embedded Framework: https://bitbucket.org/chromiumembedded/cef/src/master/
[4]引擎直接在您的 .NET 進程中初始化: https://github.com/cefsharp/CefSharp/wiki/General-Usage#initialize-and-shutdown
[5]CefSharp 不能在非默認 AppDomain 中使用: https://github.com/cefsharp/CefSharp/wiki/General-Usage#need-to-knowlimitations
[6]將 Chromium 嵌入到 Office 應用程序中: https://docs.microsoft.com/en-us/visualstudio/vsto/architecture-of-vsto-add-ins
[7]幾個選項: https://github.com/cefsharp/CefSharp/issues/1714
[8]可能需要長達一個月的時間: https://greenlightstudionet.wordpress.com/2019/10/09/stream-netflix-in-your-c-sharp-program/amp/
[9]Chromium沙箱: https://www.google.com/googlebooks/chrome/med_26.html
[10]不支持 Chromium 沙箱: https://github.com/cefsharp/CefSharp/wiki/General-Usage#need-to-knowlimitations
[11]初始化期間將其禁用: https://dotnetbrowser-support.teamdev.com/docs/guides/gs/chromium.html#windows?utm_campaign=dotnetbrowser-articles&utm_medium=article&utm_source=medium
[12]提供有限的設計器支持: https://github.com/cefsharp/CefSharp/wiki/General-Usage#need-to-knowlimitations
[13]離屏渲染模式: https://github.com/cefsharp/CefSharp/wiki/General-Usage#offscreen-rendering-osr
[14]IME: https://github.com/cefsharp/CefSharp/issues/1262
[15]已知的限制: https://dotnetbrowser-support.teamdev.com/docs/guides/gs/browser-view.html#mouse-keyboard-touch-drag-and-drop?utm_campaign=dotnetbrowser-articles&utm_medium=article&utm_source=c-sharpcorner
[16]默認 DPI 感知: https://github.com/cefsharp/CefSharp/wiki/General-Usage#high-dpi-additional-info
[17]不允許: https://github.com/cefsharp/CefSharp/wiki/General-Usage#binding-an-async-object-in-javascript
[18]它的限制: https://docs.microsoft.com/en-us/dotnet/api/system.drawing?view=netstandard-2.0#remarks
[19]Microsoft Visual C++ 運行時存在于環境中: https://github.com/cefsharp/CefSharp/wiki/Output-files-description-table-%28Redistribution%29#Requirements
[20]提出建議: https://github.com/cefsharp/CefSharp
[21]標準支持: https://dotnetbrowser-support.teamdev.com/getting-help/#standard-support?utm_campaign=dotnetbrowser-articles&utm_medium=article&utm_source=medium
[22]聯系并獲得幫助: https://dotnetbrowser-support.teamdev.com/getting-help?utm_campaign=dotnetbrowser-articles&utm_medium=article&utm_source=medium
*請認真填寫需求信息,我們會在24小時內與您取得聯系。