jsoup Cookbook(中文版)--爬虫(java)
2015-07-30 10:11
465 查看
转载:http://www.open-open.com/jsoup/
目录:
入门
输入
数据抽取
数据修改
html清理
1、解析和遍历一个HTML文档
如何解析一个HTML文档:Stringhtml="<html><head><title>Firstparse</title></head>" +"<body><p>ParsedHTMLintoadoc.</p></body></html>"; Documentdoc=Jsoup.parse(html);
(更详细内容可查看
其解析器能够尽最大可能从你提供的HTML文档来创见一个干净的解析结果,无论HTML的格式是否完整。比如它可以处理:
没有关闭的标签(比如:
<p>Lorem<p>Ipsumparsesto
<p>Lorem</p><p>Ipsum</p>)
隐式标签(比如.它可以自动将
<td>Tabledata</td>包装成
<table><tr><td>?)
创建可靠的文档结构(html标签包含head和body,在head只出现恰当的元素)
一个文档的对象模型
文档由多个Elements和TextNodes组成(以及其它辅助nodes:详细可查看:其继承结构如下:
继承Document
继承Element
.Node
继承TextNode
.Node
一个Element包含一个子节点集合,并拥有一个父Element。他们还提供了一个唯一的子元素过滤列表。
参见
数据抽取:数据抽取:
2、解析一个HTML字符串
存在问题
来自用户输入,一个文件或一个网站的HTML字符串,你可能需要对它进行解析并取其内容,或校验其格式是否完整,或想修改它。怎么办?jsonu能够帮你轻松解决这些问题解决方法
使用静态方法或Jsoup.parse(Stringhtml)
示例代码:Jsoup.parse(Stringhtml,StringbaseUri)
Stringhtml="<html><head><title>Firstparse</title></head>" +"<body><p>ParsedHTMLintoadoc.</p></body></html>"; Documentdoc=Jsoup.parse(html);
描述
这方法能够将输入的HTML解析为一个新的文档(Document),参数baseUri是用来将相对URL转成绝对URL,并指定从哪个网站获取文档。如这个方法不适用,你可以使用parse(Stringhtml,StringbaseUri)
方法来解析成HTML字符串如上面的示例。.parse(Stringhtml)
只要解析的不是空字符串,就能返回一个结构合理的文档,其中包含(至少)一个head和一个body元素。
一旦拥有了一个Document,你就可以使用Document中适当的方法或它父类
和Element
中的方法来取得相关数据。Node
3、解析一个body片断
问题
假如你有一个HTML片断(比如.一个div包含一对
p标签;一个不完整的HTML文档)想对它进行解析。这个HTML片断可以是用户提交的一条评论或在一个CMS页面中编辑body部分。
办法
使用方法.Jsoup.parseBodyFragment(Stringhtml)
Stringhtml="<div><p>Loremipsum.</p>"; Documentdoc=Jsoup.parseBodyFragment(html); Elementbody=doc.body();
说明
parseBodyFragment方法创建一个空壳的文档,并插入解析过的HTML到
body元素中。假如你使用正常的
方法,通常你也可以得到相同的结果,但是明确将用户输入作为body片段处理,以确保用户所提供的任何糟糕的HTML都将被解析成body元素。Jsoup.parse(Stringhtml)
方法能够取得文档body元素的所有子元素,与Document.body()
doc.getElementsByTag("body")相同。
保证安全Staysafe
假如你可以让用户输入HTML内容,那么要小心避免跨站脚本攻击。利用基于的清除器和Whitelist
方法来清除用户输入的恶意内容。clean(StringbodyHtml,Whitelistwhitelist)
4、从一个URL加载一个Document
存在问题
你需要从一个网站获取和解析一个HTML文档,并查找其中的相关数据。你可以使用下面解决方法:解决方法
使用方法:Jsoup.connect(Stringurl)
Documentdoc=Jsoup.connect("http://example.com/").get(); Stringtitle=doc.title();
说明
方法创建一个新的connect(Stringurl)
,和Connection
取得和解析一个HTML文件。如果从该URL获取HTML时发生错误,便会抛出IOException,应适当处理。get()
接口还提供一个方法链来解决特殊请求,具体如下:Connection
Documentdoc=Jsoup.connect("http://example.com") .data("query","Java") .userAgent("Mozilla") .cookie("auth","token") .timeout(3000) .post();
这个方法只支持WebURLs(
http和
https协议);假如你需要从一个文件加载,可以使用
代替。parse(Filein,StringcharsetName)
5、从一个文件加载一个文档
问题
在本机硬盘上有一个HTML文件,需要对它进行解析从中抽取数据或进行修改。办法
可以使用静态方法:Jsoup.parse(Filein,StringcharsetName,StringbaseUri)
Fileinput=newFile("/tmp/input.html"); Documentdoc=Jsoup.parse(input,"UTF-8","http://example.com/");
说明
这个方法用来加载和解析一个HTML文件。如在加载文件的时候发生错误,将抛出IOException,应作适当处理。parse(Filein,StringcharsetName,StringbaseUri)
baseUri参数用于解决文件中URLs是相对路径的问题。如果不需要可以传入一个空的字符串。
另外还有一个方法
,它使用文件的路径做为parse(Filein,StringcharsetName)
baseUri。这个方法适用于如果被解析文件位于网站的本地文件系统,且相关链接也指向该文件系统。
6、使用DOM方法来遍历一个文档
问题
你有一个HTML文档要从中提取数据,并了解这个HTML文档的结构。方法
将HTML解析成一个之后,就可以使用类似于DOM的方法进行操作。示例代码:Document
Fileinput=newFile("/tmp/input.html"); Documentdoc=Jsoup.parse(input,"UTF-8","http://example.com/");
Elementcontent=doc.getElementById("content");
Elementslinks=content.getElementsByTag("a");
for(Elementlink:links){
StringlinkHref=link.attr("href");
StringlinkText=link.text();
}
说明
Elements这个对象提供了一系列类似于DOM的方法来查找元素,抽取并处理其中的数据。具体如下:查找元素
getElementById(Stringid)
getElementsByTag(Stringtag)
getElementsByClass(StringclassName)
(andrelatedmethods)getElementsByAttribute(Stringkey)
Elementsiblings:
,siblingElements()
,firstElementSibling()
;lastElementSibling()
,nextElementSibling()
previousElementSibling()
Graph:
,parent()
,children()
child(intindex)
元素数据
获取属性attr(Stringkey)
设置属性attr(Stringkey,Stringvalue)
获取所有属性attributes()
,id()
andclassName()
classNames()
获取文本内容text()
设置文本内容text(Stringvalue)
获取元素内HTMLhtml()
设置元素内的HTML内容html(Stringvalue)
获取元素外HTML内容outerHtml()
获取数据内容(例如:script和style标签)data()
andtag()
tagName()
操作HTML和文本
,append(Stringhtml)
prepend(Stringhtml)
,appendText(Stringtext)
prependText(Stringtext)
,appendElement(StringtagName)
prependElement(StringtagName)
html(Stringvalue)
7、使用选择器语法来查找元素
问题
你想使用类似于CSS或jQuery的语法来查找和操作元素。方法
可以使用和Element.select(Stringselector)
方法实现:Elements.select(Stringselector)
Fileinput=newFile("/tmp/input.html"); Documentdoc=Jsoup.parse(input,"UTF-8","http://example.com/");
Elementslinks=doc.select("a[href]");//带有href属性的a元素
Elementspngs=doc.select("img[src$=.png]");
//扩展名为.png的图片
Elementmasthead=doc.select("div.masthead").first();
//class等于masthead的div标签
ElementsresultLinks=doc.select("h3.r>a");//在h3元素之后的a元素
说明
jsoupelements对象支持类似于这个
select方法在
,Document
,或Element
对象中都可以使用。且是上下文相关的,因此可实现指定元素的过滤,或者链式选择访问。Elements
Select方法将返回一个
集合,并提供一组方法来抽取和处理结果。Elements
Selector选择器概述
tagname:通过标签查找元素,比如:
a
ns|tag:通过标签在命名空间查找元素,比如:可以用
fb|name语法来查找
<fb:name>元素
#id:通过ID查找元素,比如:
#logo
.class:通过class名称查找元素,比如:
.masthead
[attribute]:利用属性查找元素,比如:
[href]
[^attr]:利用属性名前缀来查找元素,比如:可以用
[^data-]来查找带有HTML5Dataset属性的元素
[attr=value]:利用属性值来查找元素,比如:
[width=500]
[attr^=value],
[attr$=value],
[attr*=value]:利用匹配属性值开头、结尾或包含属性值来查找元素,比如:
[href*=/path/]
[attr~=regex]:利用属性值匹配正则表达式来查找元素,比如:
img[src~=(?i)\.(png|jpe?g)]
*:这个符号将匹配所有元素
Selector选择器组合使用
el#id:元素+ID,比如:
div#logo
el.class:元素+class,比如:
div.masthead
el[attr]:元素+class,比如:
a[href]
任意组合,比如:
a[href].highlight
ancestorchild:查找某个元素下子元素,比如:可以用
.bodyp查找在"body"元素下的所有
p元素
parent>child:查找某个父元素下的直接子元素,比如:可以用
div.content>p查找
p元素,也可以用
body>*查找body标签下所有直接子元素
siblingA+siblingB:查找在A元素之前第一个同级元素B,比如:
div.head+div
siblingA~siblingX:查找A元素之前的同级X元素,比如:
h1~p
el,el,el:多个选择器组合,查找匹配任一选择器的唯一元素,例如:
div.masthead,div.logo
伪选择器selectors
:lt(n):查找哪些元素的同级索引值(它的位置在DOM树中是相对于它的父节点)小于n,比如:
td:lt(3)表示小于三列的元素
:gt(n):查找哪些元素的同级索引值大于
n
,比如:
divp:gt(2)表示哪些div中有包含2个以上的p元素
:eq(n):查找哪些元素的同级索引值与
n相等,比如:
forminput:eq(1)表示包含一个input标签的Form元素
:has(seletor):查找匹配选择器包含元素的元素,比如:
div:has(p)表示哪些div包含了p元素
:not(selector):查找与选择器不匹配的元素,比如:
div:not(.logo)表示不包含class="logo"元素的所有div列表
:contains(text):查找包含给定文本的元素,搜索不区分大不写,比如:
p:contains(jsoup)
:containsOwn(text):查找直接包含给定文本的元素
:matches(regex):查找哪些元素的文本匹配指定的正则表达式,比如:
div:matches((?i)login)
:matchesOwn(regex):查找自身包含文本匹配指定正则表达式的元素
注意:上述伪选择器索引是从0开始的,也就是说第一个元素索引值为0,第二个元素index为1等
8、从元素抽取属性,文本和HTML
问题
在解析获得一个Document实例对象,并查找到一些元素之后,你希望取得在这些元素中的数据。方法
要取得一个属性的值,可以使用方法Node.attr(Stringkey)
对于一个元素中的文本,可以使用
方法Element.text()
对于要取得元素或属性中的HTML内容,可以使用
,或Element.html()
方法Node.outerHtml()
示例:
Stringhtml="<p>An<ahref='http://example.com/'><b>example</b></a>link.</p>"; Documentdoc=Jsoup.parse(html);//解析HTML字符串返回一个Document实现 Elementlink=doc.select("a").first();//查找第一个a元素 Stringtext=doc.body().text();//"Anexamplelink"//取得字符串中的文本 StringlinkHref=link.attr("href");//"http://example.com/"//取得链接地址 StringlinkText=link.text();//"example""//取得链接地址中的文本 StringlinkOuterH=link.outerHtml(); //"<ahref="http://example.com"><b>example</b></a>" StringlinkInnerH=link.html();//"<b>example</b>"//取得链接内的html内容
说明
上述方法是元素数据访问的核心办法。此外还其它一些方法可以使用:Element.id()
Element.tagName()
andElement.className()
Element.hasClass(StringclassName)
这些访问器方法都有相应的setter方法来更改数据.
参见
和Element
集合类的参考文档Elements
9、处理URLs
问题
你有一个包含相对URLs路径的HTML文档,需要将这些相对路径转换成绝对路径的URLs。方法
在你解析文档时确保有指定baseURI,然后
使用
abs:属性前缀来取得包含
baseURI的绝对路径。代码如下:
Documentdoc=Jsoup.connect("http://www.open-open.com").get(); Elementlink=doc.select("a").first(); StringrelHref=link.attr("href");//=="/" StringabsHref=link.attr("abs:href");//"http://www.open-open.com/"
说明
在HTML元素中,URLs经常写成相对于文档位置的相对路径:<ahref="/download">...</a>.当你使用
方法来取得a元素的href属性时,它将直接返回在HTML源码中指定定的值。Node.attr(Stringkey)
假如你需要取得一个绝对路径,需要在属性名前加
abs:前缀。这样就可以返回包含根路径的URL地址
attr("abs:href")
因此,在解析HTML文档时,定义baseURI非常重要。
如果你不想使用
abs:前缀,还有一个方法能够实现同样的功能
。Node.absUrl(Stringkey)
10、示例程序:获取所有链接
这个示例程序将展示如何从一个URL获得一个页面。然后提取页面中的所有链接、图片和其它辅助内容。并检查URLs和文本信息。运行下面程序需要指定一个URLs作为参数
packageorg.jsoup.examples; importorg.jsoup.Jsoup; importorg.jsoup.helper.Validate; importorg.jsoup.nodes.Document; importorg.jsoup.nodes.Element; importorg.jsoup.select.Elements; importjava.io.IOException; /** *ExampleprogramtolistlinksfromaURL. */ publicclassListLinks{ publicstaticvoidmain(String[]args)throwsIOException{ Validate.isTrue(args.length==1,"usage:supplyurltofetch"); Stringurl=args[0]; print("Fetching%s...",url); Documentdoc=Jsoup.connect(url).get(); Elementslinks=doc.select("a[href]"); Elementsmedia=doc.select("[src]"); Elementsimports=doc.select("link[href]"); print("\nMedia:(%d)",media.size()); for(Elementsrc:media){ if(src.tagName().equals("img")) print("*%s:<%s>%sx%s(%s)", src.tagName(),src.attr("abs:src"),src.attr("width"),src.attr("height"), trim(src.attr("alt"),20)); else print("*%s:<%s>",src.tagName(),src.attr("abs:src")); } print("\nImports:(%d)",imports.size()); for(Elementlink:imports){ print("*%s<%s>(%s)",link.tagName(),link.attr("abs:href"),link.attr("rel")); } print("\nLinks:(%d)",links.size()); for(Elementlink:links){ print("*a:<%s>(%s)",link.attr("abs:href"),trim(link.text(),35)); } } privatestaticvoidprint(Stringmsg,Object...args){ System.out.println(String.format(msg,args)); } privatestaticStringtrim(Strings,intwidth){ if(s.length()>width) returns.substring(0,width-1)+"."; else returns; } } org/jsoup/examples/ListLinks.java 示例输入结果 Fetchinghttp://news.ycombinator.com/... Media:(38) *img:<http://ycombinator.com/images/y18.gif>18x18() *img:<http://ycombinator.com/images/s.gif>10x1() *img:<http://ycombinator.com/images/grayarrow.gif>x() *img:<http://ycombinator.com/images/s.gif>0x10() *script:<http://www.co2stats.com/propres.php?s=1138> *img:<http://ycombinator.com/images/s.gif>15x1() *img:<http://ycombinator.com/images/hnsearch.png>x() *img:<http://ycombinator.com/images/s.gif>25x1() *img:<http://mixpanel.com/site_media/images/mixpanel_partner_logo_borderless.gif>x(AnalyticsbyMixpan.) Imports:(2) *link<http://ycombinator.com/news.css>(stylesheet) *link<http://ycombinator.com/favicon.ico>(shortcuticon) Links:(141) *a:<http://ycombinator.com>() *a:<http://news.ycombinator.com/news>(HackerNews) *a:<http://news.ycombinator.com/newest>(new) *a:<http://news.ycombinator.com/newcomments>(comments) *a:<http://news.ycombinator.com/leaders>(leaders) *a:<http://news.ycombinator.com/jobs>(jobs) *a:<http://news.ycombinator.com/submit>(submit) *a:<http://news.ycombinator.com/x?fnid=JKhQjfU7gW>(login) *a:<http://news.ycombinator.com/vote?for=1094578&dir=up&whence=%6e%65%77%73>() *a:<http://www.readwriteweb.com/archives/facebook_gets_faster_debuts_homegrown_php_compiler.php?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+readwriteweb+%28ReadWriteWeb%29&utm_content=Twitter>(FacebookspeedsupPHP) *a:<http://news.ycombinator.com/user?id=mcxx>(mcxx) *a:<http://news.ycombinator.com/item?id=1094578>(9comments) *a:<http://news.ycombinator.com/vote?for=1094649&dir=up&whence=%6e%65%77%73>() *a:<http://groups.google.com/group/django-developers/msg/a65fbbc8effcd914>("Tough.DjangoproducesXHTML.") *a:<http://news.ycombinator.com/user?id=andybak>(andybak) *a:<http://news.ycombinator.com/item?id=1094649>(3comments) *a:<http://news.ycombinator.com/vote?for=1093927&dir=up&whence=%6e%65%77%73>() *a:<http://news.ycombinator.com/x?fnid=p2sdPLE7Ce>(More) *a:<http://news.ycombinator.com/lists>(Lists) *a:<http://news.ycombinator.com/rss>(RSS) *a:<http://ycombinator.com/bookmarklet.html>(Bookmarklet) *a:<http://ycombinator.com/newsguidelines.html>(Guidelines) *a:<http://ycombinator.com/newsfaq.html>(FAQ) *a:<http://ycombinator.com/newsnews.html>(NewsNews) *a:<http://news.ycombinator.com/item?id=363>(FeatureRequests) *a:<http://ycombinator.com>(YCombinator) *a:<http://ycombinator.com/w2010.html>(Apply) *a:<http://ycombinator.com/lib.html>(Library) *a:<http://www.webmynd.com/html/hackernews.html>() *a:<http://mixpanel.com/?from=yc>()
11、设置属性的值
问题
在你解析一个Document之后可能想修改其中的某些属性值,然后再保存到磁盘或都输出到前台页面。方法
可以使用属性设置方法,和Element.attr(Stringkey,Stringvalue)
.Elements.attr(Stringkey,Stringvalue)
假如你需要修改一个元素的
class属性,可以使用
和Element.addClass(StringclassName)
方法。Element.removeClass(StringclassName)
提供了批量操作元素属性和class的方法,比如:要为div中的每一个a元素都添加一个Elements
rel="nofollow"可以使用如下方法:
doc.select("div.commentsa").attr("rel","nofollow");
说明
与中的其它方法一样,Element
attr方法也是返回当
(或在使用选择器是返回Element
集合)。这样能够很方便使用方法连用的书写方式。比如:Elements
doc.select("div.masthead").attr("title","jsoup").addClass("round-box");
12、设置一个元素的HTML内容
问题
你需要一个元素中的HTML内容方法
可以使用中的HTML设置方法具体如下:Element
Elementdiv=doc.select("div").first();//<div></div> div.html("<p>loremipsum</p>");//<div><p>loremipsum</p></div> div.prepend("<p>First</p>");//在div前添加html内容 div.append("<p>Last</p>");//在div之后添加html内容 //添完后的结果:<div><p>First</p><p>loremipsum</p><p>Last</p></div> Elementspan=doc.select("span").first();//<span>One</span> span.wrap("<li><ahref='http://example.com/'></a></li>"); //添完后的结果:<li><ahref="http://example.com"><span>One</span></a></li>
说明
这个方法将先清除元素中的HTML内容,然后用传入的HTML代替。Element.html(Stringhtml)
和Element.prepend(Stringfirst)
方法用于在分别在元素内部HTML的前面和后面添加HTML内容Element.append(Stringlast)
对元素包裹一个外部HTML内容。Element.wrap(Stringaround)
参见
可以查看API参考文档中和Element.prependElement(Stringtag)
方法来创建新的元素并作为文档的子元素插入其中。Element.appendElement(Stringtag)
13、设置元素的文本内容
问题
你需要修改一个HTML文档中的文本内容方法
可以使用的设置方法::Element
Elementdiv=doc.select("div").first();//<div></div> div.text("five>four");//<div>five>four</div> div.prepend("First"); div.append("Last"); //now:<div>Firstfive>fourLast</div>
说明
文本设置方法与将清除一个元素中的内部HTML内容,然后提供的文本进行代替Element.text(Stringtext)
和Element.prepend(Stringfirst)
将分别在元素的内部html前后添加文本节点。Element.append(Stringlast)
对于传入的文本如果含有像
<,
>等这样的字符,将以文本处理,而非HTML。
14、消除不受信任的HTML(来防止XSS攻击)
问题
在做网站的时候,经常会提供用户评论的功能。有些不怀好意的用户,会搞一些脚本到评论内容中,而这些脚本可能会破坏整个页面的行为,更严重的是获取一些机要信息,此时需要清理该HTML,以避免跨站脚本方法
使用jsoupHTML方法进行清除,但需要指定一个可配置的Cleaner
。Whitelist
Stringunsafe=
"<p><ahref='http://example.com/'onclick='stealCookies()'>Link</a></p>";
Stringsafe=Jsoup.clean(unsafe,Whitelist.basic());
//now:<p><ahref="http://example.com/"rel="nofollow">Link</a></p>
说明
XSS又叫CSS(CrossSiteScript),跨站脚本攻击。它指的是恶意攻击者往Web页面里插入恶意html代码,当用户浏览该页之时,嵌入其中Web里面的html代码会被执行,从而达到恶意攻击用户的特殊目的。XSS属于被动式的攻击,因为其被动且不好利用,所以许多人常忽略其危害性。所以我们经常只让用户输入纯文本的内容,但这样用户体验就比较差了。一个更好的解决方法就是使用一个富文本编辑器WYSIWYG如
jsoup的whitelist清理器能够在服务器端对用户输入的HTML进行过滤,只输出一些安全的标签和属性。
jsoup提供了一系列的
基本配置,能够满足大多数要求;但如有必要,也可以进行修改,不过要小心。Whitelist
这个cleaner非常好用不仅可以避免XSS攻击,还可以限制用户可以输入的标签范围。
参见
参阅参阅
,了解如何返回一个Cleaner
对象,而不是字符串Document
参阅
,了解如何创建一个自定义的whitelistWhitelist
相关文章推荐
- EJB学习(二)——使用Eclipse+JBOSS创建第一个EJB项目
- Eclipse/Myeclipse中Installed JREs,添加jdk和jre的区别
- java序列化java.io.Externalizable
- org.springframework.web.servlet.view
- Java IO【16】利用转换流简单的写一些代码
- 企业支付宝账号开发接口教程--JAVA-UTF-8(实际操作完善中...SpringMVC+JSP)
- Spring IoC原理实例分析
- 7.30java学习笔记
- SpringMVC中Velocity的配置
- Java开发中常见的5个错误
- JAVA中的equals(二)
- Java基础(二)巧用Java中Calendar工具类
- JAVA中的equals(一)
- java.io.IOException:stream closed 异常的原因及处理
- Java 容器与泛型
- Eclipse javax.servlet.jsp.PageContext cannot be resolved to a type 错误解决办法
- Java泛型中E、T、K、V等的含义
- MyEclipse10.7破解后将工程导成war包时报错解决
- 配置log4j日志
- 《深入浅出struts2》--自定义拦截器以及自定义拦截器栈