c# - visual - webbrowser events
Usando XPath y WebBrowser Control para seleccionar mĂșltiples nodos (1)
He encontrado una solución, aquí está el código:
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Windows.Forms;
namespace myTest.WinFormsApp
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void MainForm_Load(object sender, EventArgs e)
{
webBrowser1.DocumentText = @"
<html>
<body>
<img alt=""0764547763 Product Details""
src=""http://ecx.images-amazon.com/images/I/51AK1MRIi7L._AA160_.jpg"">
<hr/>
<h2>Product Details</h2>
<ul>
<li><b>Paperback:</b> 648 pages</li>
<li><b>Publisher:</b> Wiley; Unlimited Edition edition (October 15, 2001)</li>
<li><b>Language:</b> English</li>
<li><b>ISBN-10:</b> 0764547763</li>
</html>
";
}
private void cmdTest_Click(object sender, EventArgs e)
{
var processor = new WebBrowserControlXPathQueriesProcessor(webBrowser1);
// change attributes of the first element of the list
{
var li = processor.GetHtmlElement("//li");
li.innerHTML = string.Format("<span style=''text-transform: uppercase;font-family:verdana;color:green;''>{0}</span>", li.innerText);
}
// change attributes of the second and subsequent elements of the list
var list = processor.GetHtmlElements("//ul//li");
int index = 1;
foreach (var li in list)
{
if (index++ == 1) continue;
li.innerHTML = string.Format("<span style=''text-transform: uppercase;font-family:verdana;color:blue;''>{0}</span>", li.innerText);
}
}
/// <summary>
/// Enables IE WebBrowser control to evaluate XPath queries
/// by injecting http://svn.coderepos.org/share/lang/javascript/javascript-xpath/trunk/release/javascript-xpath-latest-cmp.js
/// and to return XPath queries results to the calling C# code as strongly typed
/// mshtml.IHTMLElement and IEnumerable<mshtml.IHTMLElement>
/// </summary>
public class WebBrowserControlXPathQueriesProcessor
{
private System.Windows.Forms.WebBrowser _webBrowser;
public WebBrowserControlXPathQueriesProcessor(System.Windows.Forms.WebBrowser webBrowser)
{
_webBrowser = webBrowser;
injectScripts();
}
private void injectScripts()
{
// Thanks to: http://stackoverflow.com/questions/7998996/how-to-inject-javascript-in-webbrowser-control
HtmlElement head = _webBrowser.Document.GetElementsByTagName("head")[0];
HtmlElement scriptEl = _webBrowser.Document.CreateElement("script");
mshtml.IHTMLScriptElement element = (mshtml.IHTMLScriptElement)scriptEl.DomElement;
element.src = "http://svn.coderepos.org/share/lang/javascript/javascript-xpath/trunk/release/javascript-xpath-latest-cmp.js";
head.AppendChild(scriptEl);
string javaScriptText = @"
function GetElements (XPath) {
var xPathRes = document.evaluate ( XPath, document, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
var nextElement = xPathRes.iterateNext ();
var elements = new Object();
var elementIndex = 1;
while (nextElement) {
elements[elementIndex++] = nextElement;
nextElement = xPathRes.iterateNext ();
}
elements.length = elementIndex -1;
return elements;
};
";
scriptEl = _webBrowser.Document.CreateElement("script");
element = (mshtml.IHTMLScriptElement)scriptEl.DomElement;
element.text = javaScriptText;
head.AppendChild(scriptEl);
}
/// <summary>
/// Gets Html element''s mshtml.IHTMLElement object instance using XPath query
/// </summary>
public mshtml.IHTMLElement GetHtmlElement(string xPathQuery)
{
string code = string.Format("document.evaluate(''{0}'', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;", xPathQuery);
return _webBrowser.Document.InvokeScript("eval", new object[] { code }) as mshtml.IHTMLElement;
}
/// <summary>
/// Gets Html elements'' IEnumerable<mshtml.IHTMLElement> object instance using XPath query
/// </summary>
public IEnumerable<mshtml.IHTMLElement> GetHtmlElements(string xPathQuery)
{
// Thanks to: http://stackoverflow.com/questions/5278275/accessing-properties-of-javascript-objects-using-type-dynamic-in-c-sharp-4
var comObject = _webBrowser.Document.InvokeScript("eval", new object[] { string.Format("GetElements(''{0}'')", xPathQuery) });
Type type = comObject.GetType();
int length = (int)type.InvokeMember("length", BindingFlags.GetProperty, null, comObject, null);
for (int i = 1; i <= length; i++)
{
yield return type.InvokeMember(i.ToString(), BindingFlags.GetProperty, null, comObject, null) as mshtml.IHTMLElement;
}
}
}
}
}
Y aquí están los resultados de ejecución del código:
He puesto referencias de créditos a mi código en línea. Si encuentra que me he perdido algunos, indíqueme sus comentarios y los agregaré.
Si conoce una solución mejor: código más corto, código más efectivo, por favor comente y / o publique su respuesta.
En la aplicación de ejemplo C # WinForms, he usado el control WebBrowser y JavaScript-XPath para seleccionar un solo nodo y cambiar ese nodo .innerHtml por el siguiente código:
private void MainForm_Load(object sender, EventArgs e)
{
webBrowser1.DocumentText = @"
<html>
<head>
<script src=""http://svn.coderepos.org/share/lang/javascript/javascript-xpath/trunk/release/javascript-xpath-latest-cmp.js""></script>
</head>
<body>
<img alt=""0764547763 Product Details""
src=""http://ecx.images-amazon.com/images/I/51AK1MRIi7L._AA160_.jpg"">
<hr/>
<h2>Product Details</h2>
<ul>
<li><b>Paperback:</b> 648 pages</li>
<li><b>Publisher:</b> Wiley; Unlimited Edition edition (October 15, 2001)</li>
<li><b>Language:</b> English</li>
<li><b>ISBN-10:</b> 0764547763</li>
</ul>
</body>
</html>
";
}
private void cmdTest_Click(object sender, EventArgs e)
{
string xPath = "//li";
string code = string.Format("document.evaluate(''{0}'', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;", xPath);
var li = webBrowser1.Document.InvokeScript("eval", new object[] { code }) as mshtml.IHTMLElement;
li.innerHTML = string.Format("<span style=''text-transform: uppercase;font-family:verdana;color:green;''>{0}</span>", li.innerText);
}
El resultado de ejecutar este código es el siguiente:
Ahora me gustaría utilizar la misma técnica para seleccionar varios nodos <li>
nodo <ul>
y escribo:
xPath = "//ul//*";
code = string.Format("document.evaluate(''{0}'', document, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);", xPath);
var allLI = webBrowser1.Document.InvokeScript("eval", new object[] { code }) as mshtml.IHTMLElementCollection;
pero el valor devuelto de la variable allLI
es NULL
.
Si voy a escribir
xPath = "//ul//*";
code = string.Format("document.evaluate(''{0}'', document, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);", xPath);
var allLI = webBrowser1.Document.InvokeScript("eval", new object[] { code });
a continuación, la variable allLI
devuelta no es nula y su tipo de valor es el COM Object
pero no está claro para qué tipo más específico se puede convertir este COM Object
.
¿Hay alguna manera de seleccionar múltiples nodos según la técnica usada aquí?
[EDITADO]
xPath = "ul // *";
a
xPath = "// ul // *";
[Adición]
He agregado dos funciones javaScript a mi HTML de ejemplo:
<script type=""text/javascript"">
function GetElementsText (XPath) {
var xPathRes = document.evaluate ( XPath, document, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
var nextElement = xPathRes.iterateNext ();
var text = """";
while (nextElement) {
text += nextElement.innerText;
nextElement = xPathRes.iterateNext ();
}
return text;
};
function GetElements (XPath) {
var xPathRes = document.evaluate ( XPath, document, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
var nextElement = xPathRes.iterateNext ();
var elements = new Object();
var elementIndex = 1;
while (nextElement) {
elements[elementIndex++] = nextElement;
nextElement = xPathRes.iterateNext ();
}
return elements;
};
</script>
Ahora cuando estoy ejecutando la siguiente línea de código C # dentro de mi método cmd_TestClick
:
var text = webBrowser1.Document.InvokeScript("eval", new object[] { "GetElementsText(''//ul'')" });
Obtengo el texto de todos los elementos li
:
"Paperback: 648 pages /r/nPublisher: Wiley; Unlimited Edition edition (October 15, 2001) /r/nLanguage: English /r/nISBN-10: 0764547763 "
Y cuando estoy ejecutando la siguiente línea de código C # dentro de mi método cmd_TestClick
:
var elements = webBrowser1.Document.InvokeScript("eval", new object[] { "GetElements(''//ul'')" });
IEnumerable<mshtml.IHtmlElement>
COM Object
, que no puedo convertir en IEnumerable<mshtml.IHtmlElement>
.
¿Hay alguna manera de procesar dentro del código C # una colección javaScript de nodos HTML devueltos por
var elements = webBrowser1.Document.InvokeScript("eval", new object[] { "GetElements(''//ul'')" });
?