网络爬虫框架:Webmagic(含案例代码)

一.概述

在这里插入图片描述
四大组件

  • Downloader
    Downloader负责从互联网上下载页面,以便后续处理。WebMagic默认使用了 ApacheHttpClient作为下载工具

  • PageProcessor

    • PageProcessor负责解析页面,抽取有用信息,以及发现新的链接。WebMagic使用Jsoup 作为HTML解析工具,并基于其开发了解析XPath的工具Xsoup。
    • 在这四个组件中,PageProcessor对于每个站点每个页面都不一样,是需要使用者定制的部分。
  • Pipeline
    Pipeline负责抽取结果的处理,包括计算、持久化到文件、数据库等。WebMagic默认提供 了“输出到控制台”和“保存到文件”两种结果处理方案。

  • Scheduler
    Scheduler负责管理待抓取的URL,以及一些去重的工作。WebMagic默认提供了JDK的内 存队列来管理URL,并用集合来进行去重。也支持使用Redis进行分布式管理。

二.各个组件的使用

1.依赖

		<dependency>
            <groupId>us.codecraft</groupId>
            <artifactId>webmagic-core</artifactId>
            <version>0.7.3</version>
        </dependency>
        <dependency>
            <groupId>us.codecraft</groupId>
            <artifactId>webmagic-extension</artifactId>
            <version>0.7.3</version>
        </dependency>
        
        <!--要用redis去保存已经爬取过的链接-->
        <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>

2.PageProcessor

  1. 新建一个类 implements PageProcessor
  2. 重写:process()、getSite()
public class MyProcessor implements PageProcessor { 
	public void process(Page page) { 
		//对爬取到的页面进行的操作
	}
	
	public Site getSite() { 
		//在爬取页面时对http请求的一些设置 例如编码、HTTP头、超时时间、重试策略 等、代理等
		return Site.me().setSleepTime(100).setRetryTimes(3); 
	}
}
  • Page的常用操作

    • 将当前页面 里的所有链接都添加到目标页面中
    page.addTargetRequests( page.getHtml().links().all() );
    
    • 对下面要爬取的链接进行正则表达式的限制
    page.addTargetRequests( page.getHtml().links().regex("https://blog.csdn.net/[a‐z 0‐9 ‐]+/article/details/[0‐9]{8}").all() );
    
    • 获得爬取到的整个html页面代码
    page.getHtml().toString()
    
    • 对获取的整个html页面只取得想要部分的标签里的内容
    page.getHtml().xpath("//* [@id=\"nav\"]/div/div/ul/li[5]/a").toString();
    
    • 获取标签里的属性使用‘@’(比如获取<a herf="" 的herf)
    page.getHtml().xpath("/html/body/div/div[4]/div[3]/h2[1]/a/@href")
    
    • 把取到的内容保存下来 让Pipeline可以取出 然后保存的数据库或者打印
    page.putField("title",page.getHtml().xpath("//* [@id=\"mainBox\"]/main/div[1]/div[1]/h1/text()").toString())
    
  • Site的可选设置

    • setCharset(String):设置编码
    • setUserAgent(String):设置UserAgent
    • setTimeOut(int):设置超时时间,单位是毫秒
    • setRetryTimes(int):设置重试次数
    • setCycleRetryTimes(int):设置循环重试次数
    • addCookie(String,String):添加一条cookie
    • setDomain(String):设置域名,需设置域名 后,addCookie才可生效
    • addHeader(String,String): 添加一条 addHeader
    • setHttpProxy(HttpHost):设置Http代理
  • 注意

    • 在process() 方法中一般就是:对下面要爬取的链接进行正则表达式的限制、将要要用的字段保存下来 让Pipeline可以处理
    • 在使用page.putField()存入数据之前要先判断在html中取得的数据是否为空
    String title= page.getHtml().xpath("//* [@id=\"mainBox\"]/main/div[1]/div[1]/h1/text()").get(); //页面标题
    String content= page.getHtml().xpath("//* [@id=\"article_content\"]/div/div[1]").get(); //页面内容
    
    if(title!=null && content!=null){ 
     	//如果有标题和内容 
     	page.putField("title",title); 
     	page.putField("content",content); 
    }else{
     	page.setSkip(true);//跳过 
    }
    
    • 在获取XPath的时候 可用通过右键要用的部分 查看该部分网页源代码 然后在该部分html代码再右键选择复制xpath
      在这里插入图片描述

