C# 如何在使用 CefSharp 重定向或刷新后继续 运行 JavaScript 代码
C# How to continue running JavaScript code after a redirect or refresh by using CefSharp
我需要以编程方式在网站上执行 3 个操作
- 填写搜索输入
- 按下搜索按钮
(之后出现请求,页面returns我是搜索结果)
- 点击第一个link
如果我将操作 1 和 2 放在按钮 1 中:
private void Button1_Click(object sender, EventArgs e)
{
string jsScript1 = "document.getElementById('story').value=" + '\'' + textFind.Text + '\'' + ";"
+ "document.querySelector('body > div.wrapper > div.header > div.header44 > div.search_panel > span > form > button').click();";
var task1 = chrome.EvaluateScriptAsync(jsScript1);
task1.Wait();
}
按钮 2 中的操作 3:
private void Button2_Click(object sender, EventArgs e)
{
string jsScript3 = "document.getElementsByTagName('a')[1].click();";
var task3 = chrome.EvaluateScriptAsync(jsScript3);
task3.Wait();
}
一切正常。但我想在一个按钮内完成所有这些操作。
我找到了以下解决方案:
private async void ButtonFind_Click(object sender, EventArgs e)
{
//Action 1 & 2
string jsScript1 = "document.getElementById('story').value=" + '\'' + textFind.Text + '\'' + ";"
+ "document.querySelector('body > div.wrapper > div.header > div.header44 > div.search_panel > span > form > button').click();";
await chrome.EvaluateScriptAsync(jsScript1);
//Action3
Thread.Sleep(1000); //it is necessary to set exactly 1 seconds
string jsScript3 = "document.getElementsByTagName('a')[2].click();";
await chrome.EvaluateScriptAsync(jsScript3);
}
我必须使用 Thread.Sleep(1000);
,因为在 JavaScript 中重定向或单击搜索按钮会破坏浏览器中当前 运行 的所有脚本。
但我认为使用 Thread.Sleep 或 Task.Delay 不是一个好主意。
在不使用 Thread.Sleep/Task.Delay?
的情况下,我还能如何更改我的代码?
您可以使用如下方式等待页面加载
// create a static class for the extension method
public static Task<LoadUrlAsyncResponse> WaitForLoadAsync(this IWebBrowser browser)
{
var tcs = new TaskCompletionSource<LoadUrlAsyncResponse>(TaskCreationOptions.RunContinuationsAsynchronously);
EventHandler<LoadErrorEventArgs> loadErrorHandler = null;
EventHandler<LoadingStateChangedEventArgs> loadingStateChangeHandler = null;
loadErrorHandler = (sender, args) =>
{
//Actions that trigger a download will raise an aborted error.
//Generally speaking Aborted is safe to ignore
if (args.ErrorCode == CefErrorCode.Aborted)
{
return;
}
//If LoadError was called then we'll remove both our handlers
//as we won't need to capture LoadingStateChanged, we know there
//was an error
browser.LoadError -= loadErrorHandler;
browser.LoadingStateChanged -= loadingStateChangeHandler;
tcs.TrySetResult(new LoadUrlAsyncResponse(args.ErrorCode, -1));
};
loadingStateChangeHandler = (sender, args) =>
{
//Wait for while page to finish loading not just the first frame
if (!args.IsLoading)
{
browser.LoadError -= loadErrorHandler;
browser.LoadingStateChanged -= loadingStateChangeHandler;
var host = args.Browser.GetHost();
var navEntry = host?.GetVisibleNavigationEntry();
int statusCode = navEntry?.HttpStatusCode ?? -1;
//By default 0 is some sort of error, we map that to -1
//so that it's clearer that something failed.
if (statusCode == 0)
{
statusCode = -1;
}
tcs.TrySetResult(new LoadUrlAsyncResponse(statusCode == -1 ? CefErrorCode.Failed : CefErrorCode.None, statusCode));
}
};
browser.LoadingStateChanged += loadingStateChangeHandler;
browser.LoadError += loadErrorHandler;
return tcs.Task;
}
// usage example
await Task.WhenAll(chrome.WaitForLoadAsync(), chrome.EvaluateScriptAsync(script));
您绝不能以这种方式使用 Sleep。 Web 可能需要更多或更少的时间,并且您的代码会在一段时间后失败。您必须始终在异步模式下工作。如果你愿意,你可以设置一个超时以防出错,但在正常模式下,总是等到你想要的。
例如,单击按钮并设置计时器以等待您的 link 出现。然后,导航到第一个 link.
在其他情况下,当真正的导航发生并且您丢失了 JavaScript 上下文时,您可以使用绑定对象来连接您的 C# 应用程序和浏览器。为此,您可以从 JavaScript 查询您在做什么,并从 JavaScript 向您的 C# 代码提供反馈。这是一种持久化状态的方法。
一个典型的示例是当您填写一些表格并单击“继续”、“继续...”时在多个页面中导航。如果你想收集所有信息,你需要绑定对象。我将它与 json 序列化一起使用来管理更复杂的数据。
如果您要经常使用浏览器,我建议您在您导航的页面中插入您自己的脚本。在此表单中,您可以在加载的页面中使用您自己的 类。
我需要以编程方式在网站上执行 3 个操作
- 填写搜索输入
- 按下搜索按钮
(之后出现请求,页面returns我是搜索结果)
- 点击第一个link
如果我将操作 1 和 2 放在按钮 1 中:
private void Button1_Click(object sender, EventArgs e)
{
string jsScript1 = "document.getElementById('story').value=" + '\'' + textFind.Text + '\'' + ";"
+ "document.querySelector('body > div.wrapper > div.header > div.header44 > div.search_panel > span > form > button').click();";
var task1 = chrome.EvaluateScriptAsync(jsScript1);
task1.Wait();
}
按钮 2 中的操作 3:
private void Button2_Click(object sender, EventArgs e)
{
string jsScript3 = "document.getElementsByTagName('a')[1].click();";
var task3 = chrome.EvaluateScriptAsync(jsScript3);
task3.Wait();
}
一切正常。但我想在一个按钮内完成所有这些操作。
我找到了以下解决方案:
private async void ButtonFind_Click(object sender, EventArgs e)
{
//Action 1 & 2
string jsScript1 = "document.getElementById('story').value=" + '\'' + textFind.Text + '\'' + ";"
+ "document.querySelector('body > div.wrapper > div.header > div.header44 > div.search_panel > span > form > button').click();";
await chrome.EvaluateScriptAsync(jsScript1);
//Action3
Thread.Sleep(1000); //it is necessary to set exactly 1 seconds
string jsScript3 = "document.getElementsByTagName('a')[2].click();";
await chrome.EvaluateScriptAsync(jsScript3);
}
我必须使用 Thread.Sleep(1000);
,因为在 JavaScript 中重定向或单击搜索按钮会破坏浏览器中当前 运行 的所有脚本。
但我认为使用 Thread.Sleep 或 Task.Delay 不是一个好主意。
在不使用 Thread.Sleep/Task.Delay?
您可以使用如下方式等待页面加载
// create a static class for the extension method
public static Task<LoadUrlAsyncResponse> WaitForLoadAsync(this IWebBrowser browser)
{
var tcs = new TaskCompletionSource<LoadUrlAsyncResponse>(TaskCreationOptions.RunContinuationsAsynchronously);
EventHandler<LoadErrorEventArgs> loadErrorHandler = null;
EventHandler<LoadingStateChangedEventArgs> loadingStateChangeHandler = null;
loadErrorHandler = (sender, args) =>
{
//Actions that trigger a download will raise an aborted error.
//Generally speaking Aborted is safe to ignore
if (args.ErrorCode == CefErrorCode.Aborted)
{
return;
}
//If LoadError was called then we'll remove both our handlers
//as we won't need to capture LoadingStateChanged, we know there
//was an error
browser.LoadError -= loadErrorHandler;
browser.LoadingStateChanged -= loadingStateChangeHandler;
tcs.TrySetResult(new LoadUrlAsyncResponse(args.ErrorCode, -1));
};
loadingStateChangeHandler = (sender, args) =>
{
//Wait for while page to finish loading not just the first frame
if (!args.IsLoading)
{
browser.LoadError -= loadErrorHandler;
browser.LoadingStateChanged -= loadingStateChangeHandler;
var host = args.Browser.GetHost();
var navEntry = host?.GetVisibleNavigationEntry();
int statusCode = navEntry?.HttpStatusCode ?? -1;
//By default 0 is some sort of error, we map that to -1
//so that it's clearer that something failed.
if (statusCode == 0)
{
statusCode = -1;
}
tcs.TrySetResult(new LoadUrlAsyncResponse(statusCode == -1 ? CefErrorCode.Failed : CefErrorCode.None, statusCode));
}
};
browser.LoadingStateChanged += loadingStateChangeHandler;
browser.LoadError += loadErrorHandler;
return tcs.Task;
}
// usage example
await Task.WhenAll(chrome.WaitForLoadAsync(), chrome.EvaluateScriptAsync(script));
您绝不能以这种方式使用 Sleep。 Web 可能需要更多或更少的时间,并且您的代码会在一段时间后失败。您必须始终在异步模式下工作。如果你愿意,你可以设置一个超时以防出错,但在正常模式下,总是等到你想要的。
例如,单击按钮并设置计时器以等待您的 link 出现。然后,导航到第一个 link.
在其他情况下,当真正的导航发生并且您丢失了 JavaScript 上下文时,您可以使用绑定对象来连接您的 C# 应用程序和浏览器。为此,您可以从 JavaScript 查询您在做什么,并从 JavaScript 向您的 C# 代码提供反馈。这是一种持久化状态的方法。
一个典型的示例是当您填写一些表格并单击“继续”、“继续...”时在多个页面中导航。如果你想收集所有信息,你需要绑定对象。我将它与 json 序列化一起使用来管理更复杂的数据。
如果您要经常使用浏览器,我建议您在您导航的页面中插入您自己的脚本。在此表单中,您可以在加载的页面中使用您自己的 类。