巧夺天工:采用正则表达式解决树匹配问题

简介:
一、问题背景

    最近在开发 网络风行者(参见我的博客文章:《Web风行者的设计方案与计划》,《网络风行者(KSpider)的规则体系结构》)的 Web 版,与Web风行者的设计方案与计划 中不同的是,新版本采用C#/Asp.Net开发,Web页面解析引擎采用的是 HtmlAgilityPack。开发思想遵照 网络风行者(KSpider)的规则体系结构 一文。新版名字改名为 OrcSpider网络风行者,Orc代表兽族的英勇和嗜血。

    现有的网络数据提取程序采用的提取规则主要是正则表达式匹配提取,《网络风行者(KSpider)的规则体系结构》一文中的规则是基于树的匹配,在直观性、规则的柔性、规则的制定成本上有很大的优势。树匹配在技术上存在难点,难点主要在于多节点匹配算法(关于节点匹配,参见《网络风行者(KSpider)的规则体系结构》),描述如下:

    规则节点列表由一系列有序的规则节点组成,记为 P[P1,P2,…Pm],给定一个内容节点系列N[N1,N2,…….Nn]。要求在N中查找满足P的内容节点系列。

    举例说明,以cnblogs为例(例子猥琐了点,dudu勿怪,俺本世纪以来爬cnblogs页面数在10次以下):

    首页的标签结构摘录如下:

          …
          
< H3 >
            
< HREF >
            
</ A >
          
</ H3 >
          
< H4 >
            
< HREF >
            
</ A >
            
< CLASS ALIGN >
              
< HREF CLASS >
              
</ A >
              
< HREF CLASS >
              
</ A >
            
</ P >
          
</ H4 >
          
< H3 >
            
< HREF >
            
</ A >
          
</ H3 >
          
< H4 >
            
< HREF >
            
</ A >
            
< CLASS ALIGN >
              
< HREF CLASS >
              
</ A >
              
< HREF CLASS >
              
</ A >
            
</ P >
          
</ H4 >
          …

    可以看出一个规则节点系列:

< H3 ></ H3 >
< H4 ></ H4 >

    这一系列代表的是首页的一篇文章。

    对于页面节点的子节点列表,采用上面规则节点进行递归匹配,就可以找出页面上所有的文章。

    为了让规则使用更广泛,对于规则节点,可引入通配符*,+,?*代表0个或多个匹配,+代表1个或多个匹配,?代表0个或1个匹配,比如,这样的规则P[P1+,P2?,P3*],代表匹配的节点列表必须依次为1到多个满足P1的节点,01个满足P2的节点,0到多个满足P3的节点。

    这个问题和字符串的正则表达式匹配是同类型的问题。为了解决这个问题,俺是跋山涉水啊,翻山越岭啊。先是刨出了java的正则表达式匹配部分代码,近10000行,一堆内部类,吓的俺倒吸一口气。到 sourceforge 查询正则树相关开源程序,只找到一个 C 语言版的,它自定义了一堆语法,文档看的俺头昏。没办法之下,自己手写,边写边测试,改bug,写是写出来了,测试也没问题,只是程序的可读性太差,添加新规则的测试量巨大。

