使用xpath实现document.querySelector样式选择器进行html解析(二):扩展一下xpath以便支持正则

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介:

使用xpath实现document.querySelector样式选择器进行html解析(一):将html转成xml

使用xpath实现document.querySelector样式选择器进行html解析(二):扩展一下xpath以便支持正则

使用xpath实现document.querySelector样式选择器进行html解析(三):实现样式选择器

使用xpath实现document.querySelector样式选择器进行html解析(四):将选择结果封装进行输出

-----------------------------------------------------------------

继续我们的工作,在进行下一步之前,先考虑一下,为了支持css选择器,我们需要使用xpath完成哪些东西

标签选择器。。。。这个很简单嘛,//*[name()='tagName'],完全就是标签选择器嘛,根本都不需要再加工了

id选择器。。。。这个好像也很容易,//*[@id='ID'],貌似也挺容易

再看看类选择器。。。。好像有点问题,//*[contains(@class,'className')] 到是能把符合条件的节点选择出来,但是结果貌似比我们预期的要多了?他连 class="classNameA"、class="PickclassName"之类的也给匹配上了?!

Hmmmmmmmmmmm,好吧,在类选择器上,看来是必须扩展一下xpath的方法了,不管是扩展一个正则支持,还是扩展一个其他自定义函数支持,就看个人爱好了,文盲个人是倾向用正则来搞一下,毕竟除了以上三个基本选择器,后边还有属性选择器等着我们实现类似*=啦、^=啦、$=啦,嗯。。。。。为了再htmlParser中不使用正则,结果编写的实现代码中,正则还是不少啊。。。

好了,我们开始去实现一下xpath的扩展吧,这个东西网上搜一搜还是挺多了,基本上就是XsltContext、IXsltContextFunction来对xpath进行扩展


public class XpathContext: XsltContext
{
private XsltArgumentList _args;
public XpathContext()
{
}
public XpathContext(NameTable nt) : base(nt)
{
}
public XpathContext(NameTable nt,XsltArgumentList args) : base(nt)
{
_args = args;
}
public XsltArgumentList ArgList
{
get
{
return _args;
}
}
public override IXsltContextFunction ResolveFunction(string prefix, string name, XPathResultType[] ArgTypes)
{
XPathExtensionFunction fun = null;
switch (prefix)
{
case "regex":    // 这里是前缀名
switch (name)
{
case "ismatch":    // 这里是函数名,下边的委托中,第一个参数是委托调用的函数名?应该可以这么说吧。。。。
fun = new XPathExtensionFunction("RegexIsMatch", 1, 2, new XPathResultType[] { XPathResultType.NodeSet, XPathResultType.String }, XPathResultType.Boolean);
break;
}
break;
}
return fun;
}
public override IXsltContextVariable ResolveVariable(string prefix, string name)
{
XPathExtensionVariable result = new XPathExtensionVariable(name);
return result;
}
public override int CompareDocument(string baseUri, string nextbaseUri)
{
return 0;
}
public override bool PreserveWhitespace(XPathNavigator node)
{
return true;
}
public override bool Whitespace
{
get
{
return true;
}
}
}
public class XPathExtensionFunction : IXsltContextFunction
{
private XPathResultType[] _xprts;
private XPathResultType _xprt;
private string _fn;
private int _min;
private int _max;
public int Minargs
{
get
{
return _min;
}
}
public int Maxargs
{
get
{
return _max;
}
}
public XPathResultType[] ArgTypes
{
get
{
return _xprts;
}
}
public XPathResultType ReturnType
{
get
{
return _xprt;
}
}
public XPathExtensionFunction(string fn, int min, int max, XPathResultType[] argTypes, XPathResultType returnType)
{
_fn = fn;
_min = min;
_max = max;
_xprts = argTypes;
_xprt = returnType;
}
public object Invoke(XsltContext xls,object[] args,XPathNavigator doc)
{
switch (_fn)    // 根据函数名,进行具体实现。Hmmmmmmm,应该可以叫做函数名吧。^v^
{
case "RegexIsMatch": // 具体实现稍后再说
return false;
}
return null;
}
}
public class XPathExtensionVariable : IXsltContextVariable
{
private string _fn = string.Empty;
public XPathExtensionVariable(string fn)
{
_fn = fn;
}
public object Evaluate(XsltContext xsl)
{
XsltArgumentList vars = ((XpathContext)xsl).ArgList;
return vars.GetParam(_fn, null);
}
public bool IsLocal
{
get
{
return false;
}
}
public bool IsParam
{
get
{
return false;
}
}
public XPathResultType VariableType
{
get
{
return XPathResultType.Any;
}
}
}

呵呵,别看上边这些代码一大片,其实。。。。都是网上抄的,嗯,真的,文盲同学抄完了之后,都没弄明白各个方法之间传递的都是什么玩意,结果一不小心掉到坑里了,先不要关正则的实现,看看我们的类选择器应该怎么实现

前边已经说了,//*[contains(@class,'className')]不合适,那么用正则来进行选择就好了,//*[regex:ismatch(@class,'(?<!\w)className(?!\w)')],嗯,这个正则很标准嘛,肯定不会选择出多余的东西。。。。好吧,我说的早了,被打脸了

问题出在什么地方?仔细调试后发现在具体实现的地方,也就是Invoke方法里,我所设置的@class传递进来的是个什么玩意?怎么看都没有发现和class这个属性有关系。。。。