3.Pipeline

  1. 常用的自带的有:

    1. ConsolePipeline:控制台输出
    2. FilePipeline:文件保存
    3. JsonFilePipeline:以json方式文件保存
  2. 定制Pipline(最常用)

    import us.codecraft.webmagic.ResultItems;
    import us.codecraft.webmagic.Task; 
    import us.codecraft.webmagic.pipeline.Pipeline; 
    
    public class MyPipeline implements Pipeline { 
    
    	public void process(ResultItems resultItems, Task task) { 
    		//获得刚才page.putField() 放入的值
    		String title=resultItems.get("title"); 	
    		System.out.println("我的定制的 title:"+title); 
    
    		/*
    		 * 一般都会在这里放入数据库中持久化
    		 */
    	} 
    }
    
  • 注意
    • 必须要在page. putField()时 给要放入的值调用tostring()方法 不然到时无法转化为string类型

4 Scheduler

  1. 使用Scheduler的目的是防止重复爬取同一链接 所以需要把爬取过的链接保存下来

  2. 三种实现方式

    1. QueueScheduler:内存队列(保存在内存中 不常用)
    2. FileCacheQueueScheduler :文件队列(保存在本机的文件里 只适合单机 不适合分布式爬虫)
    3. RedisScheduler:Redis队列(生产环境最常用)

5.Spider

  • Spider是爬虫启动的入口

    • create(PageProcessor):创建Spider
    • addUrl(String…):添加初始的URL
    • thread(n):开启n个线程
    • run():启动 会阻塞当前线程执行
    • start()/runAsync():异步启动 当前线程继续执行
    • stop():停止爬虫
    • addPipeline(Pipeline):添加一个 Pipeline 一个Spider可以有多个Pipeline
    • setScheduler(Scheduler):设置Scheduler 一 个Spider只能 有个一个Scheduler
    • setDownloader(Downloader):设置Downloader 一个Spider只能有个一个Downloader
    • get(String):同步调用 并直接取得结果
    • getAll(String…):同步调用 并直接取得一堆结果
    public static void main(String[] args) { 
    	Spider.create( new MyProcessor() ) 
    		.addUrl("https://blog.csdn.net") 
      		.addPipeline(new ConsolePipeline())									//把已经爬过的链接存入内存 
        	.addPipeline(new FilePipeline("e:/data"))							//以一般格式存入文件已经爬过的链接 
       		.addPipeline(new JsonFilePipeline("e:/json"))						//以json格式存入文件已经爬过的链接 
       		.addPipeline(new MyPipeline())										//定制化输出(可以写入数据库、文件或者打印等等) 
    		//.setScheduler(new QueueScheduler())								//设置内存队列 
    		//.setScheduler(new FileCacheQueueScheduler("E:\\scheduler"))		//设置文件队列 
    		.setScheduler(new RedisScheduler("127.0.0.1"))						//设置Redis队列(只能有一个Scheduler)
    		.run(); 
    }
    

6.如果要爬取得内容是图片

  1. 怎么获得便签中的属性值
    //获取到<img>变迁的上一级标签就停止 然后再用.css获取image标签的属性
    String image= page.getHtml().xpath("//* [@id=\"asideProfile\"]/div[1]/div[1]/a").css("img","src").toString();
    
  2. 爬到链接后下载图片的工具类
    import java.io.*; 
    import java.net.URL; 
    import java.net.URLConnection; 
    
    /**
    * 下载工具类 
    */ 
    public class DownloadUtil { 
    	
    	public static void download(String urlStr,String filename,String savePath) throws IOException { 
    		URL url = new URL(urlStr); 
    		//打开url连接 
    		URLConnection connection = url.openConnection(); 
    		//请求超时时间 
    		connection.setConnectTimeout(5000); 
    		//输入流 
    		InputStream in = connection.getInputStream(); 
    		//缓冲数据 
    		byte [] bytes = new byte[1024]; 
    		//数据长度 
    		int len;
    		//文件 
    		File file = new File(savePath); 
    		if(!file.exists()){
    			file.mkdirs(); 
    		}
    		OutputStream out = new FileOutputStream(file.getPath()+"\\"+filename); 
    		//先读到bytes中 
    		while ((len=in.read(bytes))!=‐1){ 
    			//再从bytes中写入文件 
    			out.write(bytes,0,len); 
    		}
    		//关闭IO 
    		out.close(); in.close(); 
    	}
     }
    
  3. Pipeline怎么下载使用
    @Override 
    public void process(ResultItems resultItems, Task task) { 
    	String image = resultItems.get("image");//图片地址 
    	String fileName = image.substring(image.lastIndexOf("/")+1); 
    	//下载图片 
    	try {
    		DownloadUtil.download(image,fileName,"e:/userimg"); 
    	} catch (IOException e) { 
    		e.printStackTrace(); 
    	} 
    	//之后图片已经下载到本地 就可以把该文件路径保存起来了
    }
    

