jsoup爬虫,爬取全站代码

最近使用jsoup扒了几个网站,感觉bug改的差不多了,于是写出来与大家分享.

首先我会把爬虫基础的爬取思路与部分重要方法展示出来,最后我会把全部代码贴出来.并且我会写一个Main类,里面就是爬虫的模板,改一下目标网站的url和一些有注释的参数就行了

maven依赖:

jsoup爬虫需要maven的依赖包:

<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.10.3</version>
</dependency>

爬取思路:

1.使用爬虫爬取一个连接的html代码对于我们jsoup很容易:

private Element getElement(String url) {
        try {
            return Jsoup.connect(url)
                    .userAgent("Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)")
                    .timeout(3000)
                    .get();
        } catch (Exception e) {
        }
        return null;
    }
这样,就可以把url连接的全部代码爬下来,然后返回一个Element类
2.然后就是分析目标中的<a>标签了,a标签的href属性就是我们要继续爬取的敲门砖:

Elements linksElements = element.select("a[href]");//得到所有的a标签的链接
                for (Element oneLink : linksElements) {
                    
                }
这里,可以直接得到一个可以循环执行的linksElements对象,从循环体中的oneLink提取出有效的url:

String url = oneLink.attr("abs:href");
这就得到了目标网站有效的url.

然后需要做的就是判断这个链接是否被扒过,所以我使用了一个HashMap来检测,把url作为key,这样就可以判断是否遇到相同的key.

private HashMap<String, Integer> links;//用来记录所有的链接与层次,防止出现重复

那么总体思路说完了,我们要怎么拿到最后的运行结果呢?

答案 : 我仍旧使用了HashMap<String, HashMap<Integer, String>>,爬取结束后,所有的结果都会保存在这里,这个的含义是< 域名 , < 层数 , HTML代码 > >.

域名就是目标url啦,层数是指我们递归爬取的时候在第几次递归时找到它的,HTML代码就是它的目标代码.在Main函数里我还会把它们都提取出来,让大家可以直接使用就好了.

重要方法:

public Spider2(String url,int level)

这是构造函数,这里的url就是指定网站的url,这里的level是指递归爬取的层数,如果确定要爬取整站代码,不但要设置level为一个很大的数,而且还有一点需要注意,后面会有警告,请认真阅读,防止目标网站有外站连接,这样就尴尬了

public Spider2(String url, int level) {
        if (!url.equals("") && level >= 0) {
            this.links = new HashMap<String, Integer>();
            this.url = url;
            this.level = level;
            this.allHtml = new HashMap<String, HashMap<Integer, String>>();
            this.onlyGetDown = false;
            this.topUrl = "";
        }
    }

HashMap< String , HashMap < Integer , String > > run()

这是最核心的代码,没有特殊需求,直接运行这个代码就可以拿到整站代码了.

如果有任何错误,比如目标网站某些网页有403或404等问题,都会记录在工作目录的一个文件"exception.txt"下,方便爬取完成后看到错误反馈

public HashMap<String, HashMap<Integer, String>> run() {
        try {
            Element element = getElement(url);
            links.put(url, 0);
            getLevelLinks(element, 0, url);
            return allHtml;
        } catch (Exception e) {
            /*将错误的信息打印到当前目录的 exception.txt 文件中*/
            try {
                FileOutputStream os = new FileOutputStream("exception.txt", true);
                os.write(e.toString().getBytes("utf-8"));
                os.write("\r\n".getBytes());
            } catch (Exception e1) {
                System.err.println(e1);
            }
        }
        return null;
    }

void setOnlyGetDown()

void setOnlyGetDown(String topUrl)

这是为了有针对性的爬取指定规模的代码而定制的:

    /*只从指定的部分url路径往下爬去*/
    /*例如url为 : http://www.??.com/a/b/xxxx.html
    * 所有的爬去网页都会是 : http://www.??.com/a/......*/
    public void setOnlyGetDown() {
        this.setOnlyGetDown("", true);
    }

    public void setOnlyGetDown(String topUrl) {
        this.setOnlyGetDown(topUrl, false);
    }

    private void setOnlyGetDown(String topUrl, boolean auto) {
        this.onlyGetDown = true;
        if (auto) {
            String[] strings = url.split("/");
            {
                System.out.println((strings[strings.length - 1]).length());//12
                System.out.println((strings[strings.length - 2]).length());//2
            }
            int length = url.length() - strings[strings.length - 1].length() - strings[strings.length - 2].length() - 1;
            this.topUrl = url.substring(0, length);
        } else {
            this.topUrl = topUrl;
        }
    }
注意:
如果要爬取整站代码:

  1. 构造函数中 level 为一个较大值
  2. setOnlyGetDown(String topUrl) 最好使用这个方法,设置一个目标网站的顶级域名,防止爬到外站的连接
全部代码:
使用爬虫的模板类

Main:

import java.util.HashMap;
import java.util.Map;

public class Main {

