zoukankan      html  css  js  c++  java
  • java实现简单爬虫(httpclient+htmlparser)

      该程序需要提供一个种子(一个URl地址)作为其实页面,通过分析该页面,将页面上涉及到的url地址爬取到,从而理论上实现爬虫的原来。

      先用一个图来说明该程序的工作流程

      

      在这个程序中存在俩个数据结构,一个是一个队列,该队列存放的是带分析的url,称作UrlQueue.另外一个是一个hashset,该数据结构是存放已经访问过的url。一个url从urlQueue中出队,通过判断看看是否已经存在,若不存在则用htmlparser解析出该页面包含的地址,并且将这些子url进队,对该url的处理是将他存入文件系统利用httpclient完成。该程序需要httpClient和htmlparser相应的包

      下面试两个数据结构的代码

     

    package crawler.store;
    
    import java.util.LinkedList;
    
    /**
     * UrlQueue数据结构用来储存需要进行分析的Url
     * 该结构是一个队列
     * @author Xinyuyu
     *
     */
    class UrlQueue {
        private LinkedList<String> urlQueue = new LinkedList<String>(); 
        
        // 进队
        void inQueue(String url){
            urlQueue.addLast(url);
        }
        
        // 出队
        String outQueue(){
            return urlQueue.removeFirst();
        }
        
        // 判断是否队列是否为空
        boolean queueIsEmpty(){
            return urlQueue.isEmpty();
        }
        // 返回队列的元素数
        int queueNum(){
            return urlQueue.size();
        }
        
        // 判断队列是否包含某元素
        public boolean contains(String url){
            return urlQueue.contains(url);
        }
    }
    package crawler.store;
    
    import java.util.HashSet;
    import java.util.Set;
    
    /**
     * UrlPool数据结构是用来存放已经访问过的url信息
     * 该结构是一个HashSet
     * 一部分完成对自己的操作,一部分完成对UrlQueue的操作(判断是否已经存在,将没有访问过的入队)
     * @author Xinyuyu
     *
     */
    public class UrlPool {
        private static Set<String> urlPool = new HashSet<String>();
        private static UrlQueue urlQueue = new UrlQueue();
        /*
         * 完成自身的操作
         */
        // 增加元素
        public static UrlQueue getQueue(){
            return urlQueue;
        }
        
        public static void addPool(String url){
            urlPool.add(url);
        }
        // 删除元素
        public static void removePool(String url){
            urlPool.remove(url);
        }
        // 判断是否包含
        public static boolean poolContains(String url){
            return urlPool.contains(url);
        }
        // 返回元素个数
        public static int poolNum(){
            return urlPool.size();
        }
    
        /*
         * 完成对UrlQueue的操作
         */
        //判断pool中是否包含url,不包含就入队
        public static void inQueue(String url){
            if(url != null && !url.trim().equals("") && !urlPool.contains(url)){
                urlQueue.inQueue(url);
            }
        }
        // 完成出队
        public static String outQueue(){
            return urlQueue.outQueue();
        }
        // 返回队列元素个数
        public static int queueNum(){
            return urlQueue.queueNum();
        }
        // 判断urlQueue是否为空
        public static boolean queueIsEmpty(){
            return urlQueue.queueIsEmpty();
        }
        // 判断队列中是否包含元素
        public static boolean queueContains(String url){
            return urlQueue.contains(url);
        }
    }

      接着是一个下载网页的类,该类引用了httpclient包

    package crawler.utill;
    
    import java.io.DataOutputStream;
    import java.io.File;
    import java.io.FileNotFoundException;
    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;
    
    /**
     * 将没有访问过的url的内容存储到文件系统中去
     * 
     * @author Xinyuyu
     * 
     */
    public class DownloadFile {
        // 构造存储的文件名称(去除url中不能作为文件名的字符)
        public String getFileName(String url, String contentType) {
            String fileName = "";
            if (contentType.contains("html"))
                fileName = url.replaceAll("[\\?/:*|<>\"]", "_") + ".html";
            else
                // 将类似text/html的字符串截取出文件类型后缀
                fileName = url.replaceAll("[\\?/:*|<>\"]", "_") + "."
                        + contentType.substring(contentType.indexOf("/") + 1);
            return fileName;
        }
    
        // 一个写文件的方法
        public void saveToLocal(byte[] content, String filePath) {
            try {
                DataOutputStream out = new DataOutputStream(new FileOutputStream(
                        new File(filePath)));
                for (int i = 0; i < content.length; i++) {
                    try {
                        out.write(content[i]);
                    } catch (IOException e) {
                        System.err.println("write error");
                        e.printStackTrace();
                    }
                }
                if (out != null) {
                    try {
                        out.close();
                    } catch (IOException e) {
                        System.err.println("close error");
                        e.printStackTrace();
                    }
                }
            } catch (FileNotFoundException e) {
                System.err.println("out error");
                e.printStackTrace();
            }
        }
    
        // 将page的内容写入文件系统
        public String downloadFile(String url) {
            String fileName = "";
            HttpClient httpClient = new HttpClient();
            // 设置HttpClient的连接管理对象,设置 HTTP连接超时5s
            httpClient.getHttpConnectionManager().getParams()
                    .setConnectionTimeout(5000);
            if (url.indexOf("http") != -1) {
                GetMethod getMethod = new GetMethod(url);
                // 作用是什么?
                getMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT,
                        5000);
                getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
                        new DefaultHttpMethodRetryHandler());
                try {
    
                    int statut = httpClient.executeMethod(getMethod);
                    if (statut != HttpStatus.SC_OK)
                        System.err.println("Method failed: "
                                + getMethod.getStatusLine());
                    else {
                        byte[] content = getMethod.getResponseBody();
                        fileName = "e:\\temp\\"
                                + getFileName(url,
                                        getMethod.getResponseHeader("Content-Type")
                                                .getValue());
                        saveToLocal(content, fileName);
                        System.out.println(url + "::" + fileName);
                    }
                } catch (HttpException e) {
                    System.err.println("getmethod error");
                    e.printStackTrace();
                } catch (IOException e) {
                    System.err.println("io error");
                    e.printStackTrace();
                }
            }
            return fileName;
        }
    }

      下面是分析url的类

    package crawler.utill;
    
    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;
    
    /**
     * 完成对要分析的page包含的地址的提取
     * 将提取的地址放入到一个集合中去
     * @author Xinyuyu
     *
     */
    public class HtmlParser {
        private Set<String> links = new HashSet<String>();
        public Set<String> extraLinks(String path){
            // 需要两个过滤器,一个过滤掉<frame src=>和<a href>标签
            @SuppressWarnings("serial")
            NodeFilter nodeFiler = new NodeFilter(){
                public boolean accept(Node node) {
                    if(node.getText().startsWith("fram src="))
                        return true;
                    else
                        return false;
                }
            };
            NodeFilter tagFilter = new NodeClassFilter(LinkTag.class);
            OrFilter orFilter = new OrFilter(nodeFiler, tagFilter);
            try {
                Parser parser = new Parser(path);
                NodeList nodes = parser.extractAllNodesThatMatch(orFilter);
                if(nodes != null){
                    for(int i = 0; i < nodes.size(); i++){
                        Node node = nodes.elementAt(i);
                        if(node instanceof LinkTag)
                            links.add(((LinkTag) node).getLink());
                        else{
                            // 形如<frame src="test.html">
                            String frame = node.getText();
                            int start = frame.indexOf("src=");
                            frame = frame.substring(start);
                            int end = frame.indexOf(">");
                            if(end == -1){
                                end = frame.indexOf(" ");
                            }
                            frame = frame.substring(5, end - 1);
                        }
                    }
                }
            } catch (ParserException e) {
                System.err.println("extra error");
                e.printStackTrace();
            }
            return links;
        }
    }

      最后是初始化爬虫的类

    package crawler.main;
    
    import java.util.Set;
    
    import crawler.store.UrlPool;
    import crawler.utill.DownloadFile;
    import crawler.utill.HtmlParser;
    /**
     * 实例化一个crwler需要一个种子
     * @author Xinyuyu
     *
     */
    public class CrawlerAction {
        
        private void initCrawler(String [] seeds){
            for(int i = 0; i < seeds.length; i++){
                UrlPool.inQueue(seeds[i]);
            }
        }
        public CrawlerAction(String [] seeds){
            initCrawler(seeds);
            System.out.println(UrlPool.queueNum());
            // 若urlpool中的元素超过1000并队列中的元素不为空的时候虚幻
            while(UrlPool.queueNum() > 0 && UrlPool.poolNum() < 1000){
                String visitUrl = UrlPool.outQueue();
                if(visitUrl == null)
                    continue;
                DownloadFile downloadFile = new DownloadFile();
                String filePath = downloadFile.downloadFile(visitUrl);
                UrlPool.addPool(visitUrl);
                HtmlParser htmlParser = new HtmlParser();
                Set<String> links = htmlParser.extraLinks(visitUrl);
                // 若urlpool中不包含该链接的时候就入队
                for(String link : links){
                    UrlPool.inQueue(link);
                }
                System.out.println(visitUrl + "::" + filePath);
            }
        }
    }

      运行程序

    package crawler.main;
    
    public class CrawlerMain {
        public static void main(String [] args){
            String [] seeds = {"http://www.coderanch.com/t/63473/open-source/host-parameter-null-httpclient"};
            CrawlerAction crawler = new CrawlerAction(seeds);
        }
    }    

      这样就可以运行这个简单的爬虫程序了。

    参考

    书 名:自己动手写网络爬虫
    作 者:罗刚
    出 版 社:清华大学出版社
  • 相关阅读:
    基于Ubuntu Jeos打造自己的精简版Linux服务器
    35 vs 53怎么裁
    父母在,不远游
    linux deepin是基于linux mint修改
    novell
    Sahi
    virtualbox on windows store vdi on ndfs due the file will bigger than 4gb
    在Linux下配置邮件系统
    CSS3 backgroundsize 属性
    dede:list及dede:arclist 按权重排序的方法
  • 原文地址:https://www.cnblogs.com/xinyuyu/p/3647791.html
Copyright © 2011-2022 走看看