注意:爬取一般都会有定时任务每天去定时爬取

三.案例(爬取csdn的用户名和头像)

1.配置启动类

@SpringBootApplication 
@EnableScheduling 
public class UserReptileApplication { 

	//读取在配置文件自己配置的redis地址
	@Value("${spring.redis.host}") 
	private String redis_host; 

	public static void main(String[] args) { 
		SpringApplication.run(UserCrawlerApplication.class, args); ''
	}
	
	@Bean 
	public RedisScheduler redisScheduler(){ 
		return new RedisScheduler(redis_host); 
	} 
}

2.爬取类

import org.springframework.stereotype.Component; 
import us.codecraft.webmagic.Page; 
import us.codecraft.webmagic.Site; 
import us.codecraft.webmagic.processor.PageProcessor; 

/**
* 爬取类 
**/ 
@Component 
public class UserProcessor implements PageProcessor { 

	@Override 
	public void process(Page page) { 
		page.addTargetRequests(page.getHtml().links().regex("https://blog.csdn.net/ [a‐z 0‐9 ‐]+/article/details/[0‐9]{8}").all()); 
		/**
		 * /text():取标签中间的值 不然获取的结果是<>值</> 
		 * /@属性名:取标签里的属性值 比如取<a href="">的href
		 */
		String nickname= page.getHtml().xpath("//* [@id=\"uid\"]/text()").get(); 
		String image= page.getHtml().xpath("//* [@id=\"asideProfile\"]/div[1]/div[1]/a/img/@src").toString(); 	
		if(nickname!=null && image!=null){ 
			//如果有昵称和头像 
			page.putField("nickname",nickname); 
			page.putField("image",image); 
		}else{
			page.setSkip(true);//跳过 
		} 
	}

	@Override 
	public Site getSite() { 
		return Site.me().setRetryTimes(3000).setSleepTime(100); 
	} 
}

3.入库类

import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.stereotype.Component; 
import us.codecraft.webmagic.ResultItems; 
import us.codecraft.webmagic.Task; 
import us.codecraft.webmagic.pipeline.Pipeline; 
import util.DownloadUtil; //上面的文件下载工具类
import java.io.IOException; 

@Component 
public class UserPipeline implements Pipeline { 

	@Autowired //无法注入
	//自己封装的一个将爬取到用户名和图片保存起来的实体类 这个是该实体类的dao层用来存储实体类
	private UserDao userDao; 
	
	private static UserPipeline  userPipeline ;
    @PostConstruct //通过@PostConstruct实现初始化bean之前进行的操作
    public void init() {
        userPipeline = this;
        /**
         * 这个类必须用@Component注入spring
         * 初使化时将已静态化的userDao实例化
		 * 使用方式 userDao的方式:userPipeline.userDao
		 */
        userPipeline .userDao= this.userDao;
     }

	@Override 
	public void process(ResultItems resultItems, Task task) { 
		User user=new User(); 
		user.setNickname(resultItems.get("nickname")); 
		String image = resultItems.get("image");//图片地址 
		String fileName = image.substring(image.lastIndexOf("/")+1); 
		try {
		 	//下载图片 
			DownloadUtil.download(image,fileName,"e:/userimg"); 
		} catch (IOException e) {
			 e.printStackTrace(); 
		} 
		user.setImage(fileName); 
		userPipeline.userDao.save(user);
	} 
}

4.任务类

@Component 
public class UserTask { 

	@Autowired 
	private RedisScheduler redisScheduler; //启动类配置的RedisScheduler 
	
	@Autowired 
	private UserPipeline userPipeline; //自定的Pipeline
	
	@Autowired 
	private UserProcessor userProcessor;//实现的PageProcessor的类
	
	//springboot定时任务还要给启动类注解 @EnableScheduling
	@Scheduled(cron="0 0 0 * * ?") //每天的0点执行
	public void userTask(){ 
		System.out.println("爬取用户"); 
		Spider spider = Spider.create(userProcessor); 
		spider.addUrl("https://blog.csdn.net"); 
		spider.addPipeline(userPipeline); 
		spider.setScheduler(redisScheduler); 
		//异步爬取
		spider.start(); 
	}
}
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值