.NET WebBrowser - FireBug 样式检查 HTML 元素
Posted
技术标签:
【中文标题】.NET WebBrowser - FireBug 样式检查 HTML 元素【英文标题】:.NET WebBrowser - FireBug Style Inspect HTML Element 【发布时间】:2012-03-19 03:59:05 【问题描述】:是否可以使用 .NET 3.5/4.0 在 WinForm Web 浏览器中检查 html 元素?
可能使用 IHighlightRenderingServices 接口或 Html Agility Pack?
我希望应用程序像 FireBug 一样运行:
只需将鼠标悬停在要检查的元素上并单击即可。在 1.7 之前的 Firebug 版本中,这会自动切换到 HTML 面板并在节点视图中选择适当的元素。
编辑:
哇,我刚遇到http://www.selectorgadget.com/,这正是我想要做的。它是用 javascript 编写的,在查看了过去 2 小时的源代码后,我仍然不知道如何将它合并到我的程序中......
据我所知,它使用 DOM 元素的标记化和递归分析来找出 CSS 选择器路径:http://www.selectorgadget.com/stable/lib/dom.js。
编辑:好的!我已将 selectorgadget 加载到我的应用程序中。它允许您选择与 Firebug 完全相同的 HTML 元素!甚至创建 Xpath 查询。
但是,我正在使用 AxWebBrowser,但我不知道如何让它与 HtmlAgilityPack 一起使用...
private void xpathQuery_Click(object sender, EventArgs e)
// Load Browser
HtmlWindow window = axWebBrowser1.Document.Window; // <---- 'object' does not contain a definition for 'Window'
string str = window.Document.Body.OuterHtml;
// Load HTML
HtmlAgilityPack.HtmlDocument HtmlDoc = new HtmlAgilityPack.HtmlDocument();
HtmlDoc.LoadHtml(str);
//Process Xpath Query
HtmlAgilityPack.HtmlNodeCollection Nodes = HtmlDoc.DocumentNode.SelectNodes(xpathText.Text);
//Print in Text box
foreach (HtmlAgilityPack.HtmlNode Node in Nodes)
richTextBox1.Text += Node.OuterHtml + "\r\n";
编辑: 我无法让 AxWebBrowser 与 HtmlAgilityPack 一起使用,所以我只是使用 WebClient 类来加载 URL,然后使用 HtmlAgilityPack 对其进行解析。
我即将完成 Web Scraper。它的功能类似于 Visual Web Ripper 以及其他价格超过 1,000 美元的产品。
【问题讨论】:
我可以有你的代码示例吗? 我也需要一个示例代码。谢谢! 【参考方案1】:其实我以前也做过。您必须将文档转换为IExpando
,然后您可以对其进行反射调用以获取成员。我实际上创建了一个DynamicNode
类,它允许您使用动态关键字与文档进行交互。
不过,您可能想使用 mshtml COM 对象:Reusing MSHTML
-
在 COM 引用列表中添加对 mshtml 的引用
创建
var document = new mshtml.HTMLDocument();
的实例
投射到 IExpando:var window = (IExpando)document.parentWindow;
创建动态对象包装器(见下文)
使用动态关键字与文档进行交互。
例如这里是我的动态节点:
class DynamicNode : DynamicObject, IExpando, IEnumerable
private IExpando value;
public DynamicNode(IExpando value)
this.value = value;
public override bool TryUnaryOperation(UnaryOperationBinder binder, out object result)
switch (binder.Operation)
case System.Linq.Expressions.ExpressionType.Convert:
case System.Linq.Expressions.ExpressionType.ConvertChecked:
result = this.value;
return true;
return base.TryUnaryOperation(binder, out result);
public override IEnumerable<string> GetDynamicMemberNames()
return this.value
.GetMembers(BindingFlags.Instance | BindingFlags.Public)
.Select(m => m.Name)
.Distinct()
.ToArray();
public override bool TryConvert(ConvertBinder binder, out object result)
result = this.value;
return true;
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
if (indexes.Length == 1)
var memberName = indexes[0].ToString();
result = ReflectionHelpers.GetValue(this.value, memberName);
result = DynamicNode.Wrap(result);
return true;
return base.TryGetIndex(binder, indexes, out result);
public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
if (indexes.Length == 1)
var memberName = indexes[0].ToString();
value = DynamicNode.Unwrap(value);
ReflectionHelpers.SetValue(this.value, memberName, value);
return true;
return base.TrySetIndex(binder, indexes, value);
public override bool TryGetMember(GetMemberBinder binder, out object result)
if (base.TryGetMember(binder, out result))
return true;
result = ReflectionHelpers.GetValue(this.value, binder.Name);
result = DynamicNode.Wrap(result);
return true;
public override bool TrySetMember(SetMemberBinder binder, object value)
ReflectionHelpers.SetValue(this.value, binder.Name, value);
return true;
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
if (binder.Name == "New")
var constructorArgs = new object[args.Length - 1];
Array.ConstrainedCopy(args, 1, constructorArgs, 0, constructorArgs.Length);
result = ReflectionHelpers.New(this.value, (string)args[0], constructorArgs);
else
result = ReflectionHelpers.Invoke(this.value, binder.Name, args);
result = DynamicNode.Wrap(result);
return true;
public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
IExpando self = this.value;
object[] constructorArgs = new object[0];
if (args.Length > 0)
self = (IExpando)DynamicNode.Unwrap(args[0]);
constructorArgs = new object[args.Length - 1];
Array.ConstrainedCopy(args, 1, constructorArgs, 0, constructorArgs.Length);
result = ReflectionHelpers.Call(this.value, self, constructorArgs);
result = DynamicNode.Wrap(result);
return true;
private static object Wrap(object value)
if (value != null && Marshal.IsComObject(value))
value = new DynamicNode((IExpando)value);
return value;
public static object Unwrap(object value)
DynamicNode node = value as DynamicNode;
if (node != null)
return node.value;
return value;
public IEnumerator GetEnumerator()
var members = this.value.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var indexProperties = new List<Tuple<int, PropertyInfo>>();
var isArray = true;
foreach (var member in members)
int value = 0;
if (!int.TryParse(member.Name, out value))
isArray = false;
break;
var propertyMember = member as PropertyInfo;
if (propertyMember != null)
indexProperties.Add(Tuple.Create(value, propertyMember));
if (isArray)
indexProperties.Sort((left, right) => left.Item1.CompareTo(right.Item1));
foreach (var prop in indexProperties)
yield return prop.Item2.GetValue(this.value, null);
else
foreach (var member in members)
yield return member.Name;
#region IExpando
FieldInfo IExpando.AddField(string name)
return this.value.AddField(name);
MethodInfo IExpando.AddMethod(string name, Delegate method)
return this.value.AddMethod(name, method);
PropertyInfo IExpando.AddProperty(string name)
return this.value.AddProperty(name);
void IExpando.RemoveMember(MemberInfo m)
this.value.RemoveMember(m);
FieldInfo IReflect.GetField(string name, BindingFlags bindingAttr)
return this.value.GetField(name, bindingAttr);
FieldInfo[] IReflect.GetFields(BindingFlags bindingAttr)
return this.value.GetFields(bindingAttr);
MemberInfo[] IReflect.GetMember(string name, BindingFlags bindingAttr)
return this.value.GetMember(name, bindingAttr);
MemberInfo[] IReflect.GetMembers(BindingFlags bindingAttr)
return this.value.GetMembers(bindingAttr);
MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr)
return this.value.GetMethod(name, bindingAttr);
MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers)
return this.value.GetMethod(name, bindingAttr, binder, types, modifiers);
MethodInfo[] IReflect.GetMethods(BindingFlags bindingAttr)
return this.value.GetMethods(bindingAttr);
PropertyInfo[] IReflect.GetProperties(BindingFlags bindingAttr)
return this.value.GetProperties(bindingAttr);
PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers)
return this.value.GetProperty(name, bindingAttr, binder, returnType, types, modifiers);
PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr)
return this.value.GetProperty(name, bindingAttr);
object IReflect.InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, System.Globalization.CultureInfo culture, string[] namedParameters)
return this.value.InvokeMember(name, invokeAttr, binder, target, args, modifiers, culture, namedParameters);
Type IReflect.UnderlyingSystemType
get return this.value.UnderlyingSystemType;
#endregion
[ComVisible(true)]
public class ScriptObject : IReflect, IExpando
private readonly Type type;
private dynamic _constructor;
private dynamic _prototype;
public ScriptObject()
type = this.GetType();
[DispId(0)]
protected virtual object Invoke(object[] args)
return "ClrObject";
public dynamic constructor
get return _constructor;
set this._constructor = value;
public dynamic prototype
get return _prototype;
set this._prototype = value;
public string toString()
return "ClrObject";
#region IReflect Members
MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers)
return type.GetMethod(name, bindingAttr, binder, types, modifiers);
MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr)
return type.GetMethod(name, bindingAttr);
protected virtual MethodInfo[] GetMethods(BindingFlags bindingAttr)
return type.GetMethods(bindingAttr);
MethodInfo[] IReflect.GetMethods(BindingFlags bindingAttr)
return GetMethods(bindingAttr);
FieldInfo IReflect.GetField(string name, BindingFlags bindingAttr)
return type.GetField(name, bindingAttr);
FieldInfo[] IReflect.GetFields(BindingFlags bindingAttr)
return new FieldInfo[0]; // type.GetFields(bindingAttr);
PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr)
return type.GetProperty(name, bindingAttr);
PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers)
return type.GetProperty(name, bindingAttr, binder, returnType, types, modifiers);
protected virtual PropertyInfo[] GetProperties(BindingFlags bindingAttr)
return type.GetProperties(bindingAttr);
PropertyInfo[] IReflect.GetProperties(BindingFlags bindingAttr)
return GetProperties(bindingAttr);
MemberInfo[] IReflect.GetMember(string name, BindingFlags bindingAttr)
return type.GetMember(name, bindingAttr);
MemberInfo[] IReflect.GetMembers(BindingFlags bindingAttr)
return type.GetMembers(bindingAttr);
protected virtual object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters)
if (name == "[DISPID=0]")
return this.Invoke(args);
return type.InvokeMember(name, invokeAttr, binder, target, args, modifiers, culture, namedParameters);
object IReflect.InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters)
return this.InvokeMember(name, invokeAttr, binder, target, args, modifiers, culture, namedParameters);
Type IReflect.UnderlyingSystemType
get return type.UnderlyingSystemType;
#endregion
#region IExpando Members
public FieldInfo AddField(string name)
throw new NotImplementedException();
public MethodInfo AddMethod(string name, Delegate method)
throw new NotImplementedException();
public PropertyInfo AddProperty(string name)
throw new NotImplementedException();
public void RemoveMember(MemberInfo m)
throw new NotImplementedException();
#endregion
public static class ReflectionHelpers
private const BindingFlags DefaultFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
public static object New(this IExpando scope, string functionName, params object[] args)
var constructor = (IExpando)scope.GetValue(functionName);
var proto = constructor.GetValue("prototype");
var obj = (IExpando)scope.GetValue("Object");
var instance = (IExpando)obj.Invoke("create", new object[] proto );
Call(constructor, instance, args);
return instance;
public static object Call(this IExpando function, IExpando scope, params object[] args)
object[] callArgs = new object[args.Length + 1];
callArgs[0] = scope;
Array.Copy(args, 0, callArgs, 1, args.Length);
return Invoke(function, "call", callArgs);
public static void SetValue(this IExpando instance, string propertyName, object value)
if (instance == null)
throw new ArgumentNullException("instance");
if (string.IsNullOrEmpty(propertyName))
throw new ArgumentException("Must specify a value.", "propertyName");
Invoke(instance, propertyName, InvokeFlags.DISPATCH_PROPERTYPUT, new object[] value );
public static object GetValue(this IExpando instance, string propertyName)
return Invoke(instance, propertyName, InvokeFlags.DISPATCH_PROPERTYGET, new object[0]);
public static object Invoke(this IExpando instance, string functionName, object[] args)
if (instance == null)
throw new ArgumentNullException("instance");
if (string.IsNullOrEmpty(functionName))
throw new ArgumentException("Must specify a value.", "functionName");
return Invoke(instance, functionName, InvokeFlags.DISPATCH_METHOD, args);
private static object Invoke(IExpando instance, string functionName, InvokeFlags flags, object[] args)
try
args = args.Select(arg => DynamicNode.Unwrap(arg)).ToArray();
switch (flags)
case InvokeFlags.DISPATCH_METHOD:
var method = instance.GetMethod(functionName, DefaultFlags);
return method.Invoke(instance, args);
case InvokeFlags.DISPATCH_PROPERTYGET:
var getProp = instance.GetProperty(functionName, DefaultFlags);
return getProp.GetValue(instance, null);
case InvokeFlags.DISPATCH_PROPERTYPUT:
case InvokeFlags.DISPATCH_PROPERTYPUTREF:
var setProp = instance.GetProperty(functionName, DefaultFlags);
if (setProp == null)
setProp = instance.AddProperty(functionName);
setProp.SetValue(instance, args[0], null);
return null;
default:
throw new NotSupportedException();
catch (COMException comex)
switch ((uint)comex.ErrorCode)
// Unexpected script error. This will be handled by the IProcess.UnhandledException event
case 0x80020101:
return null;
default:
throw;
private enum InvokeFlags
DISPATCH_METHOD = 1,
DISPATCH_PROPERTYGET = 2,
DISPATCH_PROPERTYPUT = 4,
DISPATCH_PROPERTYPUTREF = 8,
您实际上可以通过这种方式将 .net 对象粘贴到文档中,或者从 .net 中取出对象并与它们进行交互。您还可以将 js 评估为字符串并将其调用到 .net 函数中。下面是一些代码sn-ps的用法:
获取和设置 js 对象的成员:
this.host.Window.eval(@" Foo = ; ");
var foo = this.host.Window.Foo;
foo.B = 7.11;
Assert.Equal(7.11, foo.B);
从 C# 调用 js 函数:
this.host.eval("function add(x, y) return x + y; ");
var z = (int)this.host.Window.add(7, 11);
Assert.Equal(7 + 11, z);
将 .net 对象插入文档并从 js 调用其成员:
this.host.Window.Custom2 = new Custom2();
this.host.Window.eval(@"
function test()
return Custom2.Test().Value;
");
bool success = this.host.Window.test();
Assert.True(success);
您只能将继承自 ScriptObject 的对象粘贴到文档中(在上面的代码块中定义)。好吧,我认为您可以将任何东西放在那里,但是如果它们不实现 IReflect,您会得到奇怪的行为。
【讨论】:
【参考方案2】:您可以使用 Firebug lite,它的功能与原来的 Firebug 非常相似。
【讨论】:
【参考方案3】:我认为你不能用 HtmlAgillity 包做到这一点。我这么认为的原因是浏览器内存中发生了很多事情(例如,由于 javascript)。因此,您会错过在某些 JavaScript 的结果中呈现的所有元素。 您也不会看到通过 JS 动态应用的样式。
您最好的方法可能是使用 mshtml(在 Google 上搜索 Microsoft HTML、mshtml)并在 Document_Loaded 事件中继续迭代文档中的所有元素,将鼠标悬停在 javascript 上以突出显示它们。然后,您可以通过在 DOM 中找到当前突出显示的元素(它将具有某种“选定的”css 类或您应用的某些特定边框属性)并迭代其属性以检查它。
【讨论】:
【参考方案4】:firebug lite 怎么样?
http://getfirebug.com/firebuglite
这是一个小书签,可以将大多数萤火虫功能注入任何东西。
【讨论】:
以上是关于.NET WebBrowser - FireBug 样式检查 HTML 元素的主要内容,如果未能解决你的问题,请参考以下文章
VB.Net / VB 如何隐藏 WebBrowser 控件 中之滚动