使用AiPa爬虫框架同时爬取多个网页信息

上一篇介绍了用Java+Jsoup实现简单的网页爬虫功能,这次我们要做的稍微深一点,同时爬取多个新闻网站,并将其中有用的信息(新闻标题,URL,新闻内容等)保存在数据库中。首先介绍一个很好用的多线程爬虫框架,名字叫AiPa。

AiPa爬虫框架

Aipa是一款小巧,灵活,扩展性高的多线程爬虫框架。
AiPa依赖当下最简单的HTML解析器Jsoup。
AiPa只需要使用者提供网址集合,即可在多线程下自动爬取,并对一些异常进行处理。

Maven

直接引入

<dependency>
    <groupId>cn.yueshutong</groupId>
    <artifactId>AiPa</artifactId>
    <version>1.0.0.RELEASE</version>
</dependency>

使用

1.必须实现的接口

public class MyAiPaWorker implements AiPaWorker {

    @Override
    public String run(Document doc, AiPaUtil util) {
        //使用JSOUP进行HTML解析获取想要的div节点和属性
        //保存在数据库或本地文件中
        //新增aiPaUtil工具类可以再次请求网址
        return doc.title() + doc.body().text();
    }

    @Override
    public Boolean fail(String link) {
        //任务执行失败
        //可以记录失败网址
        //记录日志
        return false;
    }
}

2.Main 方法

    public static void main(String[] args) throws InstantiationException, IllegalAccessException, ExecutionException, InterruptedException {
        //准备网址集合
        List<String> linkList = new ArrayList<>();
        linkList.add("http://jb39.com/jibing/FeiQiZhong265988.htm");
        linkList.add("http://jb39.com/jibing/XiaoErGuoDu262953.htm");
        linkList.add("http://jb39.com/jibing/XinShengErShiFei250995.htm");
        linkList.add("http://jb39.com/jibing/GaoYuanFeiShuiZhong260310.htm");
        linkList.add("http://jb39.com/zhengzhuang/LuoYin337449.htm");
        //第一步:新建AiPa实例
        AiPaExecutor aiPaExecutor = AiPa.newInstance(new MyAiPaWorker()).setCharset(Charset.forName("GBK"));
        //第二步:提交任务
        for (int i = 0; i < 10; i++) {
            aiPaExecutor.submit(linkList);
        }
        //第三步:读取返回值
        List<Future> futureList = aiPaExecutor.getFutureList();
        for (int i = 0; i < futureList.size(); i++) {
            //get() 方法会阻塞当前线程直到获取返回值
            System.out.println(futureList.get(i).get());
        }
        //第四步:关闭线程池
        aiPaExecutor.shutdown();
    }

3. AiPaWorker接口
AiPaWorker接口是用户必须要实现的业务类。
该接口的方法如下:

public interface AiPaWorker<T,S> {
    /**
     * 如何解析爬下来的HTML文档?
     * @param doc JSOUP提供的文档
     * @param util 爬虫工具类
     * @return
     */
    T run(Document doc, AiPaUtil util);

    /**
     * run方法异常则执行fail方法
     * @param link 网址
     * @return
     */
    S fail(String link);
}

注意,接口中run方法的参数doc,即Jsoup通过连接网页URL获得的docment,可以直接使用。
在run方法中可以通过Jsoup方法爬取所想要的数据,然后存入数据库中,注意是在run方法内执行
数据库存取操作。

也就是,run()方法是用户自定义处理爬取的HTML内容,一般是利用Jsoup的Document类进行解析,
获取节点或属性等,然后保存到数据库或本地文件中。如果在业务方法需要再次请求URL,可以使用
工具类Util。比如访问新闻具体页面的时候。
fail()方法是当run方法出现异常或爬取网页时异常,多次处理无效的情况下进入的方法,该方法的参数
为此次出错的网址。一般是对其进行日志记录等操作。

