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[]>)方法用户设置代理链接池;可是设置后历来就没有成功过:

不知道咋回事啊。要上网找一些资料参考一下。

相关文章
相关标签/搜索