zoukankan      html  css  js  c++  java
  • Java网络爬虫

    实际的爬虫是从一系列的种子链接开始。种子链接是起始节点,种子页面的超链接指向的页面是子节点(中间节点),对于非html文档,如excel等,不能从中提取超链接,看做图的终端节点

    网络爬虫的基本知识

    网络爬虫通过遍历互联网络,把网络中的相关网页全部抓取过来,这体现了爬的概念。爬虫如何遍历网络呢,互联网可以看做是一张大图,每个页面看做其中的一个节点,页面的连接看做是有向边。图的遍历方式分为宽度遍历和深度遍历,但是深度遍历可能会在深度上过深的遍历或者陷入黑洞。所以,大多数爬虫不采用这种形式。另一方面,爬虫在按照宽度优先遍历的方式时候,会给待遍历的网页赋予一定优先级,这种叫做带偏好的遍历。

    实际的爬虫是从一系列的种子链接开始。种子链接是起始节点,种子页面的超链接指向的页面是子节点(中间节点),对于非html文档,如excel等,不能从中提取超链接,看做图的终端节点。整个遍历过程中维护一张visited表,记录哪些节点(链接)已经处理过了,跳过不作处理。

    使用宽度优先搜索策略,主要原因有:

    a、重要的网页一般离种子比较近,例如我们打开的新闻网站时候,往往是最热门的新闻,随着深入冲浪,网页的重要性越来越低。

    b、万维网实际深度最多达17层,但到达某个网页总存在一条很短路径,而宽度优先遍历可以最快的速度找到这个网页

    c、宽度优先有利于多爬虫合作抓取。

    网络爬虫的简单实现

    代码预览


    代码结构

    1、定义已访问队列,待访问队列和爬取得URL的哈希表,包括出队列,入队列,判断队列是否空等操作

    代码如下:

    package webspider;
    import java.util.HashSet;
    import java.util.PriorityQueue;
    import java.util.Set;
    import java.util.Queue;
    
    public class LinkQueue
    {
        // 已访问的 url 集合
        private static Set visitedUrl = new HashSet();
        // 待访问的 url 集合
        private static Queue unVisitedUrl = new PriorityQueue();
    
        // 获得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.poll();
        }
    
        // 保证每个 url 只被访问一次
        public static void addUnvisitedUrl(String url)
        {
            if (url != null && !url.trim().equals("") && !visitedUrl.contains(url)
                    && !unVisitedUrl.contains(url))
                unVisitedUrl.add(url);
        }
    
        // 获得已经访问的URL数目
        public static int getVisitedUrlNum()
        {
            return visitedUrl.size();
        }
    
        // 判断未访问的URL队列中是否为空
        public static boolean unVisitedUrlsEmpty()
        {
            return unVisitedUrl.isEmpty();
        }
    
    }

    2、定义DownLoadFile类,根据得到的url,爬取网页内容,下载到本地保存。此处需要引用commons-httpclient.jar,commons-codec.jar,commons-logging.jar

    代码如下:

    package webspider;
    
    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 = "f:\spider\"
                           + 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;
        }
    }
    

    3、定义HtmlParserTool类,用来获得网页中的超链接(包括a标签,frame中的src等等),即为了得到子节点的URL。需要引入htmlparser.jar

    代码如下:

    package webspider;
    
    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("utf-8");
                // 过滤 <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;
        }
    }

    4、编写测试类MyCrawler,用来测试爬取效果

    代码如下:

    
    
    package webspider;
    
    import java.util.Set;
    
    public class MyCrawler
    {
        /**
         * 使用种子初始化 URL 队列
         *
         * @return
         * @param seeds
         *            种子URL
         */
        private void initCrawlerWithSeeds(String[] seeds)
        {
            for (int i = 0; i < seeds.length; i++)
                LinkQueue.addUnvisitedUrl(seeds[i]);
        }
    
        /**
         * 抓取过程
         *
         * @return
         * @param seeds
         */
        public void crawling(String[] seeds)   // 定义过滤器,提取以http://www.lietu.com开头的链接
        {
            LinkFilter filter = new LinkFilter()
            {
                public boolean accept(String url)
                {
                    if (url.startsWith("http://www.baidu.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 (String link : links)
                {
                    LinkQueue.addUnvisitedUrl(link);
                }
            }
        }
    
        // main 方法入口
        public static void main(String[] args)
        {
            MyCrawler crawler = new MyCrawler();
            crawler.crawling(new String[] { "http://www.baidu.com" });
        }
    
    }
    
    

    5.linkfiter接口

    package webspider;
    
    public interface LinkFilter {
        public boolean accept(String url);
    }

    至此,可以看到f:spider文件夹下面已经出现了很多html文件,都是关于百度的,以“www.baidu.com”为开头。


    运行效果

  • 相关阅读:
    如释重负(纪——写完作业论文)
    安装sql2005中文版时提示系统配置检查器失败,消息为“性能监视器计数器检查失败”
    Get ConnectString Form Web.config
    如何在ASP.NET中实现防盗链[转]
    Authentication and Authorization
    上传大步的自唱Music,绝对好听!
    "某个程序安装已在安装计算机上创建挂起的文件操作" 解决办法
    WebM给我们带来什么?H.264又给我们带来什么?Google不支持H.264对未来会产生怎样的影响? 人工智能
    机器学习初探 人工智能
    函数式编程学习之路(九) 人工智能
  • 原文地址:https://www.cnblogs.com/ainima/p/6331858.html
Copyright © 2011-2022 走看看