宽度优先爬虫

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weijianpeng2013_2015/article/details/71334228

1.自定义队列Queue,保存将要访问的URL:

package com.peng.queue;

import java.util.LinkedList;
//队列,保存将要访问的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();
    }

}

2.定义数据结构LinkQueue ,维护已经访问过的URL和尚未访问过的URL:

package com.peng.queue;

import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.Set;
//注意此时队列用的是自定义的队列
import com.peng.queue.*;
public class LinkQueue1 {
    //已访问的url集合
    private static Set visitedUrl=new HashSet();
    //待访问的url集合
    private static Queue unVisitedUrl=new Queue();
    //获得URL队列
    public static Queue getUnVisitedUrl(){
        return unVisitedUrl;
    }
    //添加到访问过的URL队列中
    public static void addVisitedUrl(String url){
        visitedUrl.add(url);
    }
    //移除访问过的URL
    public static void removeVisitedUrl(String url){
        visitedUrl.remove(url);
    }
    //未访问过的URL出队列
    public static Object unVisitedUrlDeQueue(){
        //return unVisitedUrl.deQueue();
        return unVisitedUrl.deQueue();
    }
    //保证每个URL只被访问一次
    public static void addUnvisitedUrl(String url){
        if(url!=null&&!url.trim().equals("")&&!visitedUrl.contains(url)
                &&!unVisitedUrl.contians(url))
            //unVisitedUrl.enQueue(url);
            unVisitedUrl.enQueue(url);;
    }
    //获得已经访问的URL数目
    public static int getVisitedUrlNum(){
        return visitedUrl.size();
    }
    //判断未访问的URL队列中是否为空
    public static boolean unVisitedUrlsEmpty(){
        //return unVisitedUrl.empty();
        return unVisitedUrl.empty();
    }
}

3.DownLoadFile网页下载并处理:

package com.peng.download;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;

public class DownLoadFile {
    /**
     * 根据 url 和网页类型生成需要保存的网页的文件名 去除掉 url 中非文件名字符
     */
    public  String getFileNameByUrl(String url,String contentType)
    {
        //remove 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 (IOException e) {
            e.printStackTrace();
        }
    }

    /* 下载 url 指向的网页 */
    public String downloadFile(String url) {
        String filePath = null;
        /* 1.生成 HttpClinet 对象并设置参数 */
        HttpClient httpClient = 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.执行 HTTP GET 请求 */
        try {
            int statusCode = httpClient.executeMethod(getMethod);
            // 判断访问的状态码
            if (statusCode != HttpStatus.SC_OK) {
                System.err.println("Method failed: "
                        + 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("Please check your provided http address!");
            e.printStackTrace();
        } catch (IOException e) {
            // 发生网络异常
            e.printStackTrace();
        } finally {
            // 释放连接
            getMethod.releaseConnection();
        }
        return filePath;
    }
}

4.从获得的网页中提取 URL—HtmlParserTool:

package com.peng.download;



import java.util.HashSet;
import java.util.Set;

import org.htmlparser.Node;
import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.filters.NodeClassFilter;
import org.htmlparser.filters.OrFilter;
import org.htmlparser.tags.LinkTag;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;

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 (int i = 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 属性的链接如 <frame src="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;
    }
}

5.主程序

package com.peng.download;

import java.util.Set;

import com.peng.queue.LinkQueue;

public class MyCrawler {
    //使用种子初始化URL队列
    private void initCrawlerWithSeeds(String[] seeds){
        for(int i=0;i<seeds.length;i++)
            LinkQueue.addUnvisitedUrl(seeds[i]);
    }
    //抓取过程
    public void crawling(String[] seeds){
        //定义过滤器,提取以http://www.lietuw.com开头的链接
        LinkFilter filter=new LinkFilter() {

            @Override
            public boolean accept(String url) {
                if(url.startsWith("http://www.baidu.com"))
                    return true;
                else 
                    return false;
            }
        };
        //初始化URL队列
        initCrawlerWithSeeds(seeds);
        //循环条件:待抓取的链接不空且抓取的网页不多于1000
        int i=0;
        while(!LinkQueue.unVisitedUrlsEmpty()&&LinkQueue.getVisitedUrlNum()<=1000){
            System.out.println();
            System.out.println("执行了"+i+"次");
            i++;
            //队头URL出队列
            String visitUrl=(String) LinkQueue.unVisitedUrlDeQueue();
            if(visitUrl==null)
                continue;
            DownLoadFile downLoadFile=new DownLoadFile();
            //下载网页
            downLoadFile.downloadFile(visitUrl);
            //该URL放入已访问的URL中
            LinkQueue.addUnvisitedUrl(visitUrl);
            //提取出下载网页中的URL
            Set<String> links=HtmlParserTool.extracLinks(visitUrl, filter);
            //新的未访问的URL队列
            for(String link:links){
                LinkQueue.addUnvisitedUrl(link);
            }
        }
    }
    public static void main(String[] args) {
        MyCrawler myCrawler=new MyCrawler();
        myCrawler.crawling(new String[]{"http://www.baidu.com"});
    }

}

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

package com.peng.download;

public interface LinkFilter {
    public boolean accept(String url);

}

猜你喜欢

转载自blog.csdn.net/weijianpeng2013_2015/article/details/71334228
今日推荐