然后再想想,正则除了需要和属性计算之外,还可以和节点的正文计算,或者下一级指定节点的正文进行计算,嗯。。。xpath有这个功能,比如//div[.='标题']、//div[a=链接],好吧,我们先吧选择器调整调整//*[regex:ismatch('@class','(?<!\w)className(?!\w)')],嗯,这次Invoke传递进来的参数args的所有元素我都可以看懂了,进来了两个字符串,嘿嘿

具体实现正则其实就很简单了。。。


public object Invoke(XsltContext xls,object[] args,XPathNavigator doc)
{
XmlElement xe = doc.UnderlyingObject as XmlElement;
switch (_fn)
{
case "RegexIsMatch":
string att = args[0].ToString();
string reg = args[1].ToString();
// 按属性匹配
if (att.Substring(0, 1) == "@")
{
if (xe.Attributes.GetNamedItem(att.Substring(1)) == null)
{
return false;
}
else
{
// 考虑到css选择器是区分大小写的,所以这里的正则就不忽视大小写了
return Regex.IsMatch(xe.Attributes.GetNamedItem(att.Substring(1)).Value, reg);
}
}
// 实现其他正则需要实现的匹配
return false;
}
return null;
}

哦了,关于xpath的扩展我们也就写好了,使用这个扩展的方式也很简单,直接 xml.SelectNodes("//div",new XpathContext())即可,嗯,我是将这个扔到一个静态类里,这样只需要实例化一次就可以了


补充两个方法,XmlExpand的


        public static XmlNode addNode(XmlNode node, string name, string namespaceURI)
        {
            if (node == null)
            {
                return null;
            }
            XmlNode n = node.OwnerDocument.CreateNode(XmlNodeType.Element, name, namespaceURI);
            node.AppendChild(n);
            return n;
        }
        public static XmlNode addNode(XmlNode node, string name)
        {
            return addNode(node, name, "");
        }

public static void setAttribute(XmlNode node, string name, string attrib, string namespaceURI)
{
if (node.Name == "#text")
{
return;
}
if (node.Attributes[name] != null)
{
node.Attributes.GetNamedItem(name).Value = attrib;
}
else
{
XmlNode att = node.OwnerDocument.CreateNode(XmlNodeType.Attribute, name, namespaceURI);
att.Value = attrib;
node.Attributes.SetNamedItem(att);
}
}
public static void setAttribute(XmlNode node, string name, string attrib)
{
setAttribute(node,name,attrib,"");
}

本文作者:文盲老顾
本文发布时间:2018年06月30日
本文来自云栖社区合作伙伴 CSDN,了解相关信息可以关注csdn.net网站。
目录
相关文章
数据解析之xpath 太6了
数据解析之xpath 太6了
|
4天前
|
XML 数据采集 数据格式
Python 爬虫必备杀器,xpath 解析 HTML
【11月更文挑战第17天】XPath 是一种用于在 XML 和 HTML 文档中定位节点的语言,通过路径表达式选取节点或节点集。它不仅适用于 XML,也广泛应用于 HTML 解析。基本语法包括标签名、属性、层级关系等的选择,如 `//p` 选择所有段落标签,`//a[@href=&#39;example.com&#39;]` 选择特定链接。在 Python 中,常用 lxml 库结合 XPath 进行网页数据抓取,支持高效解析与复杂信息提取。高级技巧涵盖轴的使用和函数应用,如 `contains()` 用于模糊匹配。
|
1月前
|
XML JavaScript 前端开发
如何解析一个 HTML 文本
【10月更文挑战第23天】在实际应用中,根据具体的需求和场景,我们可以灵活选择解析方法,并结合其他相关技术来实现高效、准确的 HTML 解析。随着网页技术的不断发展,解析 HTML 文本的方法也在不断更新和完善,
|
1月前
|
JavaScript API 开发工具
<大厂实战场景> ~ Flutter&鸿蒙next 解析后端返回的 HTML 数据详解
本文介绍了如何在 Flutter 中解析后端返回的 HTML 数据。首先解释了 HTML 解析的概念,然后详细介绍了使用 `http` 和 `html` 库的步骤,包括添加依赖、获取 HTML 数据、解析 HTML 内容和在 Flutter UI 中显示解析结果。通过具体的代码示例,展示了如何从 URL 获取 HTML 并提取特定信息,如链接列表。希望本文能帮助你在 Flutter 应用中更好地处理 HTML 数据。
105 1
|
2月前
|
XML 数据格式
HTML 实例解析
本文介绍了HTML中常见元素的使用方法,包括`&lt;p&gt;`、`&lt;body&gt;`和`&lt;html&gt;`等。详细解析了这些元素的结构和作用,并强调了正确使用结束标签的重要性。此外,还提到了空元素的使用及大小写标签的规范。
|
2月前
|
XML 前端开发 数据格式
Beautiful Soup 解析html | python小知识
在数据驱动的时代,网页数据是非常宝贵的资源。很多时候我们需要从网页上提取数据,进行分析和处理。Beautiful Soup 是一个非常流行的 Python 库,可以帮助我们轻松地解析和提取网页中的数据。本文将详细介绍 Beautiful Soup 的基础知识和常用操作,帮助初学者快速入门和精通这一强大的工具。【10月更文挑战第11天】
62 2
|
3月前
|
索引 Python
XPath解析之获取属性
XPath解析(三)
53 10
|
3月前
|
Java
XPath解析(二)
XPath解析(二)
43 10
|
3月前
|
XML 数据格式
XPath解析(一)
XPath解析(一)
54 10
|
2月前
|
前端开发 JavaScript
pyquery:一个灵活方便的 HTML 解析库
pyquery:一个灵活方便的 HTML 解析库
25 1

推荐镜像

更多