HZhoog

  :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

最近在学习搜索方面的东西,需要了解网络爬虫方面的知识,虽然有很多开源的强大的爬虫,但本着学习的态度,自己写了一个简单的网络爬虫,以便了解其中原理。

首先介绍每个类的功能:

DownloadPage.java的功能是下载此超链接的页面源代码.

FunctionUtils.java 的功能是提供不同的静态方法,包括:页面链接正则表达式匹配,获取URL链接的元素,判断是否创建文件,获取页面的Url并将其转换为规范的Url,截取网页网页源文件的目标内容。

HrefOfPage.java 的功能是获取页面源代码的超链接。

UrlDataHanding.java 的功能是整合各个给类,实现url到获取数据到数据处理类。

UrlQueue.java 的未访问Url队列。

VisitedUrlQueue.java 已访问过的URL队列。

 

下面介绍一下每个类的源代码:

DownloadPage.java 此类要用到HttpClient组件。

View Code
 1 package com.sreach.spider;
 2 
 3 import java.io.IOException;
 4 import org.apache.http.HttpEntity;
 5 import org.apache.http.HttpResponse;
 6 import org.apache.http.client.ClientProtocolException;
 7 import org.apache.http.client.HttpClient;
 8 import org.apache.http.client.methods.HttpGet;
 9 import org.apache.http.impl.client.DefaultHttpClient;
10 import org.apache.http.util.EntityUtils;
11 
12 public class DownloadPage
13 {
14 
15     /**
16      * 根据URL抓取网页内容
17      * 
18      * @param url
19      * @return
20      */
21     public static String getContentFormUrl(String url)
22     {
23         /* 实例化一个HttpClient客户端 */
24         HttpClient client = new DefaultHttpClient();
25         HttpGet getHttp = new HttpGet(url);
26 
27         String content = null;
28 
29         HttpResponse response;
30         try
31         {
32             /*获得信息载体*/
33             response = client.execute(getHttp);
34             HttpEntity entity = response.getEntity();
35 
36             VisitedUrlQueue.addElem(url);
37 
38             if (entity != null)
39             {
40                 /* 转化为文本信息 */
41                 content = EntityUtils.toString(entity);
42 
43                 /* 判断是否符合下载网页源代码到本地的条件 */
44                 if (FunctionUtils.isCreateFile(url)
45                         && FunctionUtils.isHasGoalContent(content) != -1)
46                 {
47                     FunctionUtils.createFile(FunctionUtils
48                             .getGoalContent(content), url);
49                 }
50             }
51 
52         } catch (ClientProtocolException e)
53         {
54             e.printStackTrace();
55         } catch (IOException e)
56         {
57             e.printStackTrace();
58         } finally
59         {
60             client.getConnectionManager().shutdown();
61         }
62         
63         return content;
64     }
65 
66 }

FunctionUtils.java 此类的方法均为static方法

View Code
package com.sreach.spider;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class FunctionUtils
{

    /**
     * 匹配超链接的正则表达式
     */
    private static String pat = "http://www\\.oschina\\.net/code/explore/.*/\\w+\\.[a-zA-Z]+";
    private static Pattern pattern = Pattern.compile(pat);

    private static BufferedWriter writer = null;

    /**
     * 爬虫搜索深度
     */
    public static int depth = 0;

    /**
     * 以"/"来分割URL,获得超链接的元素
     * 
     * @param url
     * @return
     */
    public static String[] divUrl(String url)
    {
        return url.split("/");
    }

    /**
     * 判断是否创建文件
     * 
     * @param url
     * @return
     */
    public static boolean isCreateFile(String url)
    {
        Matcher matcher = pattern.matcher(url);

        return matcher.matches();
    }

    /**
     * 创建对应文件
     * 
     * @param content
     * @param urlPath
     */
    public static void createFile(String content, String urlPath)
    {
        /* 分割url */
        String[] elems = divUrl(urlPath);
        StringBuffer path = new StringBuffer();

        File file = null;
        for (int i = 1; i < elems.length; i++)
        {
            if (i != elems.length - 1)
            {

                path.append(elems[i]);
                path.append(File.separator);
                file = new File("D:" + File.separator + path.toString());

            }

            if (i == elems.length - 1)
            {
                Pattern pattern = Pattern.compile("\\w+\\.[a-zA-Z]+");
                Matcher matcher = pattern.matcher(elems[i]);
                if ((matcher.matches()))
                {
                    if (!file.exists())
                    {
                        file.mkdirs();
                    }
                    String[] fileName = elems[i].split("\\.");
                    file = new File("D:" + File.separator + path.toString()
                            + File.separator + fileName[0] + ".txt");
                    try
                    {
                        file.createNewFile();
                        writer = new BufferedWriter(new OutputStreamWriter(
                                new FileOutputStream(file)));
                        writer.write(content);
                        writer.flush();
                        writer.close();
                        System.out.println("创建文件成功");
                    } catch (IOException e)
                    {
                        e.printStackTrace();
                    }

                }
            }

        }
    }

    /**
     * 获取页面的超链接并将其转换为正式的A标签
     * 
     * @param href
     * @return
     */
    public static String getHrefOfInOut(String href)
    {
        /* 内外部链接最终转化为完整的链接格式 */
        String resultHref = null;

        /* 判断是否为外部链接 */
        if (href.startsWith("http://"))
        {
            resultHref = href;
        } else
        {
            /* 如果是内部链接,则补充完整的链接地址,其他的格式忽略不处理,如:a href="#" */
            if (href.startsWith("/"))
            {
                resultHref = "http://www.oschina.net" + href;
            }
        }

        return resultHref;
    }

    /**
     * 截取网页网页源文件的目标内容
     * 
     * @param content
     * @return
     */
    public static String getGoalContent(String content)
    {
        int sign = content.indexOf("<pre class=\"");
        String signContent = content.substring(sign);

        int start = signContent.indexOf(">");
        int end = signContent.indexOf("</pre>");

        return signContent.substring(start + 1, end);
    }

    /**
     * 检查网页源文件中是否有目标文件
     * 
     * @param content
     * @return
     */
    public static int isHasGoalContent(String content)
    {
        return content.indexOf("<pre class=\"");
    }

}