二、解决方案

    分析节点列表匹配和字符串匹配的相同点,可以巧妙的借助字符串匹配来解决节点列表的匹配问题。

    字符串匹配问题可表述为:给定一个正则表达式R,从字符串S[C1,C2,…,Cn]中找出符合的所有子字符串。

    可以将内容节点等同于字符,将规则节点等同于字符集。用Cni表示 内容节点Ni所对应的字符。用Pi{Ci1,Ci2,…}代表一个规则节点,其中{Ci1,Ci2…}代表规则节点能匹配字符的集合。Cnext为下一个未分配的字符。存在大量的内容节点,这些内容节点和任一规则节点都不匹配,这样的节点统一记作一个字符C0,以节省字符使用量。

    在 N[N1,N2,…….Nn] 中查找 P[P1,P2,…Pm] 的算法如下:

    第一步:为P1……Pn各分配一个字符:P1{C1},P2{C2},….Pn(Cm)

    第二步:遍历N,对于Ni

    (1)如果P1…Pm均不能匹配Ni,则将字符C0分配给Ni

    (2)如果P1…Pm中只有一个能够匹配Ni的规则Pk,则将字符Ck分配给Ni

    (3)如果P1…Pm中有多个能够匹配Ni的规则,则给Ni分配一个新字符Cnext,同时将Cnext加入每一个能够匹配Ni的规则的字符集中。

    于是节点串匹配问题归结为字符串匹配问题:

    规则            (C11|C12|…)(C21|C22|…)……(Cm1|Cm2|…)

    字符串  :      Cn1Cn2Cn3…Cnn

    通过正则表达式匹配,可以得到一个Match列表,根据Matchindex  length 可以取得匹配的内容节点列表。

    鉴于需要分配的字符不定,就将第一个需要分配的字符定义为\u0000,依此向上加即可。


    在这个算法架构基础上,引入通配符是再简单也不过了。引入其它正则表达式语法也是举手之劳。

本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2007/07/19/823639.html如需转载请自行联系原作者


xiaotie 集异璧实验室(GEBLAB)

相关文章
Python 内置正则表达式库re的使用
正则表达式是记录文本规则的代码,用于查找和处理符合特定规则的字符串。在Python中,常通过原生字符串`r&#39;string&#39;`表示。使用`re.compile()`创建正则对象,便于多次使用。匹配字符串有`match()`(从开头匹配)、`search()`(搜索首个匹配)和`findall()`(找所有匹配)。替换字符串用`sub()`,分割字符串则用`split()`。
|
Python Windows
【Python进阶必备】一文掌握re库:实战正则表达式
【Python进阶必备】一文掌握re库:实战正则表达式
459 0
|
数据库 Python
Python网络数据抓取(8):正则表达式
Python网络数据抓取(8):正则表达式
|
自然语言处理 JavaScript 前端开发
Python高级语法与正则表达式(二)
正则表达式描述了一种字符串匹配的模式,可以用来检查一个串是否含有某种子串、将匹配的子串做替换或者从某个串中取出符合某个条件的子串等。
|
安全 算法 Python
Python高级语法与正则表达式(一)
Python提供了 with 语句的写法,既简单又安全。 文件操作的时候使用with语句可以自动调用关闭文件操作,即使出现异常也会自动关闭文件操作。
Python使用正则表达式分割字符串
在Python中,你可以使用re模块的split()函数来根据正则表达式分割字符串。这个函数的工作原理类似于Python内置的str.split()方法,但它允许你使用正则表达式作为分隔符。
Python中re模块的正则表达式
【6月更文挑战第2天】了解Python的re模块,它是处理正则表达式的核心工具。正则表达式用于在文本中查找特定模式。本文讨论了re模块的用法和技巧,包括导入模块、匹配、分组、替换文本、编译正则表达式以及使用预定义字符类、量词、锚点等高级功能。通过实例展示了如何在Python中执行这些操作,帮助提升文本处理能力。掌握这些技巧将使你更有效地利用正则表达式解决字符串处理问题。
|
数据安全/隐私保护 Python
Python进阶---正则表达式
Python进阶---正则表达式
112 2
|
数据采集 Python
python中的正则表达式,Python实习面试经验汇总
python中的正则表达式,Python实习面试经验汇总
|
Python
使用Python解析网页和正则表达式
使用Python解析网页涉及`requests`和`re`模块。首先导入这两个模块,然后用`requests.get()`发送HTTP请求获取URL内容。通过`.text`属性得到HTML文本。接着,利用正则表达式和`re.search()`匹配特定模式(如网页标题),并用`.group(1)`获取匹配数据。最后,对提取的信息进行处理,如打印标题。实际操作时,需根据需求调整正则表达式。
223 2