zoukankan      html  css  js  c++  java
  • 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);
    }
  • 相关阅读:
    微软外服 AlI In One
    js 循环多次和循环一次的时间的性能对比 All In One
    vue inject All In One
    Excel 表格数据倒置 All In One
    SVG tickets All In One
    OH MY ZSH All In One
    js array for loop performance compare All In One
    mac terminal show You have new mail All In one
    新闻视频 26 制作母版页
    转自牛腩 母版页和相对路径
  • 原文地址:https://www.cnblogs.com/94julia/p/2969886.html
Copyright © 2011-2022 走看看