多级爬取
有时候我们需要爬取一些多级的资料
例如 我想爬取博客 所有分栏 的 所有博文 这样的话单纯的像之前的爬取方法爬不到
而且分栏是可以改变的 可能作者突然加了个分栏,你的程序就需要做一次修改
为了解决这种问题,所以可以使用 多次爬取来解决
第一步 配置爬虫入口
这里直接使用简写方式了
//只需要在这里添加上博客地址就可以了
private static String homeUrl = "https://blog.csdn.net/qq_18604209";
public static void main(String[] args) {
Spider.create(new MoreProcessor()).addUrl(homeUrl).thread(5).run();
}
第二步 配置模型
我们需要一个数据类型来存放我们爬取的结果
这里创建了一个Item类,用来表示爬取的数据
class Item{
private String title;//分栏标题
private String itemurl;//分栏url
private Map<String,String> items;//分栏的博文 <标题,链接>
public String getTitle() {
return title;
}
public void setItems(Map<String, String> items) {
this.items = items;
}
public Map<String, String> getItems() {
return items;
}
//创建构造方法
public Item(String title, String itemurl, Map<String, String> items) {
this.title = title;
this.itemurl = itemurl;
this.items = items;
}
//转换成String
@Override
public String toString() {
return "Item{" +
"title='" + title + '\'' +
", itemurl='" + itemurl + '\'' +
", items=" + items +
'}';
}
}
第三步 配置Site
这里也简写了,Site都有默认配置,这里直接使用
@Override
public Site getSite() {
return Site.me();
}
第四步 分析Html
我们把最开始初步的分析Html源码,找到我们想要的结果
@Override
public void process(Page page) {
System.out.println(page.getHtml());
}
然后分析控制台输出的结果
然后我们就可以先对程序进行一次修改,再进一步分析
分析关键字眼
<span class="text">OpenCV学习日记-Java语言描述</span>
和
<a class="clearfix" data-report-click="{&quo...}" href="https://blog.csdn.net/qq_18604209/category_9674847.html">
虽然这个a标签好长,但是还是可以看见href属性
我们对他进行提取
之后对源码进行一次修改
@Override
public void process(Page page) {
// System.out.println(page.getHtml());
Selectable aside = page.getHtml().$("#asideCategory");//选择class为aside-content的
Selectable title = aside.$(".text","text");//提取class=text 的内容
Selectable title_url = aside.$(".clearfix","href");//提取class=clearfix 的href属性
System.out.println(title.all());
System.out.println(title_url.all());
}
输出结果也是我们想要的
[OpenCV学习日记-Java语言描述, OpenCV-Android教程, Java爬虫-Webmagic,...]
[https://blog.csdn.net/qq_18604209/category_9674847.html, https://blog.csdn.net/qq_18604209/category_9676039.html,...]
第五步 对URL进行分类处理
我们可以使用Request进行二次爬取
而process只有一个,所以我们要对不同的url进行不同的处理
@Override
public void process(Page page) {
//如果爬取的页面是首页
if (homeUrl.equals(page.getUrl().toString())){
Selectable aside = page.getHtml().$("#asideCategory");//选择class为aside-content的
Selectable title = aside.$(".text","text");//提取class=text 的内容
Selectable title_url = aside.$(".clearfix","href");//提取class=clearfix 的href属性
List<String> titles = title.all();
List<String> title_urls = title_url.all();
for (int i = 0; i < titles.size(); i++) {
Request req = new Request();
Item item = new Item(titles.get(i),title_urls.get(i),null);
req.putExtra("item",item);
req.setUrl(title_urls.get(i));
page.addTargetRequest(req);
page.addTargetRequest("");
}
}else{//二次爬取的结果
System.out.println(page.getHtml());
// 同上方法进行结果分析
}
}
然后在对二次爬取的结果进行分析
这里就省略分析的过程了
然后从page里获取item
Request req = page.getRequest();
Item item = (Item) req.getExtra("item");
再将博文和博文连接封装成map放到item里面
List<String> titles = title.all();
List<String> title_urls = title_url.all();
Map<String , String> map = new HashMap<>();
for (int i = 0; i < titles.size(); i++) {
map.put(titles.get(i),title_urls.get(i));
}
item.setItems(map);
最后抽取我们想要的内容
page.putField(item.getTitle(),item);
因为默认使用的控制台输出,所以程序运行后
最终控制台输出
get page: https://blog.csdn.net/qq_18604209/category_9676039.html
OpenCV-Android教程: Item{title='OpenCV-Android教程', itemurl='https://blog.csdn.net/qq_18604209/category_9676039.html', items={OpenCV-Android教程-Bitmap与Mat对象 =https://blog.csdn.net/qq_18604209/article/details/104044374, OpenCV-Android教程-引言(先看这里) =https://blog.csdn.net/qq_18604209/article/details/104033121, OpenCV-Android教程-OpenCV Manager 环境搭建 =https://blog.csdn.net/qq_18604209/article/details/104033262, OpenCV-Android教程-不使用 OpenCV Manager 环境搭建 =https://blog.csdn.net/qq_18604209/article/details/104033944}}
get page: https://blog.csdn.net/qq_18604209/category_9703534.html
Java爬虫-Webmagic: Item{title='Java爬虫-Webmagic', itemurl='https://blog.csdn.net/qq_18604209/category_9703534.html', items={[Java爬虫-WebMagic]-03-解析Html源码 =https://blog.csdn.net/qq_18604209/article/details/104213572, [Java爬虫-WebMagic]-02-获取网页源码 =https://blog.csdn.net/qq_18604209/article/details/104208837, [Java爬虫-WebMagic]-01-初识爬虫框架WebMagic =https://blog.csdn.net/qq_18604209/article/details/104208038, [Java爬虫-WebMagic]-04-处理爬取的结果 =https://blog.csdn.net/qq_18604209/article/details/104221544}}
...
一个完整的代码
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Request;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.processor.PageProcessor;
import us.codecraft.webmagic.selector.Selectable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MoreProcessor implements PageProcessor {
@Override
public void process(Page page) {
//如果爬取的页面是首页
if (homeUrl.equals(page.getUrl().toString())){
Selectable aside = page.getHtml().$("#asideCategory");//选择class为aside-content的
Selectable title = aside.$(".text","text");//提取class=text 的内容
Selectable title_url = aside.$(".clearfix","href");//提取class=clearfix 的href属性
List<String> titles = title.all();
List<String> title_urls = title_url.all();
for (int i = 0; i < titles.size(); i++) {
Request req = new Request();
Item item = new Item(titles.get(i),title_urls.get(i),null);
req.putExtra("item",item);
req.setUrl(title_urls.get(i));
page.addTargetRequest(req);
page.addTargetRequest("");
}
}else{//二次爬取的结果
Selectable column = page.getHtml().$("#column").$(".column_article_list");
Selectable title = column.$(".title","text");
Selectable title_url = column.$("a","href");
// 从Request获取item
Request req = page.getRequest();
Item item = (Item) req.getExtra("item");
List<String> titles = title.all();
List<String> title_urls = title_url.all();
Map<String , String> map = new HashMap<>();
for (int i = 0; i < titles.size(); i++) {
map.put(titles.get(i),title_urls.get(i));
}
item.setItems(map);
page.putField(item.getTitle(),item);
}
}
@Override
public Site getSite() {
return Site.me();
}
private static String homeUrl = "https://blog.csdn.net/qq_18604209";
public static void main(String[] args) {
//只需要在这里添加上博客地址就可以了
Spider.create(new MoreProcessor()).addUrl(homeUrl).thread(5).run();
}
}
class Item{
private String title;//分栏标题
private String itemurl;//分栏url
private Map<String,String> items;//分栏的博文 <标题,链接>
//创建构造方法
public Item(String title, String itemurl, Map<String, String> items) {
this.title = title;
this.itemurl = itemurl;
this.items = items;
}
public String getTitle() {
return title;
}
public void setItems(Map<String, String> items) {
this.items = items;
}
public Map<String, String> getItems() {
return items;
}
//转换成String
@Override
public String toString() {
return "Item{" +
"title='" + title + '\'' +
", itemurl='" + itemurl + '\'' +
", items=" + items +
'}';
}
}