天天看点

Java简单的网络爬虫实现

一、网络爬虫
           

是一个自动提取网页的程序,它为搜索引擎从万维网上下载网页,是搜索引擎的重要组成。

二、目的

      需要把别人网站上发布的信息,采集放到自己数据库中,这里先放到自定义bean中(这里采集网站http://www.szhfpc.gov.cn/xxgk/zcfggfxwj/mybh_5 政策法规则内容)

1. 传入需要采集的页面获取整个页面的内容

         编写 AnalysisUtils.java 工具类

public static String SendGet(String url) {
		// 定义一个字符串用来存储网页内容
		String result = "";
		// 定义一个缓冲字符输入流
		BufferedReader in = null;
		try {
			// 将string转成url对象
			URL realUrl = new URL(url);
			
			// 初始化一个链接到那个url的连接
			URLConnection connection = realUrl.openConnection();
			
			// 设置欺骗服务器访问
			connection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
			
			// 开始实际的连接
			connection.connect();
			// 初始化 BufferedReader输入流来读取URL的响应
			in = new BufferedReader(new InputStreamReader(
					connection.getInputStream(), "UTF-8"));
			// 用来临时存储抓取到的每一行的数据
			String line;
			while ((line = in.readLine()) != null) {
				// 遍历抓取到的每一行并将其存储到result里面
				result += line;
			}
		} catch (Exception e) {
			System.out.println("发送GET请求出现异常!" + e);
			e.printStackTrace();
		}
		// 使用finally来关闭输入流
		finally {
			try {
				if (in != null) {
					in.close();
				}
			} catch (Exception e2) {
				e2.printStackTrace();
			}
		}
		return result;
	}
           

                注:如果不设置connection.setRequestProperty属性,多测试几次会报403错误

2.  解析整个页面内容

         编写网站解析AnalysisSzhfpcWebsite类,支持分页解析,支持附件下载到本地

private static final String LOCAL_PATH = "D:/tempfile"; // 存放本地文件路径
	
	/**
	 * http://www.szhfpc.gov.cn/ 采集
	 * @param url 路径 
	 * 				例 
	 * 					采集政策法规
	 * 					http://www.szhfpc.gov.cn/xxgk/zcfggfxwj/mybh_5
	 * 					注 传入URL最后一位不要加 / 
	 */
	public static List<ZcfgEntity> szhfpc(String url) {
		List<ZcfgEntity> result = new ArrayList<ZcfgEntity>();
		
		String mybhContent = AnalysisUtils.SendGet(url);
		
		result.addAll(parseLevel(mybhContent,url));
		
		// 获得左侧链接只执行一次
		String g1LeftDiv = "<div class=\"gl-left\">(.*?)</div>";
		Pattern g1LeftPt = Pattern.compile(g1LeftDiv);
		Matcher g1LeftMt = g1LeftPt.matcher(mybhContent);
		if(g1LeftMt.find()){
			String g1LeftContent = g1LeftMt.group(1);
			
			// 先DIV 下 A 链接
			String g1Href = "href=\"([^\"]*)";
			Pattern g1HrefPt = Pattern.compile(g1Href);
			Matcher g1HrefMt = g1HrefPt.matcher(g1LeftContent);
			
			while(g1HrefMt.find()){
				String g1LeftHrefContent = g1HrefMt.group(1);
				System.out.println("链接:"+g1LeftHrefContent);
				
				int index = url.lastIndexOf("/");
				String tempUrl = url.substring(0, index);
				System.out.println("tempUrl:"+tempUrl);
				
				int startIndex = g1LeftHrefContent.indexOf("/");
				int beginIndex = g1LeftHrefContent.lastIndexOf("/");
				String temp = g1LeftHrefContent.substring(startIndex, beginIndex);
				System.out.println("temp:"+temp);
				
				if(!temp.trim().equals("")){
					String u = tempUrl+temp;
					String c = AnalysisUtils.SendGet(u);
					result.addAll(parseLevel(c,u));
				}
				
			}
		}
		
		return result;
	}
	
	/**
	 * 二级页面内容解析
	 * @param content
	 */
	public static List<ZcfgEntity> parseLevel(String mybhContent,String url){
		List<ZcfgEntity> result = new ArrayList<ZcfgEntity>();
		
		String type = "";
		String typeItem = "";
		// 获取位置信息
		String curmbDiv = "<div class=\"curmb\">(.*?)</div>";
		Pattern curmbPt = Pattern.compile(curmbDiv);
		Matcher curmbMt = curmbPt.matcher(mybhContent);
		if(curmbMt.find()){
			String curmbContent = curmbMt.group(1);
			System.out.println("curmbMt:"+curmbMt.group(1));
			
			// 先DIV 下 A 链接
			String curmbHref = "<a[^>]*>(.*?)</a>";
			Pattern curmbHrefPt = Pattern.compile(curmbHref);
			Matcher curmbHrefMt = curmbHrefPt.matcher(curmbContent);
			
			// 取最后二项
			List<String> tempList = new ArrayList<String>();
			while(curmbHrefMt.find()){
				tempList.add(curmbHrefMt.group(1));
			}
			
			if(tempList.size()>0){
				int size = tempList.size();
				typeItem = tempList.get(size-1); // 最后一项
				type = tempList.get(size-2); // 最后第二项
			}
		}
		
		// 先取<div class="gl-right">
		String g1Div = "<div class=\"gl-right\">(.*?)</div>";
		Pattern g1Pt = Pattern.compile(g1Div);
		Matcher g1Mt = g1Pt.matcher(mybhContent);
		if(g1Mt.find()){
			String g1Cotnent = g1Mt.group(1);
			System.out.println("g1Div:"+g1Cotnent);
			
			// 先DIV 下 A 链接
			String g1Href = "href=\"([^\"]*)";
			Pattern g1HrefPt = Pattern.compile(g1Href);
			Matcher g1HrefMt = g1HrefPt.matcher(g1Cotnent);
			
			ZcfgEntity entity = null;
			while(g1HrefMt.find()){
				String g1HrefContent = g1HrefMt.group(1);
				System.out.println("链接:"+g1HrefContent);
				
				entity = new ZcfgEntity();
				entity.setType(type);
				entity.setTypeItem(typeItem);
				
				
				// 详细页面解析 与附件下载 
				parseDetail(url+g1HrefContent.substring(1, g1HrefContent.length()),entity);
				
				result.add(entity);
			}
		}
		
		// 获取分页数 
		String pageDiv = "<div class=\"page mt20\">(.*?)</div>";
		Pattern pagePt = Pattern.compile(pageDiv);
		Matcher pageMt  = pagePt.matcher(mybhContent);
		if(pageMt.find()){
			String pageContent = pageMt.group(1);
			System.out.println("pageContent:"+pageContent);
			int startIndex = pageContent.indexOf("(")+1;
			int beginIndex = pageContent.indexOf(",");
			String pageStr = pageContent.substring(startIndex, beginIndex);
			
			//System.out.println("页数:"+pageStr);
			
			int page = Integer.parseInt(pageStr);
			
			// 分页循环读取
			for (int i = 1; i < page; i++) {
				System.out.println("第"+(i+1)+"页数据如下:");
				String s = url + "/index_" + i + ".htm";
				String secondLevelContent = AnalysisUtils.SendGet(s);
				
				g1Mt = g1Pt.matcher(secondLevelContent);
				if(g1Mt.find()){
					String g1Cotnent = g1Mt.group(1);
					//System.out.println("g1Div:"+g1Cotnent);
					
					// 先DIV 下 A 链接
					String g1Href = "href=\"([^\"]*)";
					Pattern g1HrefPt = Pattern.compile(g1Href);
					Matcher g1HrefMt = g1HrefPt.matcher(g1Cotnent);
					
					ZcfgEntity entity = null;
					while(g1HrefMt.find()){
						String g1HrefContent = g1HrefMt.group(1);
						System.out.println("链接:"+g1HrefContent);
						
						entity = new ZcfgEntity();
						entity.setType(type);
						entity.setTypeItem(typeItem);
						
						// 详细页面解析 与附件下载 
						parseDetail(url+g1HrefContent.substring(1, g1HrefContent.length()),entity);
						result.add(entity);
					}
				}
			}
		}
		return result;
	}
	
	/**
	 * 解析http://www.szhfpc.gov.cn/xxgk/zcfggfxwj 网络下 详细页面
	 * @param url 页面地址
	 */
	private static void parseDetail(String url,ZcfgEntity entity){
		
		String content = AnalysisUtils.SendGet(url);
		System.out.println("content:"+content);
		
		// 获得class=main的DIV
		String div = "<div class=.+main.+?>(.*?)</div>";
		Pattern mainPt = Pattern.compile(div);
		Matcher mainMt = mainPt.matcher(content);
		if(mainMt.find()){
			System.out.println("内容:"+mainMt.group(1));
			
			String group = mainMt.group(1);
			String h4 = "<h4>(.*?)</h4>";
			Pattern h4Pt = Pattern.compile(h4);
			Matcher h4Mt = h4Pt.matcher(group);
			if(h4Mt.find()){ // 标题
				String title = h4Mt.group(1);
				entity.setTitle(title);
			}
			
			// 发布机构
			String mechanism = "<label>发布机构:(.*?)</label>";
			Pattern pt1 = Pattern.compile(mechanism);
			Matcher mt1 = pt1.matcher(group);
			if(mt1.find()){ // 发布机构
				String sendMechanism = mt1.group(1);
				entity.setSendMechanism(sendMechanism);
			}
			
			// 发布时间
			String date = "<label>发布日期:(.*?)</label>";
			Pattern pt2 = Pattern.compile(date);
			Matcher mt2 = pt2.matcher(group);
			if(mt2.find()){ // 发布日期
				String sendDate = mt2.group(1);
				entity.setSendDate(sendDate);
			}
			
			// 信息索取号
			String info = "<label>信息索取号:(.*?)</label>";
			Pattern pt3 = Pattern.compile(info);
			Matcher mt3 = pt3.matcher(group);
			if(mt3.find()){ // 信息索取号
				String infoNumber = mt3.group(1);
				entity.setInfoNumber(infoNumber);
			}
			
			// 内容         
			String nrDiv = "<div class=\"nr\">(.*?)</div>";
			Pattern nrPt = Pattern.compile(nrDiv);
			Matcher nrMt = nrPt.matcher(content);
			if(nrMt.find()){ // 内容
				String t = nrMt.group(1);
				entity.setContent(t);
			}
			
			// 附件
			boolean isFile = false; //是否有文件名
			String fileDesc = "filedesc=\"(.*?)\";"; // 文件名
			Pattern fileDescPt = Pattern.compile(fileDesc);
			Matcher fileDescMt = fileDescPt.matcher(content);
			
			String[] fileDescs = null;
			
			if(fileDescMt.find()){
				if(fileDescMt.group(1)!=null && !fileDescMt.group(1).trim().equals("")){ // 判断是否存在
					isFile = true;
					System.out.println("文件名:"+fileDescMt.group(1)); 
					
					// 可能存在多个用;分割
					fileDescs = fileDescMt.group(1).split(";");
				}
			}
			
			if(isFile){
				List<FileEntity> fileList = new ArrayList<FileEntity>();
				
				String fileUrl = "fileurl=\"(.*?)\";"; // 路径
				Pattern fileUrlPt = Pattern.compile(fileUrl);
				Matcher fileUrlMt = fileUrlPt.matcher(content);
				if(fileUrlMt.find()){
					System.out.println("文件路径:"+fileUrlMt.group(1)); 
					
					// 可能存在多个用;分割
					String fileName = fileUrlMt.group(1);
					
					String[] fileNames = fileName.split(";");
					
					FileEntity fileEntity = null;
					for (int i = 0; i < fileNames.length; i++) {
						fileEntity = new FileEntity();
						
						String tempFileName = fileNames[i].substring(1, fileNames[i].length());
						// 文件下载到本地
						fileEntity.setFileName(fileDescs[i]); // 显示文件名
						fileEntity.setFilePath(tempFileName); // 下载别名
						
						int lastIndex = url.lastIndexOf("/");
						String path = url.substring(0, lastIndex);
						
						String netUrl = path+tempFileName;
						String localUrl = LOCAL_PATH+tempFileName;
						try{
							downloadNet(netUrl,localUrl);
						}catch(Exception e){
							e.printStackTrace();
						}
						
						fileList.add(fileEntity); //存放到集合中
					}
				}
				
				entity.setFileList(fileList);
			}
			
			
		}
	}
	
	/**
	 * 网络文件下载
	 * @param netUrl 网络路径
	 * @param lcalUrl 本地路径
	 * @throws Exception
	 */
	private static void downloadNet(String netUrl,String lcalUrl) throws Exception{
		int byteread = 0;
        InputStream inStream = null;
        FileOutputStream fs = null;
		URL url = new URL(netUrl);
		URLConnection conn = url.openConnection();
        inStream = conn.getInputStream();
		
        fs = new FileOutputStream(lcalUrl);

        byte[] buffer = new byte[1024];
        while ((byteread = inStream.read(buffer)) != -1) {
            fs.write(buffer, 0, byteread);
        }
		
		if(fs!=null){
			fs.close();
		}
		if(inStream!=null){
			inStream.close();
		}
	}

           

                 实体类:ZcfgEntity

<span style="white-space:pre">	</span>private String type; // 类型
	
	private String typeItem; // 子类
	
	private String title; // 标题
	
	private String sendMechanism; // 发布机构
	
	private String sendDate; // 发布时间
	
	private String infoNumber; // 信息索取号
	
	private String content; // 内容
	
	private List<FileEntity> fileList; // 附件
           

省略get、set方法

附件类FileEntity

private String fileName;
	
	private String filePath;
           

省略get、set方法

编写测试类:Test.java

long startTime = System.currentTimeMillis();
		
		
	// 政策法规
	String zcfgUrl = "http://www.szhfpc.gov.cn/xxgk/zcfggfxwj/mybh_5";
	List<ZcfgEntity> zcfgList = AnalysisSzhfpcWebsite.szhfpc(zcfgUrl);
	System.out.println("政策法规条数:"+zcfgList.size());
		
		
	long endTime = System.currentTimeMillis();
	System.out.println("总共耗时:"+(endTime-startTime) +" S");
		
           

三、 碰到的问题与后续工作

1. 刚开始忘记设置connection.setRequestProperty属性,多运行几次就禁止访问了!

2. 主要工作查看原HTML代码,去分析自己需要的数据在什么链接下,使用正则表达式去获取链接解析。

        3. 页面左侧链接只解析一次,如每次解析数据重复。

        4. 分页只有在第一次进入列表页面获取分页数,此后不再获取。

         忧化

         1. 可以使用多线程去读取,解析!(可能会被禁IP)

         2. 过滤重复数据读取