• 2 宽度优先爬虫和带偏好的爬虫(2)


        接上节

      3 Java 宽度优先爬虫示例

        本节使用java实现一个简易的爬虫。其中用到了HttpClient和HtmlParser两个开源工具包。HttpClient的内容之前已经做过详细的阐述。有关HtmlParser的用法,以后会给出详细的介绍。为了便于理解,下面给出示例程序的结构,如下图:

                                             

        首先,需要定义图中所描述的“URL队列”,这里使用一个LinkedList来实现这个队列。

    Queue类

    /**
    *队列,保存将要访问的URL
    */
    
    public class Queue{
       //使用链表实现队列
        private LinkedList queue = new LinkedList();
      //入队列
       public void enQueue(Object t){
             queue.addLast(t);
      }  
      //出队列
       public Object deQueue(){
             return queue.removeFirst();  
      }
       //判断队列是否为空
         public boolean isQueueEmpty(){
               return queue.isEmpty();
      } 
       //判断队列是否包含t
         public boolean contians(Object t){
               return queue.contains(t);
      }    
         public boolean empty(){
              return queue.isEmpty();
      }
    
    
    }     

        除了URL队列之外,在爬虫过程中,还需要一个数据结构记录已经访问过得URL。每当要访问URL的时候,首先在这个数据结构中进行查找,如果当前的URL已经存在,则丢弃它。这个数据结构要有两个特点:

    • 结构中保存的URL不能重复。
    • 能够快速地查找(实际系统中URL的数目非常多,因此要考虑查找性能)。

      针对以上两点,我们选择HashSet作为存储结构。

    LinkQueue类:

     1 public class LinkQueue{
     2   37 }

      下面的代码详细说明了网页下载并处理的过程。和第一节讲述的内容相比,它考虑了更多的方面。比如存储网页,设置请求超时策略等。

    DownLoadFile类:

    public class DownLoadFile{
      /**
      *根据URL和网页类型生成需要保存的网页的文件名,去除URL中的非文件名字符
      */
      public String getFileNameByUrl(String url,String contentType)
      {
        //移除http:
        url=url.substring(7);
        //text/html类型
        if(contentType.indexOf("html")!=-1)
        {
          url=url.replaceAll("[\\?/:*|<>\"]","_")+".html";
          return url;
        }
        //如application/pdf类型
        else
        {
          return url.replaceAll("[\\?/:*|<>\"]","_")+"."+contentType.substring(contentType.lastIndexOf("/")+1);
        }
      }
      /**
      *保存网页字节数组到本地文件,filePath为要保存的文件的相对地址
      */
      private void saveToLocal(byte[]data,String filePath){
        try{
            DataOutputStream out=new DataOutputStream(new FileOutputStream(new File(filePath)));
            for(int i=0;i<data.length;i++)
            out.write(data[i]);
            out.flush();
            out.close();
            }catch(IOExceptione){
              e.printStackTrace();
            }
         }
            //下载URL指向的网页
         public String downloadFile(String url){
            String filePath=null;
            //1.生成HttpClinet对象并设置参数
            HttpClienthttp Client=new HttpClient();
            //设置HTTP连接超时5s
            httpClient.getHttpConnectionManager().getParams()
            .setConnectionTimeout(5000);
            //2.生成GetMethod对象并设置参数
            GetMethod getMethod=new GetMethod(url);
            //设置get请求超时5s
            getMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT,5000);
            //设置请求重试处理
            getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,new DefaultHttpMethodRetryHandler());
            //3.执行HTTPGET请求
            try{
              int statusCode=httpClient.executeMethod(getMethod);
              //判断访问的状态码
              if(statusCode!=HttpStatus.SC_OK){
                  System.err.println("Methodfailed:"+getMethod.getStatusLine());
                  filePath=null;
                }
                  //4.处理HTTP响应内容
                  byte[]responseBody=getMethod.getResponseBody();//读取为字节数组
                  //根据网页url生成保存时的文件名
                  filePath="temp\\"+getFileNameByUrl(url,getMethod.getResponseHeader("Content-Type").getValue());
                  saveToLocal(responseBody,filePath);
                }catch(HttpException e){
                  //发生致命的异常,可能是协议不对或者返回的内容有问题
                  System.out.println("Pleasecheckyourprovidedhttpaddress!");
                  e.printStackTrace();
                }catch(IOException e){
                  //发生网络异常
                  e.printStackTrace();
                }finally{
                  //释放连接
                  getMethod.releaseConnection();
                }
                return filePath;        
         }
    }
    }

        接下来,演示如何从获得的网页中提取URL,Java有一个非常实用的开源工具包HtmlParser,它专门针对Html页面进行处理,不仅能提取URL,还能提取文本以及你想要的任何内容。关于它的有关内容,会在后面详细介绍。以下是代码:

    HtmlParserTool类:

    public class HtmlParserTool{
      //获取一个网站上的链接,filter用来过滤链接
        public static Set<String> extracLinks(String url,LinkFilter filter){
            Set<String> links=new HashSet<String>();
            try{
                Parser parser=new Parser(url);
                 parser.setEncoding("gb2312");
              //过滤<frame>标签的filter,用来提取frame标签里的src属性
                 NodeFilter frameFilter=new NodeFilter(){
                public boolean accept(Node node){
                if(node.getText().startsWith("frame src=")){
                   return true;
                 }else{
                   return false;
                 }
                };
                    //OrFilter来设置过滤<a>标签和<frame>标签
                 OrFilter linkFilter=new OrFilter(new NodeClassFilter(LinkTag.class),frameFilter);
                    //得到所有经过过滤的标签
                    NodeList list=parser.extractAllNodesThatMatch(linkFilter);
                    for(inti=0;i<list.size();i++){
                        Node tag=list.elementAt(i);
                        if(tag instanceof LinkTag)//<a>标签
                        {
                            LinkTag link=(LinkTag)tag;
                            String linkUrl=link.getLink();//URL
                            if(filter.accept(linkUrl))
                                links.add(linkUrl);
                        }else//<frame>标签
                        {
                            //提取frame里src属性的链接,如<framesrc="test.html"/>
                            String frame=tag.getText();
                            int start=frame.indexOf("src=");
                            frame=frame.substring(start);
                            int end=frame.indexOf("");
                            if(end==-1)
                              end=frame.indexOf(">");
                            String frameUrl=frame.substring(5,end-1);
                            if(filter.accept(frameUrl))
                                links.add(frameUrl);
                        }
                        }
                    }catch(ParserException e){
                        e.printStackTrace();
                    }
                    return links;
                    }
                }

    最后,来看看宽度爬虫的主程序:

    MyCrawler类:

    public class MyCrawler{
    /**
    *使用种子初始化URL队列
    *@return
    *@paramseeds种子URL
    */
    private void initCrawlerWithSeeds(String[]seeds)
    {
        for(int i=0;i<seeds.length;i++)
            LinkQueue.addUnvisitedUrl(seeds[i]);
    }
    /**
    *抓取过程
    *@return
    *@paramseeds
    */
    public void crawling(String[]seeds)
    { //定义过滤器,提取以http://www.lietu.com开头的链接
        LinkFilter filter=new LinkFilter(){
            public boolean accept(Stringurl){
                if(url.startsWith("http://www.lietu.com"))
                    return true;
                else
                    return false;
            }
    };
       //初始化URL队列
        initCrawlerWithSeeds(seeds);
          //循环条件:待抓取的链接不空且抓取的网页不多于1000
        while(!LinkQueue.unVisitedUrlsEmpty()&&LinkQueue.getVisitedUrlNum()<=1000)
        {
           //队头URL出队列
            String visitUrl=(String)LinkQueue.unVisitedUrlDeQueue();
            if(visitUrl==null)
                continue;
            DownLoadFile downLoader=new DownLoadFile();
            //下载网页
            downLoader.downloadFile(visitUrl);
           //该URL放入已访问的URL中
            LinkQueue.addVisitedUrl(visitUrl);
           //提取出下载网页中的URL
            Set<String>links=HtmlParserTool.extracLinks(visitUrl,filter);
          //新的未访问的URL入队
            for(Stringlink:links)
                    {
                           LinkQueue.addUnvisitedUrl(link);
                    }
                    }
                    }
                    //main方法入口
    public static void main(String[]args)
    {
        MyCrawler crawler=new MyCrawler();
        crawler.crawling(new String[]{"http://www.lietu.com"});
        }
    }

      上面的主程序使用了一个LinkFilter接口,并且实现了一个内部类。这个接口的目的是为了过滤提取出来的URL,它使得程序中提取出来的URL只会和猎图网站相关。而不会提取其他无关的网站,代码如下:

    public interface LinkFilter{
    
          public boolean accept(String url);
    }
  • 相关阅读:
    欧几里得 与 扩展欧几里得
    hdu-1559 最大子矩阵
    hdu-1081 To The Max (最大子矩阵和)
    Oracle处理排序问题
    报表犯的错误
    MySQL中汉字一二三排序问题
    MySQL复习
    帆软查看显示和填报显示
    MySQL某年查询12个月份的数据
    mysql中去日期格式
  • 原文地址:https://www.cnblogs.com/94julia/p/2969886.html
Copyright © 2020-2023  润新知