webmagic學習-使用註解編寫爬蟲

寫在前面:html

    官方文檔:http://webmagic.io/docs/zh/posts/ch5-annotation/README.htmljava

WebMagic支持使用獨有的註解風格編寫一個爬蟲,引入webmagic-extension包便可使用此功能。web

在註解模式下,使用一個簡單的Model對象加上註解,能夠用極少的代碼量就完成一個爬蟲的編寫。
註解模式的開發方式是這樣的:redis

1. 首先定義你須要抽取的數據,並編寫Model類數據庫

2. 在類上寫明@TargetUrl註解,定義對哪些URL進行下載和抽取。apache

3. 在類的字段上加上@ExtractBy註解,定義這個字段使用什麼方式進行抽取。bash

4. 定義結果的存儲方式。實現PageModelPipeline便可。maven

 下面是我用webmagic的註解方式對芝麻代理(http://http.zhimaruanjian.com/)網站寫的簡單爬蟲;ide

一、建立maven項目,引入須要的包;這裏是個人pom.xmlpost

​
​
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>



<parent>

<artifactId>webmagic-parent</artifactId>

<groupId>us.codecraft</groupId>

<version>0.5.3</version>

</parent>



<groupId>webmagic</groupId>

<artifactId>webmagic-test</artifactId>

<!-- 這個提示讓我remove掉 -->

<version>0.5.3</version>

<packaging>jar</packaging>



<name>webmagic-test</name>

<url>http://maven.apache.org</url>



<properties>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<webmagic.version>0.5.3</webmagic.version>

</properties>



<dependencies>



<dependency>

<groupId>us.codecraft</groupId>

<artifactId>webmagic-core</artifactId>

<version>${webmagic.version}</version>

</dependency>



<!-- 根據上文所說的:引入webmagic-extension包便可使用此功能 -->

<dependency>

<groupId>us.codecraft</groupId>

<artifactId>webmagic-extension</artifactId>

<version>${webmagic.version}</version>

</dependency>



</dependencies>

</project>

​

​

 二、上面說了,使用註解方式寫webmagic爬蟲,須要寫一個Model類。這個Model類裏面有須要pipeline持久化的字段,字段上經過@ExtractBy註解來指定這個字段抓取的規則;在類名上使用@TargetUrl註解,標識哪些url須要解析(至關於原來的us.codecraft.webmagic.processor.PageProcessor.process(Page)方法);最後寫個main方法,並用OOSpider類建立爬蟲程序。(別忘了寫getter/setter方法)

package com.lacerta.ipproxy.OOpageprocess;

import java.util.List;import us.codecraft.webmagic.Site;import us.codecraft.webmagic.model.OOSpider;import us.codecraft.webmagic.model.annotation.ExtractBy;import us.codecraft.webmagic.model.annotation.TargetUrl;import us.codecraft.webmagic.scheduler.RedisScheduler;

@TargetUrl(value = "(http://www.kuaidaili.com/proxylist/(\\d)/)|(http://www.kuaidaili.com/free/inha/(\\d)+/)")

public class IpProxyModel {



@ExtractBy("//td[@data-title='IP']/text()")

List<String> IP;

@ExtractBy("//td[@data-title='PORT']/text()")

List<String> PORT;

@ExtractBy("//td[@data-title='匿名度']/text()")

List<String> 匿名度;

@ExtractBy("//td[@data-title='類型']/text()")

List<String> 類型;

@ExtractBy("//td[@data-title='get/post支持']/text()")

List<String> get_post支持;

@ExtractBy("//td[@data-title='位置']/text()")

List<String> 位置;

@ExtractBy("//td[@data-title='響應速度']/text()")

List<String> 響應速度;

@ExtractBy("//td[@data-title='最後驗證時間']/text()")

List<String> 最後驗證時間;



public static void main(String[] args) {

OOSpider.create(Site.me().setDomain("OOwww.kuaidaili.com"),

new new ConsolePageModelPipeline()//這裏使用的Pipeline是打印到控制檯。

, IpProxyModel.class)

.setScheduler(new RedisScheduler("10.2.1.203"))//使用redis作爲個人scheduler,參數是redis的ip地址

.addUrl("http://www.kuaidaili.com/proxylist/1/")//

.addUrl("http://www.kuaidaili.com/free/")//

.thread(3)//

.run();

}



//這裏省略了全部字段的getter/setter方法。

}

若是不會使用RedisScheduler的童鞋能夠使用默認的QueueScheduler(也就是不寫.setScheduler(new RedisScheduler("10.2.1.203"))這一行就好了)到這裏這個基於註解的webmagic爬蟲就寫好了。點擊F11,讓爬蟲飛一會。。。。能夠在控制檯看到打印的結果。若是相應字段打印結果不正確,就要修改@ExtractBy註解的提取規則了。

三、是否是以爲控制檯打印的結果太亂了?是否是以爲垂直爬蟲爬取的結構化數據應該保存到文件或者數據庫中呢?好辦,只要實us.codecraft.webmagic.pipeline.PageModelPipeline<T>這個藉口就好了。這裏我參考官方自帶的us.codecraft.webmagic.pipeline.FilePageModelPipeline本身寫了一個com.lacerta.ipproxy.OOpageprocess.IpProxyFilePageModelPipeline,這個PageModelPipeline會以我自定義的方式,把爬取的結構化數據保存到文件中。那麼,上代碼:

package com.lacerta.ipproxy.OOpageprocess;

import java.io.BufferedWriter;import java.io.FileWriter;import java.io.IOException;import java.util.List;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import us.codecraft.webmagic.Task;import us.codecraft.webmagic.model.HasKey;import us.codecraft.webmagic.pipeline.PageModelPipeline;import us.codecraft.webmagic.utils.FilePersistentBase;

public class IpProxyFilePageModelPipeline extends FilePersistentBase implements PageModelPipeline<IpProxyModel> {



private Logger logger = LoggerFactory.getLogger(getClass());



/**

* new JsonFilePageModelPipeline with default path "/data/webmagic/"

*/

public IpProxyFilePageModelPipeline() {

setPath("/data/webmagic/");

}



public IpProxyFilePageModelPipeline(String path) {

setPath(path);

}



private boolean flag = true; // 標誌位,若是true就writer.



@Override

public void process(IpProxyModel ipProxyModel, Task task) {

String path = this.path + PATH_SEPERATOR + task.getUUID() + PATH_SEPERATOR;



BufferedWriter writer = null;



try {

String filename;

if (ipProxyModel instanceof HasKey) {

filename = path + ((HasKey) ipProxyModel).key() + ".txt";

} else {

filename = path + "IpProxyFileResult.txt";

}

writer = new BufferedWriter(new FileWriter(getFile(filename), true));

if (flag) {

writer.write("IP\tPORT\t匿名度\t類型\tGet/post支持\t位置\t響應速度\t最後驗證時間\r\n");

flag = false;

}



List<String> ip = ipProxyModel.getIP();

List<String> port = ipProxyModel.getPORT();

List<String> 匿名度 = ipProxyModel.get匿名度();

List<String> 類型 = ipProxyModel.get類型();

List<String> get_post支持 = ipProxyModel.getGet_post支持();

List<String> 位置 = ipProxyModel.get位置();

List<String> 響應速度 = ipProxyModel.get響應速度();

List<String> 最後驗證時間 = ipProxyModel.get最後驗證時間();



if (get_post支持.size() == 0) {

for (int i = 0; i < ip.size(); i++) {

writer.write(ip.get(i) + "\t" + port.get(i) + "\t" + 匿名度.get(i) + "\t" + 類型.get(i) + "\tnull\t"

+ 位置.get(i) + "\t" + 響應速度.get(i) + "\t" + 最後驗證時間.get(i) + "\r\n");

}

} else {

for (int i = 0; i < ip.size(); i++) {

writer.write(ip.get(i) + "\t" + port.get(i) + "\t" + 匿名度.get(i) + "\t" + 類型.get(i) + "\t"

+ get_post支持.get(i) + "\t" + 位置.get(i) + "\t" + 響應速度.get(i) + "\t" + 最後驗證時間.get(i)

+ "\r\n");

}

}

} catch (IOException e) {

logger.warn("write file error", e);

} finally {

if (writer != null) {

try {

writer.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}



}

其實也沒啥,官方提供了us.codecraft.webmagic.pipeline.FilePageModelPipeline,照着抄一個就好了。

最後,看數據吧:(路徑:F:\spider\IpProxyModel\OOwww.kuaidaili.com\IpProxyFileResult.txt)這裏只複製了前40條記錄。

IP PORT 匿名度 類型 Get/post支持 位置 響應速度 最後驗證時間218.20.236.249 8118 高匿名 HTTP, HTTPS GET, POST 中國 廣東省 廣州市 電信 2秒 2分鐘前114.227.62.150 8088 高匿名 HTTP, HTTPS GET, POST 中國 江蘇省 常州市 電信 1秒 2分鐘前183.153.3.96 808 高匿名 HTTP, HTTPS GET, POST 中國 浙江省 台州市 電信 1秒 2分鐘前117.86.12.191 808 高匿名 HTTP GET, POST 中國 江蘇省 南通市 電信 3秒 2分鐘前116.17.136.186 9797 透明 HTTP, HTTPS GET, POST 中國 廣東省 惠州市 電信 1秒 1分鐘前59.78.17.198 1080 高匿名 HTTP, HTTPS GET, POST 中國 上海市 上海市 教育網 2秒 4分鐘前121.35.130.117 9797 透明 HTTP, HTTPS GET, POST 中國 廣東省 深圳市 電信 2秒 7分鐘前183.141.107.54 3128 高匿名 HTTP, HTTPS GET, POST 中國 浙江省 嘉興市 電信 3秒 10分鐘前115.29.37.86 8088 高匿名 HTTP GET, POST 中國 山東省 青島市 阿里雲 2秒 13分鐘前59.66.166.51 8123 高匿名 HTTP, HTTPS GET, POST 中國 北京市 北京市 教育網 1秒 16分鐘前27.46.50.22 8888 透明 HTTP, HTTPS GET, POST 中國 廣東省 深圳市 聯通 3秒 19分鐘前60.191.164.83 3128 透明 HTTP GET, POST 中國 浙江省 台州市 電信 0.4秒 23分鐘前110.73.40.246 8123 高匿名 HTTP, HTTPS GET, POST 廣西壯族自治區南寧市 聯通 3秒 26分鐘前119.86.48.30 8998 高匿名 HTTP, HTTPS GET, POST 中國 重慶市 重慶市 電信 1秒 28分鐘前101.6.52.199 8123 高匿名 HTTP GET, POST 中國 北京市 北京市 教育網 2秒 31分鐘前112.92.218.221 9797 透明 HTTP, HTTPS GET, POST 中國 廣東省 中山市 聯通 3秒 34分鐘前119.57.112.130 8080 透明 HTTP, HTTPS GET, POST 中國 北京市 北京市 3秒 38分鐘前114.250.48.111 9000 透明 HTTP, HTTPS GET, POST 中國 北京市 北京市 聯通 2秒 40分鐘前119.122.212.36 9000 透明 HTTP, HTTPS GET, POST 中國 廣東省 深圳市 電信 2秒 44分鐘前113.245.57.201 8118 高匿名 HTTP, HTTPS GET, POST 中國 湖南省 株洲市 電信 1秒 46分鐘前115.200.164.8 8998 高匿名 HTTP, HTTPS GET, POST 中國 浙江省 杭州市 電信 1秒 49分鐘前14.112.208.155 9999 透明 HTTP, HTTPS GET, POST 中國 廣東省 惠州市 電信 2秒 53分鐘前113.110.208.124 9000 透明 HTTP, HTTPS GET, POST 中國 廣東省 深圳市 電信 1秒 55分鐘前182.37.126.246 808 高匿名 HTTP, HTTPS GET, POST 中國 山東省 日照市 電信 0.4秒 59分鐘前123.127.8.248 80 高匿名 HTTP, HTTPS GET, POST 中國 北京市 北京市 聯通 2秒 1小時前122.96.59.106 82 高匿名 HTTP GET, POST 江蘇省南京市 聯通 1秒 1小時前111.13.7.42 82 高匿名 HTTP GET, POST 中國 北京市 北京市 移動 2秒 1小時前219.216.122.250 8998 高匿名 HTTP, HTTPS GET, POST 中國 遼寧省 瀋陽市 教育網 2秒 1小時前117.23.248.234 8118 高匿名 HTTP GET, POST 中國 陝西省 寶雞市 電信 2秒 1小時前171.38.78.27 8123 高匿名 HTTP, HTTPS GET, POST 廣西壯族自治區玉林市 聯通 1秒 1小時前171.110.218.210 9000 透明 HTTP, HTTPS GET, POST 中國 廣西壯族自治區 來賓市 電信 2秒 1小時前183.141.154.125 3128 高匿名 HTTP, HTTPS GET, POST 中國 浙江省 嘉興市 電信 2秒 1小時前171.11.186.176 8118 高匿名 HTTP GET, POST 中國 河南省 商丘市 電信 2秒 1小時前124.133.154.83 8090 匿名 HTTP GET, POST 中國 山東省 濟南市 聯通 2秒 1小時前118.81.251.60 9797 透明 HTTP, HTTPS GET, POST 中國 山西省 太原市 聯通 0.8秒 1小時前115.229.99.21 808 高匿名 HTTP, HTTPS GET, POST 中國 浙江省 嘉興市 電信 2秒 1小時前124.193.7.247 3128 透明 HTTP GET, POST 北京市 鵬博士寬帶 2秒 1小時前125.33.253.87 9797 透明 HTTP, HTTPS GET, POST 中國 北京市 北京市 聯通 0.7秒 1小時前221.227.131.147 8000 高匿名 HTTP GET, POST 中國 江蘇省 南通市 電信 3秒 1小時前

四、源碼解讀:

參考:http://m.blog.csdn.net/article/details?id=51971708

OOSpider這個類繼承Spider,可是對於四大組件中的PageProcesser作了更改

Pipeline須要繼承PageModelPipeline,OOSpider成員變量有個ModelPipeline,ModelPipeline首先執行,而後調用用戶本身實現的PageModelPipeline。

· 初始化一個OOSpider:   

 public OOSpider(Site site, PageModelPipeline pageModelPipeline, Class... pageModels) {

        this(ModelPageProcessor.create(site, pageModels));

        this.modelPipeline = new ModelPipeline();

        super.addPipeline(modelPipeline);

        for (Class pageModel : pageModels) {

            if (pageModelPipeline != null) {

                this.modelPipeline.put(pageModel, pageModelPipeline);

            }

            pageModelClasses.add(pageModel);

        }

    }

· ModelPageProcessor,這個類繼承PageProcessor,因此在主流程中將會執行對Page的解析工做(若是對主流程不熟悉那就先看看個人第一篇博客), 具體的解析工做是由PageModelExtractor執行,每一個包含註解的class都會對應一個PageModelExtractor。

 

 

 

 

 

 

 

 

 

 

寫在後面:

    今天其實想作爬蟲的ip代理,可是昨天晚上回家路上沒事時把官方文檔的這一章看完了,正好練習一下。

    再說爬蟲iP代理:

        先說us.codecraft.webmagic.Site.setCycleRetryTimes(int)方法:這個方法在當前url download失敗後,會添加到scheduler最後,int參數,就是這樣重複的次數。

us.codecraft.webmagic.Site.setHttpProxyPool(List<String[]>)方法用戶設置代理鏈接池;可是設置後歷來就沒有成功過:

不知道咋回事啊。要上網找一些資料參考一下。

相關文章
相關標籤/搜索