    public static void main(String[] args) {

        /*1.爬取一大堆html网页或是目标网站的一些网页*/
        String[] urlLists = new String[]{//这里是所有需要爬取的网页
                "http://blog.csdn.net/experts.html",
        };


        for (String anUrl : urlLists) {
            Spider2 spider = new Spider2(anUrl, 3);//会向下寻找3层
            /*下面就可以得到 allHtml : < url , < 当前url所处的层 , HTML代码 > >*/
            spider.setOnlyGetDown();
            HashMap<String, HashMap<Integer, String>> allHtml = spider.run();//存放着N层的全部html代码
            for (Object firstMap : allHtml.entrySet()) {
                Map.Entry entry = (Map.Entry) firstMap;

                String key = (String) entry.getKey();//得到url

                /*得到 levHtml 与*/
                Map<Integer, String> levHtml = allHtml.get(key);
                for (Object scendMap : levHtml.entrySet()) {
                    Map.Entry entry1 = (Map.Entry) scendMap;

                    int level = (Integer) entry1.getKey(); //得到层数level

                    String html = (String) entry1.getValue();//得到html

                }
            }
        }
        
        /*2.爬取整站代码*/
        Spider2 spider = new Spider2(urlLists[0], 888888);
        spider.setOnlyGetDown("http://blog.csdn.net/");//爬取目标网站的顶级,防止外部的链接
        HashMap<String, HashMap<Integer, String>> allHtml = spider.run();//存放着目标网站所有能够找到的html代码
        //然后就可以做一些事情了,吼吼吼~~~~
    }
}
真正的爬虫类

Splider2:

import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;

public class Spider2 {

    private HashMap<String, HashMap<Integer, String>> allHtml;//最终要得到的东西<url,<层级,html>>
    private String url;//传入的url
    private HashMap<String, Integer> links;//用来记录所有的链接与层次,防止出现重复
    private int level;//递归的遍历多少层
    private boolean onlyGetDown;//遍历层次时是否只选取指定域名以下的域名
    private String topUrl;//遍历时的顶级域名

    public HashMap<String, HashMap<Integer, String>> run() {
        try {
            Element element = getElement(url);
            links.put(url, 0);
            getLevelLinks(element, 0, url);
            return allHtml;
        } catch (Exception e) {
            /*将错误的信息打印到当前目录的 exception.txt 文件中*/
            try {
                FileOutputStream os = new FileOutputStream("exception.txt", true);
                os.write(e.toString().getBytes("utf-8"));
                os.write("\r\n".getBytes());
            } catch (Exception e1) {
                System.err.println(e1);
            }
        }
        return null;
    }

    /*递归的得到所有目标代码,并且将他们保存在 allHtml 中*/
    private void getLevelLinks(final Element element, final int level, final String majorUrl) throws IOException {
//        < url , < 层级 ,  html > >
        if (element != null) {
            allHtml.put(majorUrl, new HashMap<Integer, String>() {
                {
                    put(level, element.html());
                }
            });
            if (level < this.level) {
                Elements linksElements = element.select("a[href]");//得到所有的a标签的链接
                for (Element oneLink : linksElements) {
                    String url = oneLink.attr("abs:href");//抽取得到url
                    if (!onlyGetDown || (onlyGetDown && url.contains(topUrl))) {
                        if (!links.containsKey(url)) {//如果url没有重复,就要爬去
                            links.put(url, level);
                            getLevelLinks(getElement(url), level + 1, url);
                        }
                    }
                }
                //得到 links <String 超链接 , String 超链接的文字>
            }
        }
    }

    /*只从指定的部分url路径往下爬去*/
    /*例如url为 : http://www.??.com/a/b/xxxx.html
    * 所有的爬去网页都会是 : http://www.??.com/a/......*/
    public void setOnlyGetDown() {
        this.setOnlyGetDown("", true);
    }

    public void setOnlyGetDown(String topUrl) {
        this.setOnlyGetDown(topUrl, false);
    }

    private void setOnlyGetDown(String topUrl, boolean auto) {
        this.onlyGetDown = true;
        if (auto) {
            String[] strings = url.split("/");
            {
                System.out.println((strings[strings.length - 1]).length());//12
                System.out.println((strings[strings.length - 2]).length());//2
            }
            int length = url.length() - strings[strings.length - 1].length() - strings[strings.length - 2].length() - 1;
            this.topUrl = url.substring(0, length);
        } else {
            this.topUrl = topUrl;
        }
    }

    /*唯一一个需要联网爬代码的方法*/
    private Element getElement(String url) {
        try {
            return Jsoup.connect(url)
                    .userAgent("Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)")
                    .timeout(3000)
                    .get();
        } catch (Exception e) {
        }
        return null;
    }

    public Spider2(String url, int level) {
        if (!url.equals("") && level >= 0) {
            this.links = new HashMap<String, Integer>();
            this.url = url;
            this.level = level;
            this.allHtml = new HashMap<String, HashMap<Integer, String>>();
            this.onlyGetDown = false;
            this.topUrl = "";
        }
    }
}
为什么这里叫做Splider2呢?因为Splider1是调试专用类呀.......
注意爬取代码的时候要适可而止哟~~

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

呆萌的代Ma

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值