宽度优先爬虫

来源:互联网 发布:淘宝旅行网订机票 编辑:程序博客网 时间:2024/06/01 12:15

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

package com.peng.queue;import java.util.LinkedList;//队列,保存将要访问的URLpublic 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);}
0 0
原创粉丝点击