我正在索引一组网站,这些网站拥有从少量模板生成的大量页面(数千万)。我正在寻找一种算法来学习生成页面的模板,并将模板与页面匹配,这样我就只需要为每个被提取的页面存储变量部分和模板参考。
算法不需要产生尽可能大的压缩,但是当它看到更多页面时它应该会变得更好,并且当面对使用以前看不见的模板生成的页面时它应该优雅地表现。
我非常感谢任何文献或现有图书馆的引用。
我可以在批量页面上运行通用压缩算法。我不想这样做的原因是我感兴趣的数据将在页面的可变部分中,因此模板方法允许我在不解压缩的情况下对其进行检索。我希望能够重新创建整个页面,以确保未来的可复制性并防止我的抓取程序中的错误。
答案 0 :(得分:6)
在某些圈子中,此问题称为“HTML Wrapper Induction”或“Wrapper Learning”。在本文中,您可以找到一个有趣的 - 尽管是旧的 - 评论以及一些商业应用程序的链接:http://www.xrce.xerox.com/Research-Development/Historical-projects/IWRAP-Intelligent-Wrapper-Learning-Tools)
您可能对此Python库感兴趣:http://code.google.com/p/templatemaker/“嗯,假设您想从一堆使用相同模板的网页获取原始数据 - 例如Yelp.com上的餐厅评论,您可以为模板制作者提供任意数量的HTML文件,它将创建用于创建这些文件的“模板”。 (http://www.holovaty.com/writing/templatemaker/)
另外,另一个名为scrapy的Python库似乎有一个包装器归纳库:http://dev.scrapy.org/wiki/Scrapy09Changes#Addedwrapperinductionlibrary
但是,我无法详细说明这些算法。如果你想自己实现一个,这看起来是一个很好的起点:http://portal.acm.org/citation.cfm?id=1859138它具有包装器归纳和在线学习功能,因此您可以在爬行过程中继续对页面进行分类。答案 1 :(得分:4)
您考虑过clone detection了吗?这些工具决定了复制和粘贴代码的重复使用方式,这听起来很像你想要的。当然这些页面是以这种方式创建的,或者也许是作为“模板实例”生成的。这种克隆探测器会自动找出共性。
一些克隆检测器匹配最大长度相同的子串。这个问题是Web结构中的微不足道的差异(额外的空格,换行符,HTML注释)会阻止许多完全有效的匹配,所以你会错过案例。这也有缺陷,它只能找到相同的字符串,而不是带有变化点的模板。对字符串进行归纳的算法(其他海报的答案)可能会遇到这些问题。
你真正想要的是与构成语言(网页)的结构的匹配。
许多克隆检测器(“基于令牌”)找到最多一个令牌的公共令牌代码序列。通过虚拟使用特定语言词法分析器,这些通常可以忽略空白。你得到的是可变点是单一标记的模板。
根据我的经验,变体通常是语言子结构(表达式,语句序列......),因此您希望基于此进行克隆检测。我们的CloneDR工具使用语言语法来查找克隆来驱动进程,也就是说,它会对通过解析页面获得的抽象语法树进行检测。 (一些关键的克隆检测论文,包括关于CloneDR如何工作的论文,列在该维基百科页面上)。
在您的情况下,语言语法将成为制作网页的语言的混合,例如: HTML,JavaScript以及您用于动态生成Web文本的任何动态语言。 (我们有用于脏HTML,JavaScript,C#,Java,JSP,PHP的克隆检测器,[Perl还没有,但关闭!] ......)。您可以在链接中看到针对不同语言的一些克隆检测报告(遗憾的是,不是HTML的一种)。
CloneDR结果正是通用性(模板),变异点以及变异点的差异。
答案 2 :(得分:2)
在我看来,由于HTML本质上是 网页的结构化表示,因此最好的方法是完全忽略文本内容,并专注于识别相似和重复的页面结构。
由于单个网站的设计要求,其每个网页都将包含相同的一组功能子结构 - 例如菜单,边栏,面包屑,标题,页脚等 - 这样,在某个深度,同一网站内的所有页面在结构上都是相同的,在此深度以下,页面结构将有所不同。通过识别这种“相似性深度”,您应该能够隔离整个网站语料库中结构上不变的页面部分。
然后,对于以相同格式呈现不同数据的页面,如产品描述或其他数据支持的页面,页面的整个结构将是相同的,仅在文本内容中有所不同。通过屏蔽所有页面共享的不变特征,您应该能够将页面缩小为仅感兴趣的部分或部分。
其余的只是规范化HTML(使用HTMLTidy)并比较DOM树。
答案 3 :(得分:1)
鉴于大量页面使用单个模板,我们希望发现这些页面的longest common subsequence(LCS)与模板“shell”紧密对应。 (如果我们只有2或3页,那么文本中恰好以相同顺序出现的字母也会进入LCS - 但这不是一个showstopper。)
不幸的是,找到k序列的LCS需要k中的时间指数,但是可以通过计算LCS(a,LCS(b,LCS(c,...))来产生近似,其中每个LCS()操作都在2个长度为n的序列需要花费O(n ^ 2)的时间。事实上,我希望这个近似值能够在清除虚假的文本子序列时完成更好的工作,而不是完全解决问题。
到目前为止,我已经谈到了一个假设情况,其中所有文件都使用相同的模板。但我们遇到的问题是有多个模板。为了解决这个问题,我提出了一种聚类算法,我们可以在其中构建文件簇。对于每个群集,我们将维护迄今为止包含在该群集中的所有文件的LCS,使用上面给出的成对近似计算;为第i个集群调用此clcs[i]
。对于每个文件,对于每个群集i
,我们使用clcs[i]
计算文件的LCS,并将此LCS的长度与clcs[i]
的长度进行比较。如果此长度不小于clcs[i]
的长度,则此文件“非常适合”,因此将其添加到集群i
,并且刚刚计算的LCS将成为新的{{1} }。如果现有集群没有足够的文件适合该文件,则会创建一个新集群,其中仅包含此文件(及其LCS,它等于文件本身)。
关于“不低于”:这是一个需要进行一些调整的加权因子。显然,当一个新的集群刚刚诞生并且只包含一个文件时,我们可以预期使用相同模板生成的另一个文件将具有一个LCS,它远远短于该集群的LCS,所以我们应该容忍LCS长度下降。随着集群规模的扩大,其整体LCS应该稳定在模板“shell”上,因此如果它大大降低LCS长度,我们应该不太愿意向集群添加新文件。
如果文件以不同的顺序显示,该算法通常会产生一组不同的聚类,因此尝试一些不同的订单并检查是否出现相同的聚类可能是个好主意。
最后,要从群集中的给定文件中提取相关的变化信息,以下算法可以运行:
clcs[i]