1. 程式人生 > >webmagic學習-使用註解編寫爬蟲

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

包含 中國 result reat ets net con 記錄 初始

寫在前面:

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

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

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

  1. 首先定義你需要抽取的數據,並編寫Model類
  2. 在類上寫明@TargetUrl註解,定義對哪些URL進行下載和抽取。
  3. 在類的字段上加上@ExtractBy註解,定義這個字段使用什麽方式進行抽取。
  4. 定義結果的存儲方式。實現PageModelPipeline即可。

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

1、創建maven項目,引入需要的包;這裏是我的pom.xml

<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>

2、上面說了,使用註解方式寫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://http.zhimaruanjian.com/proxylist/(\\d)/)|(http://http.zhimaruanjian.com/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://http.zhimaruanjian.com/")// .thread(3)// .run(); }

//這裏省略了所有字段的getter/setter方法。

}

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

3、是不是覺得控制臺打印的結果太亂了?是不是覺得垂直爬蟲爬取的結構化數據應該保存到文件或者數據庫中呢?好辦,只要實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條記錄。

1 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小時前

4、源碼解讀:

參考: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[]>)方法用戶設置代理連接池;但是設置後從來就沒有成功過:

技術分享

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

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