首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >HtmlAgilityPack: xpath和regex

HtmlAgilityPack: xpath和regex
EN

Stack Overflow用户
提问于 2014-11-04 20:37:10
回答 2查看 3.4K关注 0票数 2

我目前正在使用HtmlAgilityPack通过xpath查询搜索某些内容。就像这样:

代码语言:javascript
复制
var col = doc.DocumentNode.SelectNodes("//*[text()[contains(., 'foo'] or @*....

现在,我想使用正则表达式在所有html源代码(= text、标记和属性)中搜索特定的内容。如何用HtmlAgilityPack来实现这一点?HtmlAgilityPack能否处理xpath+regex,或者使用regex和HtmlAgilityPack进行搜索的最佳方法是什么?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-11-21 11:23:20

Html使用底层的.NET XPATH实现作为其XPATH支持。幸运的是,.NET中的XPATH是完全可扩展的(BTW:遗憾的是微软没有在这一卓越的技术上投入更多的资金……)。

那么,让我们假设我有这个html:

代码语言:javascript
复制
<div>hello</div>
<div>hallo</div>

下面是一个选择这两个节点的示例代码,因为它将节点与'h.llo‘正则表达式进行比较:

代码语言:javascript
复制
HtmlNodeNavigator nav = new HtmlNodeNavigator("mypage.htm");
foreach (var node in SelectNodes(nav, "//div[regex-is-match(text(), 'h.llo')]"))
{
    Console.WriteLine(node.OuterHtml); // should dump both div elements
}

这是因为我使用了一个特殊的Xslt/XPath上下文,其中我定义了一个名为"regex-is-match“的新XPATH函数。下面是SelectNodes实用程序代码:

代码语言:javascript
复制
public static IEnumerable<HtmlNode> SelectNodes(HtmlNodeNavigator navigator, string xpath)
{
    if (navigator == null)
        throw new ArgumentNullException("navigator");

    XPathExpression expr = navigator.Compile(xpath);
    expr.SetContext(new HtmlXsltContext());

    object eval = navigator.Evaluate(expr);
    XPathNodeIterator it = eval as XPathNodeIterator;
    if (it != null)
    {
        while (it.MoveNext())
        {
            HtmlNodeNavigator n = it.Current as HtmlNodeNavigator;
            if (n != null && n.CurrentNode != null)
            {
                yield return n.CurrentNode;
            }
        }
    }
}

以下是支持代码:

代码语言:javascript
复制
    public class HtmlXsltContext : XsltContext
    {
        public HtmlXsltContext()
            : base(new NameTable())
        {
        }

        public override int CompareDocument(string baseUri, string nextbaseUri)
        {
            throw new NotImplementedException();
        }

        public override bool PreserveWhitespace(XPathNavigator node)
        {
            throw new NotImplementedException();
        }

        protected virtual IXsltContextFunction CreateHtmlXsltFunction(string prefix, string name, XPathResultType[] ArgTypes)
        {
            return HtmlXsltFunction.GetBuiltIn(this, prefix, name, ArgTypes);
        }

        public override IXsltContextFunction ResolveFunction(string prefix, string name, XPathResultType[] ArgTypes)
        {
            return CreateHtmlXsltFunction(prefix, name, ArgTypes);
        }

        public override IXsltContextVariable ResolveVariable(string prefix, string name)
        {
            throw new NotImplementedException();
        }

        public override bool Whitespace
        {
            get { return true; }
        }
    }

    public abstract class HtmlXsltFunction : IXsltContextFunction
    {
        protected HtmlXsltFunction(HtmlXsltContext context, string prefix, string name, XPathResultType[] argTypes)
        {
            Context = context;
            Prefix = prefix;
            Name = name;
            ArgTypes = argTypes;
        }

        public HtmlXsltContext Context { get; private set; }
        public string Prefix { get; private set; }
        public string Name { get; private set; }
        public XPathResultType[] ArgTypes { get; private set; }

        public virtual int Maxargs
        {
            get { return Minargs; }
        }

        public virtual int Minargs
        {
            get { return 1; }
        }

        public virtual XPathResultType ReturnType
        {
            get { return XPathResultType.String; }
        }

        public abstract object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext);

        public static IXsltContextFunction GetBuiltIn(HtmlXsltContext context, string prefix, string name, XPathResultType[] argTypes)
        {
            if (name == "regex-is-match")
                return new RegexIsMatch(context, name);

            // TODO: create other functions here
            return null;
        }

        public static string ConvertToString(object argument, bool outer, string separator)
        {
            if (argument == null)
                return null;

            string s = argument as string;
            if (s != null)
                return s;

            XPathNodeIterator it = argument as XPathNodeIterator;
            if (it != null)
            {
                if (!it.MoveNext())
                    return null;

                StringBuilder sb = new StringBuilder();
                do
                {
                    HtmlNodeNavigator n = it.Current as HtmlNodeNavigator;
                    if (n != null && n.CurrentNode != null)
                    {
                        if (sb.Length > 0 && separator != null)
                        {
                            sb.Append(separator);
                        }

                        sb.Append(outer ? n.CurrentNode.OuterHtml : n.CurrentNode.InnerHtml);
                    }
                }
                while (it.MoveNext());
                return sb.ToString();
            }

            IEnumerable enumerable = argument as IEnumerable;
            if (enumerable != null)
            {
                StringBuilder sb = null;
                foreach (object arg in enumerable)
                {
                    if (sb == null)
                    {
                        sb = new StringBuilder();
                    }

                    if (sb.Length > 0 && separator != null)
                    {
                        sb.Append(separator);
                    }

                    string s2 = ConvertToString(arg, outer, separator);
                    if (s2 != null)
                    {
                        sb.Append(s2);
                    }
                }
                return sb != null ? sb.ToString() : null;
            }

            return string.Format("{0}", argument);
        }

        public class RegexIsMatch : HtmlXsltFunction
        {
            public RegexIsMatch(HtmlXsltContext context, string name)
                : base(context, null, name, null)
            {
            }

            public override XPathResultType ReturnType { get { return XPathResultType.Boolean; } }
            public override int Minargs { get { return 2; } }

            public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext)
            {
                if (args.Length < 2)
                    return false;

                return Regex.IsMatch(ConvertToString(args[0], false, null), ConvertToString(args[1], false, null));
            }
        }
    }

regex函数最后在一个名为RegexIsMatch的类中实现。这不是超级复杂。注意,有一个实用函数ConvertToString试图将任何xpath“东西”强制放入一个非常有用的字符串中。

当然,使用这种技术,您可以用非常少的代码定义所需的任何XPATH函数(我一直使用它来进行大小写转换.)。

票数 7
EN

Stack Overflow用户

发布于 2014-11-21 11:50:55

直接引文

我认为这里的缺陷是HTML是Chomsky 2型语法(上下文无关文法),RegEx是Chomsky 3型语法(常规语法)。由于Type 2语法从根本上比Type 3语法复杂(请参阅乔姆斯基等级),所以您不可能做到这一点。但是很多人会尝试,有些人会声称成功,而另一些人会发现错误,把你完全搞砸了。

在HTML文档的某些部分中使用正则表达式可能是有意义的。试图使用HtmlAgilityPack在HTML文档的标记和结构上运行正则表达式是不正常的,最终不能为您的问题提供一个通用的解决方案。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26744559

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档