java 编写网页爬虫程序(利用HttpClient+jsoup)

/ 2014-10-19
  1. 抓取网页的实现的原理和技术

    -- 前言

    对于网页抓取这方面大家都知道百度和google 都是依靠这个起家的. 百度为广大的网名提供了很多便捷的地方.让大家能最快的找到自己的想要的东西.百度做的事情就是聚合互联网的信息资源(当然百度也有自己的信息平台,如百度文库,知道,贴吧这类的产品).那百度是怎样的抓取网站的呢.一般情况百度会有自己的链接库. 根据部分网站友链,外链.或者是网站管理员向百度提交网址的方式抓取网站A的内容. 百度的会根据网站A的情况派出蜘蛛(也有称爬虫的)去抓取网站A的内容.蜘蛛根据情况抓取网站A内容.百度根据自己的算法去组织抓取的内容. 然后你就能在百度中搜索到网站A的内容了.当然有时候可能不会第一页的. 这个就是百度核心了. 如何让用户第一时间找到自己想要的内容.

    --什么网页爬虫

    顾名思义就是专业的网站数据收集者. 通常会是一个具有搜索引擎的UA的浏览器程序.

    当然会和我们日常生活中使用有有些差异. 例如它不会去触发一个表单注册的请求.如果网页A需要登陆后才能访问的话.搜索引擎当然也是无法去登陆的(它根本没有登陆账号呀).它更多的是关心上用户能看到的内容(图片,文字等).它分析网站的链接.根据链接和网站的实际情况使用对应的抓取策略.

    --什么是恶意爬虫

    当然不是所有的爬虫都是友善的.有些网站为了生存或者是其它原因会抓取其它网站的内容作为自己网站内容. 这样就是导致有时候在人家不愿意被搜索引擎发现的内容也会导致在其它被收录了.或者是给用户对信息的鉴别带来困扰. 有时候翻了几页下来都发现是一样的内容. 对于这样的情况也是挺烦的啦. 还有就是有些是人家的辛辛苦苦写的内容也被不经作者的许可就转载的情况.还有就是有些恶意的爬虫会导致正常用户无法访问的情况.




    一个简单爬虫要做那些事


    抓取网页.分析网页数据.找自己想要的数据.根据网页中的链接继续抓取网页.

  2. JAVA 做爬虫需要那些技术和基本实现


    当然你需要一个浏览器(HttpClient)一个分析网页数据的(Jsoup) 好了有这两个工具后就可以做你想做的事情了.


    上代码

    package com.fzb.common.util;
    
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.util.List;
    import java.util.Set;
    import java.util.concurrent.ConcurrentSkipListSet;
    
    import jodd.io.FileUtil;
    
    import org.apache.http.Header;
    import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.log4j.Logger;
    import org.jsoup.Jsoup;
    import org.jsoup.nodes.DataNode;
    import org.jsoup.nodes.Document;
    import org.jsoup.nodes.Node;
    
    import com.fzb.common.io.CompressUtil;
    
    public class FetchWebPage{
    
    	private static Logger log=Logger.getLogger(FetchWebPage.class);
    	private String recordStr;
    	private OutputStream socketOut;
    	private boolean userOne=true ;
    	private CloseableHttpClient  httpclient;
    	private File file;
    	public FetchWebPage(String index,String key,String savePath) {
    		this.index=index;
    		this.key=key;
    		this.savePath=savePath+"/"+key+"/";
    	}
    	
    	public FetchWebPage(String index,String key,String savePath,String cookie) {
    		this.index=index;
    		this.key=key;
    		this.savePath=savePath+"/"+key+"/";
    		this.cookie=cookie;
    		 
    	}
    	
    	public FetchWebPage(String index,String key,String savePath,String cookie,OutputStream out) {
    		this.index=index;
    		this.key=key;
    		this.savePath=savePath+"/"+key+"/";
    		this.cookie=cookie;
    		this.socketOut=out;
    
    
    	}
    
    	public String goWithInZip(){
    		go();
    		// inZip
    		String zipFile= "e:/temp/"+ParseTools.getRandomFileName("zip");
    		CompressUtil.zip(file.toString(),zipFile, true, null);
    		return zipFile;
    	
    	}
    	
    	public void go() {
    		try {
    			file=new File(savePath);
    			FilesManageUtil.delFiler(file.toString());
    			file.mkdirs();
    			
    			long start=System.currentTimeMillis();
    			String htmlStr = getResponseBody(index);
    			Document doc=Jsoup.parse(htmlStr);
    			List<Node> nodes=doc.childNodes();
    			findHref(nodes,index);
    			aHref.remove(index);
    			bHref.add(index);
    			while(aHref.size()!=0){
    				for (String url : aHref) {
    					aHref.remove(url);
    					if(bHref.contains(url)){
    						continue;
    					}
    					try{
    						htmlStr=getResponseBody(url);
    					}
    					catch (Exception e) {
    						
    					}
    					 
    					bHref.add(url);
    					if(htmlStr!=null){
    						Document tdoc=Jsoup.parse(htmlStr);
    						List<Node> tnodes=tdoc.childNodes();
    						findHref(tnodes,url);
    					}
    				}
    			}
    			
    			recordStr+="总耗时  "+(System.currentTimeMillis()-start);
    			System.out.println(recordStr);
    		} catch (ClientProtocolException e1) {
    			e1.printStackTrace();
    		} catch (Exception e1) {
    			e1.printStackTrace();
    		}
    		
    	}
    	
    	public String getResponseBody(String url) throws ClientProtocolException, Exception{
    		 
    		log.info("get page " +url);
    		long start=System.currentTimeMillis();
    		String fileStr=url;
    		fileStr=fileStr.replace("http://", "");
    		//fileStr=fileStr.replace(":8080", "");
    		fileStr=fileStr.replace(":", "");
    		if(fileStr.contains("?")){
    			fileStr=fileStr.substring(0,fileStr.lastIndexOf("?"));
    		}
    		fileStr=fileStr.replace("?", "_");
    		//fileStr=fileStr.replace(".asp", ".jsp");
    		if(!fileStr.contains(".htm") && fileStr.endsWith("/")){
    			File file=new File(savePath+fileStr.substring(0,fileStr.length()-1));
    			file.delete();
    			fileStr+="/index.html";
    		}
    		 
    		//fileStr=fileStr.substring(url.indexOf("/"));
    		File file=new File(savePath+fileStr);
    		if(file.exists()){
    			return "";
    		}
    		if(file.isDirectory()){
    			file=new File(file+"/index.html");
    		}
    		if(!userOne){
    			httpclient=HttpClients.createSystem();
    		}
    		 
    		file.getParentFile().mkdirs();
    		HttpGet httpget=new HttpGet(url);
    		
            httpget.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");    
            
            httpget.setHeader("Accept-Charset", "GB2312,utf-8;q=0.7,*;q=0.7");    
        
            httpget.setHeader("Accept-Encoding", "gzip, deflate");    
        
            httpget.setHeader("Accept-Language", "zh-cn,zh;q=0.5");    
        
            httpget.setHeader("Connection", "keep-alive");    
            if(cookie!=null){
            	httpget.setHeader("Cookie", cookie);    
            }
        
            httpget.setHeader("User-Agent", "Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:32.0) Gecko/20100101 Firefox/32.0");  
            CloseableHttpResponse response = httpclient.execute(httpget);
    		Header[] header=response.getAllHeaders();
    		//关闭http的301 自动跳转
    		httpget.getParams().setParameter("http.protocol.handle-redirects",false);
    		for (Header header2 : header) {
    			System.out.println(header2.getValue());
    			if(header2.getName().equals("Location")){
    				log.info("this page " + url+" forward to--> "+ header2.getValue());
    				aHref.add(header2.getValue());
    				return "this page hava a 3XX";
    			}
    		}
    		InputStream reader=(response.getEntity().getContent());
    		/*String tmp=null;
    		StringBuffer sb=new StringBuffer();*/
    		byte b[]=new byte[1024*32];
    		int len;
    		if(!file.exists()){
    			File tempfile=file;
    			while(!(tempfile=tempfile.getParentFile()).exists()){
    				 
    			}
    			tempfile.getParentFile().delete();
    			file.getParentFile().mkdirs();
    			//file.createNewFile();
    		}
    		FileOutputStream out=new FileOutputStream(file);
    		while((len=reader.read(b))!=-1){
    			out.write(b,0,len);
    			out.flush();
    		}
    		 
    		/*while((tmp=reader.)!=null){
    			sb.append(tmp+"\n");
    		}
    		*/
    		out.close();
    		recordStr+="处理:---〉"+url+" 用时 " + ((System.currentTimeMillis()-start)/1000.0)+"\n";
    		if(socketOut!=null){
    			socketOut.write(("处理:---〉"+url+" 用时 " +((System.currentTimeMillis()-start)/1000.0)).getBytes());
    		}
    		System.out.println("处理:---〉"+url+" 用时 " +((System.currentTimeMillis()-start)/1000.0));
    		if(file.length()>1024*1024*2){
    			return "this is file";
    		}
    		FileInputStream in=new FileInputStream(file);
    		byte r[]=new byte[(int) file.length()];
    		in.read(r);
    		in.close();
    		// 如果网页存在CSS文件的处理
    		if(url.contains(".css")){
    			aHref.addAll(CssParseTool.getImgsUrlByFilePath(file.toString(), url,index));
    		}
    		String htmlStr=new String(r);
    		//FileUtil.writeString(utf8FilePath, FileUtil.readString(javaGbkFile, "GBK"), "UTF-8");
    		/*if(htmlStr.contains(" content=\"text/html; charset=gb2312\"")){
    			FileUtil.writeString(file, FileUtil.readString(file, "GBK").replace("gb2312", "utf-8"), "UTF-8");
    			//FileUtils.writeLines(file, "UTF-8", FileUtils.readLines(file));  
    		}*/
    		/*if(htmlStr.contains("<html>")){
    			 FileUtil.writeString(file, FileUtil.readString(file, "utf-8").replace(".asp", ".jsp").replace("/hostadm/", "").replace("<html>", "<%@ page language=\"java\" import=\"java.util.*\" pageEncoding=\"utf-8\"%>\n<%String path = request.getContextPath();\nString basePath = request.getScheme()+\"://\"+request.getServerName()+\":\"+request.getServerPort()+path+\"/\";%>\n<html>"), "UTF-8");
    		}*/
    		
    		return htmlStr;
    		
    	}
     
    	private  String index;
    	private  String key;
    	private String savePath;
    	private String cookie;
    	private  Set<String> aHref=new ConcurrentSkipListSet<String>();
    	private  Set<String> bHref=new ConcurrentSkipListSet<String>();
    	private void findHref(List<Node> nodes,String url){
    		for (Node node : nodes) {
    			dispose(url, node.attr("href"));
    			dispose(url, node.attr("src"));
    			dispose(url, node.attr("background"));
    			if("style".equals(node.nodeName())){
    				String nowStr=node.outerHtml().replace("<style>", "").replace("</style>", "");
    				try{
    					Set<String> urls=CssParseTool.getImgsUrlByStr(nowStr, url,index);
    					for(String urlt:urls){
    						aHref.add(urlt);
    					}
    					continue;
    				}
    				catch(Exception e){
    					e.printStackTrace();
    				}
    				continue;
    			}
    			else{
    				if(node.attr("style").contains("background:url")){
    					System.out.println(node.attr("style"));
    					dispose(url,node.attr("style").substring(node.attr("style").indexOf("(")+1, node.attr("style").indexOf(")")));
    				}
    				findHref(node.childNodes(),url);
    			}
    		}
    	}
    
    	private void dispose(String url,String context){
    		if(context.contains("javascript")) return;
    		// 去掉网址的瞄文本链接
    		if(context.indexOf("#")!=-1){
    			context=context.substring(0, context.indexOf("#"));
    		}
    		//网址的绝对路径
    		if(context.startsWith("/") && !context.contains("http://")){
    			context=index+context;
    		}
    		//  网址中只一个形如 src="1.jpg"的图片路径 || 用于拼接网址中的相对路径
    		if(!context.contains("/") || (!context.startsWith("/") && !context.contains("http://") &&  !context.contains("https://"))){
    			context=url.substring(0, url.lastIndexOf("/"))+"/"+context;
    		}
    		if(!bHref.contains(context) && context.contains(getKey()))
    		aHref.add(context);
    	}
     
    	public void setIndex(String index) {
    		this.index = index;
    	}
    
    	public String getKey() {
    		return key;
    	}
    
    	public void setKey(String key) {
    		this.key = key;
    	}
    	
    	public static void main(String[] args) {
    		String savePath="";
    		if(File.separatorChar=='/'){
    			savePath="/home/zcc/fecth_temp";
    		}
    		else{
    			savePath="/e:/home/zcc/fecth_temp";
    		}
    		
    		FetchWebPage fwp=new FetchWebPage("http://www.xxxx.net/","www.xxxx.net",savePath);
    		fwp.userOne=false;
    		
    		fwp.go();
    		
    	}
    	
    }
    				

  3. * 这代码中使用了 jodd.jar 中的一个文件编码转化的类. 不做开发估计应该是用不到的.

    * 关于key 主要用于防止抓取到站外去用.

    * 目前只具有抓取部分内容站用.和抓取网站样式用.


    如果想获取最新的代码可以 http://git.oschina.net/94fzb/fetchwebpage

转载请注明作者和出处,并添加本页链接。
原文链接: //xiaochun.zrlog.com/162.html