以上是AiPa框架的基本介绍,下面开始做一个简单的爬取多个新闻网站上的新闻的例子。
网页配置信息都存放在数据库中,第一步要从数据库中取要爬取的网站的各种配置信息。

		//取数据库内配置列表
		List<ArticleCrawler> articleCrawler= this.articleCrawlerDomain.getAllArticle();	
		
		List<String> linkList = new ArrayList<>();
		for (int i = 0; i < articleCrawler.size(); i++) {
			String url = articleCrawler.get(i).getUrl();//获取网页Url
			String ulClass = articleCrawler.get(i).getUlClass();//获取ul标签样式
			String articleName = articleCrawler.get(i).getTname();//获取网页新闻名称
			String contentClass = articleCrawler.get(i).getContentClass();//获取网页内容样式
			String authorClass = articleCrawler.get(i).getAuthorClass();//获取新闻作者样式
			String webCode = articleCrawler.get(i).getWebCode();//获取网页编码格式
			String tableClass = articleCrawler.get(i).getTableClass();//获取表格样式
			String listClass = articleCrawler.get(i).getListClass();//获取li标签样式
			String divClass = articleCrawler.get(i).getDivClass();//获取div标签样式
			String websiteUrl = articleCrawler.get(i).getWebsiteUrl();//获取网站官网URL
			linkList.add(url);//设置网址集合

取完配置信息之后,新建一个AiPa框架,在run方法里利用Jsoup的Document类进行解析,获取节点和属性,然后
调方法存入本地数据库内。

//新建AiPa框架类
			AiPaExecutor executor = AiPa.newInstance(new AiPaWorker(){
				
	            @Override
	            public Boolean run(Document document, AiPaUtil util) {
	                List contentList = new ArrayList<>();
	                List articleList = new ArrayList<>();
	                
	                //如果新闻格式为无序列表且ul上有样式,格式为<ul class=""><li></li></ul>
	                if(ulClass!=null){
		                Elements ulLinks = document.getElementsByClass(ulClass);//根据ul的样式获取ul
		                for (Element ulLink : ulLinks) {
		                	Elements liLinks = ulLink.getElementsByTag("li");//获取ul下的li
		        			for (Element liLink : liLinks){
		        				Elements aLinks = liLink.select("a");//获取li下的a标签
		        				for (Element aLink : aLinks){
		        					String title = aLink.text();//获取新闻标题
		        					String titleUrl = aLink.attr("href");//获取新闻链接
		        					List articleExist = articleDomain.queryByProperty("inforsource", titleUrl);
		    	        			try {
		    	        				if(title.length()>4 && titleUrl != "javascript:void(0);" && (articleExist == null || articleExist.size()== 0)){
			    							Article article = (Article) articleDomain.getBaseObject();
			    							article.setTname(title);
			    							article.setInforsource(titleUrl);
			    							article.setValid(false);
			    							article.setColumnName(articleName);
			    							article.setCreateId("admin");
			    							contentList.add(titleUrl);
			    							articleList.add(article);
		    	        				}
		    						} catch (Exception e) {
		    							e.printStackTrace();
		    						}
		        				}
		        			}
						}
		                articleDomain.deleteAndSaveAndUpdate(null, articleList, null);
	                }
	                
	                //如果新闻格式为无序列表且ul上有样式,格式为<div><ul><li></li></ul></div>
	                if (ulClass == null && listClass !=null ) {
	                	Elements listEle = document.getElementsByClass(listClass);
	                	for(Element list : listEle){
	                		Elements ulEle = list.getElementsByTag("ul");
	                		for(Element ulLink :ulEle){
			                	Elements liLinks = ulLink.getElementsByTag("li");//获取ul下的li
			        			for (Element liLink : liLinks){
			        				Elements aLinks = liLink.select("a");//获取li下的a标签
			        				for (Element aLink : aLinks){
			        					String title = aLink.text();//获取新闻标题
			        					String titleUrl = aLink.attr("href");//获取新闻链接
			        					List articleExist = articleDomain.queryByProperty("inforsource", titleUrl);
			    	        			try {
			    	        				if(title.length()>4 && titleUrl != "javascript:void(0);" && (articleExist == null || articleExist.size()== 0)){
				    							Article article = (Article) articleDomain.getBaseObject();
				    							article.setTname(title);
				    							article.setInforsource(titleUrl);
				    							article.setValid(false);
				    							article.setColumnName(articleName);
				    							article.setCreateId("admin");
				    							contentList.add(titleUrl);
				    							articleList.add(article);
			    	        				}
			    						} catch (Exception e) {
			    							e.printStackTrace();
			    						}
			        				}
			        			}	                			
	                		}
	                	}
	                	articleDomain.deleteAndSaveAndUpdate(null, articleList, null);	
	                	                	
	                }
	                
	                //如果新闻格式没有列表,为<div><a></a><div>
	                if(ulClass == null && divClass != null){
	                	Elements divEle = document.getElementsByClass(divClass);
	                	for(Element div : divEle){
	                		Elements aEle = div.select("a");//取div下的a标签
	                		for(Element aLink : aEle){
	        					String title = aLink.text();//获取新闻标题
	        					String titleUrl = aLink.attr("href");//获取新闻链接
	        					List articleExist = articleDomain.queryByProperty("inforsource", titleUrl);
	    	        			try {
	    	        				if(title.length()>4 && titleUrl != "javascript:void(0);" && (articleExist == null || articleExist.size()== 0)){
		    							Article article = (Article) articleDomain.getBaseObject();
		    							article.setTname(title);
		    							article.setInforsource(titleUrl);
		    							article.setValid(false);
		    							article.setColumnName(articleName);
		    							article.setCreateId("admin");
		    							contentList.add(titleUrl);
		    							articleList.add(article);
	    	        				}
	    						} catch (Exception e) {
	    							e.printStackTrace();
	    						}
	                		}
	                	}
	                	articleDomain.deleteAndSaveAndUpdate(null, articleList, null);	
	                }
	                            
	                
	                //获取新闻详细内容信息
	                for (int j = 0; j < contentList.size(); j++) {
						String articleUrl = (String) contentList.get(j);//获取新闻详细内容链接
						//验证新闻URL是否有效
						Boolean testUrl = testUrl(articleUrl, 2000);
						if(testUrl == false){
							String substring = articleUrl.substring(0, 1);
							if(".".equals(substring)){
								articleUrl =  articleUrl.replace("./", url);	
							}else{
								articleUrl = websiteUrl + articleUrl;
							}
						}
						try {
							Document contentDoc = util.getHtmlDocument(articleUrl);//根据链接获取详细页面Document
							Elements contentEle = contentDoc.getElementsByClass(contentClass);
							String author = "";
							if(authorClass!=null){
								Elements authorEle = contentDoc.getElementsByClass(authorClass);	
								author = authorEle.text();//获取作者信息
							}
							
							String contentHtml = contentEle.html();//获取详细内容的HTML
							String content = Jsoup.clean(contentHtml, Whitelist.basicWithImages());//Jsoup对网页HTML进行过滤,筛选标签内容
							
							List articleExist = articleDomain.queryByProperty("inforsource", (String) contentList.get(j));
							for (int k = 0; k < articleExist.size(); k++) {
								Article article = (Article) articleExist.get(k);
								String id = article.getId();
								String clobInfo = clobInfoDomain.query(id);
	        					if (!contentHtml.equals("") && (clobInfo == null || clobInfo.length() <= 0 )) {
	        						clobInfoDomain.save(id, content);//将新闻具体内容添加到clob里
	        					}    								
								article.setCreater(author);
								articleDomain.update(article);
							}
							
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
	                
	                return true;
	            }

	            
	            @Override
	            public Boolean fail(String s) {   
	                return false;
	            }
			}).setCharset(Charset.forName(webCode));
			executor.submit(linkList);
	        executor.shutdown();

数据库内可以存放多条网页信息,最后爬下来的数据都存入到了数据库内。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值