HrefOfPage.java 此类为获取页面的超链接

View Code
package com.sreach.spider;

public class HrefOfPage
{
    /**
     * 获得页面源代码中超链接
     */
    public static void getHrefOfContent(String content)
    {
        System.out.println("开始");
        String[] contents = content.split("<a href=\"");
        for (int i = 1; i < contents.length; i++)
        {
            int endHref = contents[i].indexOf("\"");

            String aHref = FunctionUtils.getHrefOfInOut(contents[i].substring(
                    0, endHref));

            if (aHref != null)
            {
                String href = FunctionUtils.getHrefOfInOut(aHref);

                if (!UrlQueue.isContains(href)
                        && href.indexOf("/code/explore") != -1
                        && !VisitedUrlQueue.isContains(href))
                {
                    UrlQueue.addElem(href);
                }
            }
        }

        System.out.println(UrlQueue.size() + "--抓取到的连接数");
        System.out.println(VisitedUrlQueue.size() + "--已处理的页面数");

    }

}

UrlDataHanding.java 此类主要是从未访问队列中获取url,下载页面,分析url,保存已访问url等操作,实现Runnable接口

View Code
package com.sreach.spider;

public class UrlDataHanding implements Runnable
{
    /**
     * 下载对应页面并分析出页面对应的URL放在未访问队列中。
     * @param url
     */
    public void dataHanding(String url)
    {
            HrefOfPage.getHrefOfContent(DownloadPage.getContentFormUrl(url));
    }
        
    public void run()
    {
        while(!UrlQueue.isEmpty())
        {
           dataHanding(UrlQueue.outElem());
        }
    }
}

UrlQueue.java 此类主要是用来存放未访问的URL队列

View Code
package com.sreach.spider;

import java.util.LinkedList;

public class UrlQueue
{
    /**超链接队列*/
    public static LinkedList<String> urlQueue = new LinkedList<String>();
    
    /**队列中对应最多的超链接数量*/
    public static final int MAX_SIZE = 10000;
    
    public synchronized static void addElem(String url)
    {
        urlQueue.add(url);
    }
    
    public synchronized static String outElem()
    {
        return urlQueue.removeFirst();
    }
    
    public synchronized static boolean isEmpty()
    {
        return urlQueue.isEmpty();
    }
    
    public  static int size()
    {
        return urlQueue.size();
    }
    
    public  static boolean isContains(String url)
    {
        return urlQueue.contains(url);
    }

}

VisitedUrlQueue.java 主要是保存已访问过的URL,使用HashSet来保存,主要是考虑到每个访问过的URL是不同。HashSet刚好符合这个要求

View Code
package com.sreach.spider;

import java.util.HashSet;

/**
 * 已访问url队列
 * @author HHZ
 *
 */
public class VisitedUrlQueue
{
    public static HashSet<String> visitedUrlQueue = new HashSet<String>();

    public synchronized static void addElem(String url)
    {
        visitedUrlQueue.add(url);
    }

    public synchronized static boolean isContains(String url)
    {
        return visitedUrlQueue.contains(url);
    }

    public synchronized static int size()
    {
        return visitedUrlQueue.size();
    }
}

Test.java 此类为测试类

View Code
import java.sql.SQLException;

import com.sreach.spider.UrlDataHanding;
import com.sreach.spider.UrlQueue;

public class Test
{
  public static void main(String[] args) throws SQLException
  {
      String url = "http://www.oschina.net/code/explore/achartengine/client/AndroidManifest.xml";
      String url1 = "http://www.oschina.net/code/explore";
      String url2 = "http://www.oschina.net/code/explore/achartengine";
      String url3 = "http://www.oschina.net/code/explore/achartengine/client";
      
      
      UrlQueue.addElem(url);
      UrlQueue.addElem(url1);
      UrlQueue.addElem(url2);
      UrlQueue.addElem(url3);
      
      UrlDataHanding[] url_Handings = new UrlDataHanding[10];
      
          for(int i = 0 ; i < 10 ; i++)
          {
              url_Handings[i] = new UrlDataHanding();
              new Thread(url_Handings[i]).start();
          }

  }
}

说明一下:由于我抓取的是针对oschina的,所以里面的url正则表达式不适合其他网站,需要自己修改一下。你也可以写成xml来配置。

转载请注明出处:http://www.cnblogs.com/HZhoog/archive/2012/05/08/2490374.html 

小弟技术有限,有些地方确实写得不好,希望各位大牛不吝指教

posted on 2012-05-08 17:11  HZhoog  阅读(7810)  评论(2编辑  收藏  举报