解析Html源码
在上一篇中,获取了网页的html源码,但是里面东西太多了,并不是我们想要的结果
所以我们需要提取我们想要的结果
使用Selectable接口,你可以直接完成页面元素的链式抽取,也无需去关心抽取的细节。
我们通过Page对象获取了html对象,如果深入的话会发现,Html对象的父类实现了Selectable接口,它是可以进行提取元素的。
Selectable中的部分API
方法 | 说明 | 示例 |
---|---|---|
xpath(String xpath) | 使用XPath选择 | html.xpath("//div[@class=‘title’]") |
$(String selector) | 使用Css选择器选择 | html.$(“div.title”) |
$(String selector,String attr) | 同上,获取的是标签内容 | html.$(“div.title”,“text”) |
css(String selector) | 功能同$() 使用Css选择器选择 | html.css(“div.title”) |
links() | 选择所有链接 | html.links() |
regex(String regex) | 使用正则表达式抽取 | html.regex("(.*?)") |
regex(String regex,int group) | 使用正则表达式抽取,并指定捕获组 | html.regex("(.*?)",1) |
replace(String regex, String replacement) | 替换内容 | html.replace("","") |
CSS选择器
选择器 | 例子 | 例子描述 |
---|---|---|
.class | .intro | 选择 class=“intro” 的所有元素。 |
#id | #firstname | 选择 id=“firstname” 的所有元素。 |
element | p | 选择所有 < p > 元素。 |
element,element | div,p | 选择所有 < div > 元素和所有 < p > 元素。 |
element element | div p | 选择 < div > 元素内部的所有 < p > 元素。 |
XPath选择器
路径表达式 | 结果 |
---|---|
/bookstore/book[1] | 选取属于 bookstore 子元素的第一个 book 元素。 |
/bookstore/book[last()] | 选取属于 bookstore 子元素的最后一个 book 元素。 |
/bookstore/book[last()-1] | 选取属于 bookstore 子元素的倒数第二个 book 元素。 |
/bookstore/book[position() < 3] | 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。 |
//title[@lang] | 选取所有拥有名为 lang 的属性的 title 元素。 |
//title[@lang=‘eng’] | 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。 |
/bookstore/book[price>35.00] | 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。 |
/bookstore/book[price>35.00]/title | 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。 |
获取结果的API:
当链式调用结束时,我们一般都想要拿到一个字符串类型的结果。这时候就需要用到获取结果的API了。我们知道,一条抽取规则,无论是XPath、CSS选择器或者正则表达式,总有可能抽取到多条元素。WebMagic对这些进行了统一,你可以通过不同的API获取到一个或者多个元素。
方法 | 说明 | 示例 |
---|---|---|
get() | 返回一条String类型的结果 | String link= html.links().get() |
toString() | 功能同get(),返回一条String类型的结果 | String link= html.links().toString() |
all() | 返回所有抽取结果 | List links= html.links().all() |
match() | 是否有匹配结果 | if (html.links().match()){ xxx; } |
实战演示
我们要抓取的内容 就是CSDN的博客RSS订阅内容
https://blog.csdn.net/qq_18604209/rss/list
下面是抓取过程
我们先把抓取到的html输出一下,一点点分析
第一步 配置Spider
本次配置和上一篇中一样,就是改了一下抓取地址
public static void main(String[] args) {
//创建爬虫解析页面
PageProcessor pageProcessor = new FirstWebmagic();
//创建爬虫
Spider spider = Spider.create(pageProcessor);
//给爬虫添加爬取地址
spider.addUrl("https://blog.csdn.net/qq_18604209/rss/list");
//启动一个线程
spider.thread(1);
//启动爬虫
spider.run();
}
第二步 配置抓取相关信息
@Override
public void process(Page page) {
//抓取到的页面为一个page对象
Html html = page.getHtml();//我们从page里面获取Html信息
Selectable item = html.$("item");//获取item标签
Selectable title = item.$("title","text");//获取items标签的 title 中的文字
Selectable link = item.$("guid","text");//获取items标签的 guid 中的文字
List<String> titles = title.all();
List<String> links = link.all();
System.out.println(titles);
System.out.println(links);
}
理论上通过上面的方法就能抓取到,但实际上和我们想想的还是有点不一样
他的输出结果如下
[<![CDATA[[原][Java爬虫-WebMagic]-02-获取网页源码]]>, <![CDATA[[原][Java爬虫-WebMagic]-01-初识爬虫框架WebMagic]]>, ...]
[https://blog.csdn.net/qq_18604209/article/details/104208837, https://blog.csdn.net/qq_18604209/article/details/104208038,...]
集合links 的内容的确是我们想要的
但是 titles 还是有很多多余的内容,我们并不希望得到他
第三步 去除不必要的内容
Selectable 中有一个替换的Api是 replace
我们只需要在获取之后增加以下两句话 就可以将匹配的结果替换为空白
匹配文本是正则表达式匹配,将特殊符号用\注掉就行
title = title.replace("\\<!\\[CDATA\\[","");
title = title.replace("\\]>","");
增加后的输出结果
[[原][Java爬虫-WebMagic]-02-获取网页源码], [原][Java爬虫-WebMagic]-01-初识爬虫框架WebMagic],...]
如果看着这个"[原]"有点心烦 可以在上面加上这这些 记得用反斜杠标注中括号
完整的代码
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.processor.PageProcessor;
import us.codecraft.webmagic.selector.Html;
import us.codecraft.webmagic.selector.Selectable;
import java.util.List;
public class FirstWebmagic implements PageProcessor {
@Override
public void process(Page page) {
//抓取到的页面为一个page对象
Html html = page.getHtml();//我们从page里面获取Html信息
// System.out.println(html);
Selectable item = html.$("item");//获取item标签
Selectable title = item.$("title","text");//获取items标签的 title 中的文字
Selectable link = item.$("guid","text");//获取items标签的 guid 中的文字
//替换掉我们不想要的文字
title = title.replace("\\<!\\[CDATA\\[","");
title = title.replace("\\]>","");
//获取所有结果
List<String> titles = title.all();
List<String> links = link.all();
//吧结果输出
System.out.println(titles);
System.out.println(links);
}
@Override
public Site getSite() {
Site site = Site.me();//创建Site
site.setTimeOut(1000);//设置超时
site.setRetryTimes(3);//设置重试次数
site.setCharset("UTF-8");
return site;
}
public static void main(String[] args) {
//创建爬虫解析页面
PageProcessor pageProcessor = new FirstWebmagic();
//创建爬虫
Spider spider = Spider.create(pageProcessor);
//给爬虫添加爬取地址
spider.addUrl("https://blog.csdn.net/qq_18604209/rss/list");
//启动一个线程
spider.thread(1);
//启动爬虫
spider.run();
}
}