TagSoup是一个用于解析HTML/XML的库。它支持HTML5规范,可以用于解析格式良好的XML,也可以解析来自web的非结构化和格式错误的HTML。该库还提供了从HTML文档中提取信息的有用功能,非常适合屏幕抓取。
该库提供了非结构化标记列表的基本数据类型、将HTML转换为该标记类型的解析器,以及用于查找和提取信息的有用函数和组合符。本文档给出了两个从web上获取信息的特定示例,而在样品源存储库中的文件。我们给出的示例如下:
- 获取Haskell wiki的最后修改日期
- 获取西蒙·佩顿·琼斯的最新论文列表
- 其他一些示例的简要概述
这个库的初始版本是用Javascript编写的,已经用于涉及屏幕抓取的各种商业项目。在这些例子中,包括了从痛苦的经历中学到的关于屏幕抓取的一般提示。应该注意的是,如果您依赖的数据可能会在任何给定的时间被其他人更改,那么您可能会大吃一惊!
这个库是在不了解Java版本的情况下编写的塔格汤。他们做出了一个非常不同的设计决策:确保存在默认属性,并正确嵌套解析的标记。我们不这样做——标签只是一个没有嵌套信息的列表。
致谢
感谢迈克·多兹(Mike Dodds)说服我把这篇文章写成图书馆。感谢许多调试和代码贡献者,包括:Gleb Alexeev、Ketil Malde、Conrad Parker、Henning Thielemann、Dino Morelli、Emily Mitchell、Gwern Branwen。
潜在的Bug
这些示例可能会出现两个错误:
- 被抓取的网站可能会发生变化。对此我无能为力,但如果您怀疑是这种情况,请告诉我,我将更新示例和教程。我已经这样做了好几次了,只需要几分钟的时间。
- 这个
打开URL
方法可能不起作用。这种情况经常发生,根据您的服务器、代理和风向,它们可能不起作用wget公司
要在本地下载页面,请使用readFile(读取文件)
相反。希望能够出现一个像样的Haskell HTTP库,并且可以使用它。
Haskell wiki的上次修改日期
我们的目标是开发一个程序,显示wiki的日期wiki.haskell.org网站
上次修改的。这个这个例子涵盖了设计一个基本的web抓取应用程序的所有基础知识。
查找页面
我们首先需要找到信息显示的位置和格式。看看首页,何时未登录,我们看到:
<ul id=“f-list”><li id=“lastmod”>本页上次修改时间为2013年9月9日22:38</li><li id=“copyright”>最新内容位于简单许可证下</li>隐私政策</a></li>关于HaskellWiki</a></li>免责声明</a></li></ul>
因此,我们看到最后修改日期可用。这就引出了规则1:
规则1:删除页面返回的内容,而不是浏览器呈现的内容或视图源提供的内容。
一些web服务器将根据用户代理提供不同的内容,一些浏览器将使用脚本修改其显示的HTML,一些页面将根据您的Cookie显示不同的内容。在你开始弄清楚如何开始抓取之前,首先决定程序的输入是什么。有两种方法可以获得程序中显示的页面。
使用HTTP包
我们可以使用HTTP包:
模块Main,其中导入Network.HTTPopenURL::String->IO字符串openURL x=getResponseBody=<<simpleHTTP(getRequest x)主::IO()main=做src<-openURL“http://wiki.haskell.org/haskell"writeFile“temp.htm”src
现在打开温度.htm
,找到包含命中计数的HTML片段,并对其进行检查。
现在我们检查包含我们的信息片段的片段,以及更宽的页面。碎片有什么其他东西都没有的?什么我们会使用算法来获得那个特定元素吗?我们怎么还能在内容更改时返回元素?如果设计改变了怎么办?但是等待,然后再继续:
规则2:不要对设计更改保持稳健,甚至在编写代码时也不要考虑这种可能性。
如果用户更改其网站,他们将以不可预测的方式进行更改。他们可以移动页面,可以将信息放在其他地方,也可以完全删除信息。如果你想要一些可靠的东西,请与网站所有者交谈,或者从某人那里购买数据。如果你试着考虑设计更改,你会使设计复杂化,但它仍然不会起作用。最好快速编写一个提取方法,并在情况发生变化时愉快地重写它。
现在,让我们考虑上面的片段。查找标签很有用它在你的代码片段上方是独一无二的,有一个漂亮的身份证件
或班
属性-不太可能多次出现的东西。在上面的示例,一个身份证件
具有值拉斯特莫德
看起来很完美。
模块Main,其中导入数据。烧焦导入Network.HTTP导入文本。HTML。塔格汤openURL::String->IO字符串openURL x=getResponseBody=<<simpleHTTP(getRequest x)haskellLastModifiedDateTime::IO()haskellLastModifiedDateTime=dosrc<-openURL“http://wiki.haskell.org/haskell"let lastModifiedDateTime=fromFooter$parseTags srcputStrLn$“wiki.haskell.org上次修改时间为”++lastModifiedDateTime其中fromFooter=unwords。下降6。话。innerText。取2。dropWhile(~/=“<li id=lastmod>”)主::IO()main=haskellLastModifiedDateTime
现在我们开始编写代码!首先要做的是打开所需的URL,然后将代码解析为标签
s与解析标记
. The从页脚
函数执行有趣的操作,可以从右向左阅读:
- 首先我们扔掉所有东西(
dropWhile(删除While)
)直到我们到达李
标签包含id=最后修改
. The(~==)
和(~/=)
操作员不同于标准等式和不等式,因为它们允许附加属性存在。我们写作“<li id=lastmod>”
作为语法糖标记打开“li”[(“id”,“lastmod”)]
。如果我们只想使用给定的身份证件
我们本可以写的属性(~==TagOpen“”[(“id”,“lastmod”)])
还有这个会匹配的。匹配的第二个元素中的任何空字符串都是被视为通配符。
- 接下来我们取两个元素:
<li>
标记和文本节点如下所示。
- 我们称之为
内部文本
函数从内部获取所有文本值,它将只是后面的文本节点拉斯特莫德
.
- 我们将字符串拆分为一系列单词,并删除前六个单词,即话
这个
,第页
,是
,最后的
,被改进的
和在
- 我们将剩下的单词重新组合成结果字符串
2013年9月9日22时38分。
这段代码可能看起来有点乱,事实上,这正是从标记汤中提取信息的本质。
规则3:TagSoup用于提取结构丢失的信息,如果可用,请使用更多结构化信息。
西蒙的论文
我们下一个非常重要的任务是从西蒙·佩顿·琼斯(Simon Peyton Jones)最近的研究论文中提取一份清单主页上一个例子最大的变化是,现在我们需要一个论文列表,而不仅仅是一个结果。
与之前一样,我们首先编写一个简单的程序,下载适当的页面,并查找常见模式。这一次,我们要查找每次提到一篇论文时出现的所有模式,而不是其他地方。另一个与上次不同的地方是,之前我们抓取了一条自动生成的信息——这次信息是由人以更自由的方式输入的。
首先,我们发现页面有命名锚,有一个当前工作锚,然后是Haskell的锚。我们可以用一个简单的拿
/滴
一对:
takeWhile(~/=“<a name=haskell>”)$删除5$dropWhile(~/=“<a name=current>”)标记
这段代码一直拖到“当前”部分,然后再拖到“haskell”部分,确保我们只查看页面的重要部分。接下来,我们要查找此部分中的所有超链接:
映射$sections(~==“<A>”)$。。。
请记住,选择名称为“A”的所有标记的函数可以写为(~==标记打开“A”[])
,或者也可以isTagOpenName“A”
。然后我们用一个(f)
功能。此函数需要获取链接后开始的标记,并查找链接内的文本。
f=取消报价。脱颖而出。话。来自标记文本。头部。筛选器为TagText
在这里,与人工编写的标记交互的复杂性就显现出来了。有些链接是斜体的,有些不是-滤波器
删除所有不存在的,直到找到纯文本节点。这个脱颖而出。话
删除所有多个空格,用空格替换制表符和换行符,并修剪前后部分——这是处理源代码处有空格但显示时没有空格的文本时的一个巧妙技巧。最后要考虑的是,有些论文在名字周围加了引号,有些没有,如果有引号,就会去掉引号。
为了完整起见,我们现在展示整个示例:
模块Main,其中导入Network.HTTP导入文本。HTML。塔格汤openURL::字符串->IO字符串openURL x=getResponseBody=<<simpleHTTP(getRequest x)spj论文::IO()spjPapers=do标记<-parseTags<$>openURL“http://research.microsoft.com/en-us/people/simonpj/"let links=映射f$节(~==“<A>”)$takeWhile(~/=“<a name=haskell>”)$删除5$dropWhile(~/=“<a name=current>”)标记putStr$unlines链接哪里f::[Tag String]->字符串f=取消报价。脱颖而出。话。来自标记文本。头部。过滤器是标记文本dequote('\“':xs)|last xs=='\”'=init xsdequote x=x主::IO()main=spj论文
其他示例
以下给出了更多示例样品.hs文件,包括从我的站点获取论文(短)列表、获取当前时间和基本的XML验证器。所有的屏幕刮刀都使用了与这里介绍的风格非常相似的风格——书写屏幕刮刀遵循标准的模式。我们提供两个代码只是为了享受。
我的论文
模块Main,其中导入Network.HTTP导入文本。HTML。塔格汤openURL::String->IO字符串openURL x=getResponseBody=<<simpleHTTP(getRequest x)ndm论文::IO()ndmPapers=do标记<-parseTags<$>openURL“http://community.haskell.org/~ndm/下载/“let papers=映射f$sections(~==“<li class=paper>”)标记putStr$取消纸张的链接哪里f::[Tag String]->字符串f xs=fromTagText(xs!!2)主::IO()main=ndm论文
英国时间
模块Main,其中导入Network.HTTP导入文本。HTML。塔格汤openURL::String->IO字符串openURL x=getResponseBody=<<simpleHTTP(getRequest x)当前时间::IO()currentTime=do标记<-parseTags<$>openURL“http://www.timeanddate.com/worldclock/uk/longon"让时间=fromTagText(dropWhile(~/=“<span id=ct>”)标记!!1)putStrLn时间主::IO()main=当前时间
其他示例
在样品.hs下面列出了以下附加示例:
- 谷歌技术新闻
- 黑客软件包列表
- 在sequence.complete.org上打印文章作者的姓名
- 分析表的行