HtmlAgilityPack : xpath 및 regex

c# html-agility-pack regex

문제

현재 xpath 쿼리를 통해 특정 콘텐츠를 검색하기 위해 HtmlAgilityPack을 사용하고 있습니다. 이 같은:

var col = doc.DocumentNode.SelectNodes("//*[text()[contains(., 'foo'] or @*....

이제 정규 표현식을 사용하여 모든 HTML 소스 코드 (= 텍스트, 태그 및 속성)에서 특정 내용을 검색하려고합니다. HtmlAgilityPack을 사용하면 어떻게 될 수 있습니까? HtmlAgilityPack은 xpath + regex를 처리 할 수 ​​있습니까? 아니면 정규식과 HtmlAgilityPack을 사용하여 검색하는 가장 좋은 방법은 무엇입니까?

수락 된 답변

Html Agility Pack은 XPATH 지원을 위해 기본 .NET XPATH 구현을 사용합니다. 다행히 .NET에서 XPATH는 완전히 확장 가능합니다 (BTW : Microsoft가이 뛰어난 기술에 더 이상 투자하지 않는 것은 부끄러운 일입니다 ...).

자, 제가이 html을 가지고 있다고 가정 해 봅시다 :

<div>hello</div>
<div>hallo</div>

다음은 노드를 'h.llo'정규식과 비교하기 때문에 두 노드를 모두 선택하는 샘플 코드입니다.

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
}

그것은 내가 "regex-is-match"라는 새로운 XPATH 함수를 정의한 특별한 Xslt / XPath 컨텍스트를 사용하기 때문에 가능합니다. 다음은 SelectNodes 유틸리티 코드입니다.

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;
            }
        }
    }
}

지원 코드는 다음과 같습니다.

    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라는 클래스에서 구현됩니다. 그것은 매우 복잡하지 않습니다. xpath "물건"을 매우 유용한 문자열로 강요하려고하는 유틸리티 함수 ConvertToString이 있습니다.

물론,이 기술로, 당신은 아주 작은 코드로 필요한 XPATH 함수를 정의 할 수 있습니다 (저는 대문자 / 소문자 변환을하기 위해 항상 이것을 사용합니다 ...).


인기 답변

직접 인용 ,

HTML은 Chomsky Type 2 문법 (문맥 자유 문법) 이고 RegEx는 Chomsky Type 3 문법 (정규 문법) 입니다. Type 2 문법은 Type 3 문법 ( Chomsky 계층 구조 참조)보다 근본적으로 복잡 하므로이 작업을 수행 할 수는 없습니다. 그러나 많은 사람들이 시도 할 것이며, 어떤 사람들은 성공을 주장 할 것이고 다른 사람들은 잘못을 발견하고 당신을 완전히 망칠 것입니다.

HTML 문서의 일부와 함께 정규식을 사용하는 것이 좋습니다. HtmlAgilityPack 을 사용하여 HTML 문서의 태그와 구조에 대한 정규 표현식을 사용하려고 시도하는 것은 잘못되어 궁극적으로 문제에 보편적 인 해결책을 제공 할 수 없습니다.



아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.
아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.