java写爬虫效果如何?java如何写一个简单爬虫?

简单来讲,爬虫就是一个探测机器,是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。有些会java的人员就会利用java开发爬虫,那java写爬虫效果如何?下面来我们就来给大家讲解一下。

java写爬虫效果还是不粗的,尤其是针对复杂的爬虫系统有优势。

java如何写一个简单爬虫?

一、网络爬虫的基本知识

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

实际的爬虫是从一系列的种子链接开始。种子链接是起始节点,种子页面的超链接指向的页面是子节点(中间节点),对于非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 SetextracLinks(String url, LinkFilter filter)
    {
        Setlinks = new HashSet();
        try
        {
            Parser parser = new Parser(url);
            //parser.setEncoding("utf-8");
            // 过滤 标签的 filter,用来提取 frame 标签里的 src 属性所表示的链接
            NodeFilter frameFilter = new NodeFilter()
            {
                public boolean accept(Node node)
                {
                    if (node.getText()
                        .startsWith("frame src="))
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
            };
            // OrFilter 来设置过滤 标签,和 标签
            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) // 标签
                {
                    LinkTag link = (LinkTag) tag;
                    String linkUrl = link.getLink(); // url
                    if (filter.accept(linkUrl))
                        links.add(linkUrl);
                }
                else // 标签
                {
                    // 提取 frame 里 src 属性的链接如
                    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
            Setlinks = 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"
        });
    }
}

这样一个简单的爬虫程序就完成了,java适合开发爬虫,我们在开发爬虫的时候,一定要有清晰的逻辑思维,这样才能将爬虫程序写好!最后大家如果想要了解更多java实例知识,敬请关注奇Q工具网。

推荐阅读:

jsf框架为什么很少?jsf框架生命周期是怎样的?

json语法如何执行?json语法的使用方法

java文件怎么上传?java文件上传要注意什么?