多級爬取
有時候我們需要爬取一些多級的資料
例如 我想爬取博客 所有分欄 的 所有博文 這樣的話單純的像之前的爬取方法爬不到
而且分欄是可以改變的 可能作者突然加了個分欄,你的程序就需要做一次修改
爲了解決這種問題,所以可以使用 多次爬取來解決
第一步 配置爬蟲入口
這裏直接使用簡寫方式了
//只需要在這裏添加上博客地址就可以了
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 +
'}';
}
}