4. 爬取Maven中央仓库的爬虫程序实现

4. 爬取Maven中央仓库的爬虫程序实现

一开始计划获取maven中央仓库中所有组件文件,原以为使用wget命令或网络镜像工具就可以从https://repo.maven.apache.org/maven2/网站轻松获取。可惜理想很丰满,现实很有骨感,既然获取不到,那就自己实现一个爬虫获取。

提示:本文仅仅用于技术研究的目的,并不用于其他目的,更不得使用该技术对Maven中央库带来任何不利的影响。

4.1. 分析过程

4.1.1. 打开页面

打开仓库页面:https://repo.maven.apache.org/maven2/
在这里插入图片描述
页面上都是以目录和文件的方式展示的。

4.1.2. 查看页面源码

在这里插入图片描述
可以轻易的发现目录和文件的内容都是在id为“contents”下的a标签中。

4.1.3. 版本信息查看(在maven-metadata.xml)

不断深入某个目录,可以轻易的发现组件的版本信息都在maven-metadata.xml中进行描述。例如:

https://repo.maven.apache.org/maven2/tech/ibit/sql-builder/maven-metadata.xml 的内容。

<?xml version="1.0" encoding="UTF-8"?>
<metadata>
  <groupId>tech.ibit</groupId>
  <artifactId**>sql-builder</artifactId>
  <versioning>
   <latest>2.0</latest>
   <release>2.0</release>
   <versions>
     <**version**>1.0</version>
    <version>1.1</version>
     <version>2.0</version>
   </versions>
  <lastUpdated>20201130115230</lastUpdated>
  </versioning>
</metadata>

可见maven-metadata.xml中包含groupId,artifactId和version信息。

4.1.4. 使用Jsoup获得下载链接

   private static void findSubUrl(String url, int sleepMillis) {
        try {
            Thread.sleep(sleepMillis);//设置睡眠时间(避免ip被封)
            Document doc=null;
            boolean needreconnect=true;
            while(needreconnect) {
                try {
                    doc = Jsoup.connect(url).userAgent("Mozilla").timeout(5000).get();
                } 
                catch (SocketTimeoutException te)
                {
                    //链接超时,等待重连,10秒
                    Thread.sleep(10 * 1000);
                    //System.out.println("链接超时,等待重连,10秒");
                    logger.warn("链接超时,等待10秒重连");
                    needreconnect=true;
                    continue;
                }
                needreconnect=false;
            }

​            Elements links = doc.select("#contents a");
​            for (Element link : links) {
​                String pathorfilename = link.attr("href");
​                if (pathorfilename.equals("../")) {
​                    //上级目录,不处理
​                    continue;
​                }

​                //创建文件夹

​                //获得绝对URL
​                String absUrl = link.absUrl("href");
​                System.out.println(absUrl);
​                logger.info("{}",absUrl);

..................
}

4.2. 获取顶层目录文件

4.2.1. 设置基本配置参数

/**
 * 爬取根目录
 */
//private static final String ROOT = "https://repo.maven.apache.org/maven2/";
private static final String ROOT = "https://repo1.maven.org/maven2/";
/**
 * 硬盘存取根目录
 */
private static final String DiskROOT = "E:\\maven2\\";
/**
 * 全部顶层索引文件
 */
private static String indexfilename="maven2Indexall.txt";
/**
 * maven-metadata.xml文件名
 */
private static final String MAVEN_METADATA_XML_FILENAME = "maven-metadata.xml";
private static final Logger logger = LoggerFactory.getLogger(Laucher.class);

建立文件夹函数

private static void searchdir(String rooturl,String dir,int sleepMillis)
{
    String filePath = DiskROOT + dir;
    File f = new File(filePath);
    if (!f.exists()){
        boolean flag2 = f.mkdir();
        if (!flag2){
            logger.error("文件夹创建失败:{}", filePath);
        }
    }
    String suburl = rooturl + dir;
    findSubUrl(suburl, sleepMillis);
}

4.2.2. 对Maven仓库顶层目录进行分字母爬取

public static void main(String[] args) {
    String firstAlpaca="all"; //all 全部爬取,失败概率大,建议分字母 a,b,c...爬取
    if (args.length > 0) {
        firstAlpaca = args[0];
    }

    logger.info("Beging crawler:beging with {}",firstAlpaca);
    int sleepMillis = 100;
    String rooturl = ROOT;
   // findSubUrl(rooturl,sleepMillis); //直接爬取全部

    File file = new File(DiskROOT+indexfilename);
    try {
        BufferedReader br= new BufferedReader(new FileReader(file));
        String st;
        while ((st = br.readLine()) != null) {
            System.out.println(st);
            String dir=st.trim();
            if(firstAlpaca.equals("all")||firstAlpaca.equals("ALL"))
            {
                searchdir(rooturl, dir,sleepMillis);
            }
            else
            {
                int index = dir.toLowerCase().indexOf(firstAlpaca);
                if (index == 0) {
                    //首字母合格
                    searchdir(rooturl, dir,sleepMillis);
                }
            }
        }
    }
    catch (FileNotFoundException e)
    {
        logger.error("找不到文件:{}",indexfilename);
    }
    catch (IOException ie)
    {
        logger.error("使用文件失败:{}",indexfilename);
    }
    logger.info("End crawler");
}

4.3. 递归文件目录处理与文件保存

     /**
     * 查询子url
     * @param url         当前url
     * @param sleepMillis 睡眠毫秒数
     */
    private static void findSubUrl(String url, int sleepMillis) {
        try {
            Thread.sleep(sleepMillis);
            Document doc=null;
            boolean needreconnect=true;
            while(needreconnect){
                try {
                    doc = Jsoup.connect(url).userAgent("Mozilla").timeout(5000).get();
                } catch (SocketTimeoutException te)
                {
                    //链接超时,等待重连,10秒
                    Thread.sleep(10 * 1000);
                    //System.out.println("链接超时,等待重连,10秒");
                    logger.warn("链接超时,等待10秒重连");
                    needreconnect=true;
                    continue;
                }
                needreconnect=false;
            }

            Elements links = doc.select("#contents a");
            for (Element link : links) {
                String pathorfilename = link.attr("href");
                if (pathorfilename.equals("../")) {
                    //上级目录,不处理
                    continue;
                }
                //创建文件夹
                //获得绝对URL
                String absUrl = link.absUrl("href");
                System.out.println(absUrl);
                logger.info("{}",absUrl);

                //获得保存文件路径
                int urllen = ROOT.length();
                String pathName = absUrl.substring(urllen);
                java.util.Date day=new Date();
                SimpleDateFormat sdf= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String nowtime=sdf.format(day);
                System.out.println("["+nowtime+"]: "+pathName);
                logger.info("[{}]: {}",nowtime,pathName);

                //判断是目录还是文件
                int ret = pathorfilename.indexOf("/");
                if (ret == -1) {
                    String saveFile = DiskROOT + pathName;
                    File f1 = null;
                    //是文件,不是目录
                    //储存网络文件到硬盘
                    while(true) {
                        try {
                            f1 = new File(saveFile);
                            if (!f1.exists()) {
                                //文件不存在才下载
                                URL httpurl = new URL(absUrl);
                                BufferedInputStream bis = new BufferedInputStream(httpurl.openStream());
                                FileOutputStream fis = new FileOutputStream(saveFile);
                                byte[] buffer = new byte[1024];
                                int count = 0;
                                while ((count = bis.read(buffer, 0, 1024)) != -1) {
                                    fis.write(buffer, 0, count);
                                }
                                fis.close();
                                bis.close();
                                break;
                            }
                        } catch (IOException e) {
                            logger.error("下载文件失败:{}", saveFile);
                            if (f1.exists()) {
                                f1.delete();
                            }
                            Thread.sleep(10 * 1000);
                            //System.out.println("链接超时,等待重连,10秒");
                            logger.warn("文件下载失败,等待10秒重新下载");
                            //重新下载
                            continue;
                        }
                    }
                } else {
                    //目录
                    //创建硬盘目录
                    String filePath = DiskROOT + pathName;
                    File f2 = new File(filePath);
                    if (!f2.exists()) {
                        boolean flag2 = f2.mkdir();
                        if(!flag2) {
                            //System.out.println( "文件夹创建失败:"+filePath);
                            logger.error("创建文件失败:{}",filePath);
                        }
                    }
                    //递归处理
                    findSubUrl(absUrl, sleepMillis);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

4.4. 生成jar文件,便于使用命令行运行程序

设置Artifacts在这里插入图片描述
注意:注意MANNIFEST.MF目录一定要设置对才行,IDEA默认的有问题。
在这里插入图片描述
在这里插入图片描述

构建


在这里插入图片描述

结果:

在目录D:/SVNJava/Crawler/out/artifacts/Crawler_jar/下生成Crawler.jar文件。

4.5. 测试

命令使用方式:

java -jar Crawler.jar [a-z]

java -jar Crawler.jar a

配套设置:

文件下载目录

E:\maven2

下载索引文件:(https://repo1.maven.apache.org/maven2/首页所有文件目录列表)

E:\maven2\maven2Indexall.txt 测试文件内容如下:
在这里插入图片描述

错误日志文件目录如下:

E:\log

测试结果如下:
在这里插入图片描述
测试详细结果如下:
在这里插入图片描述
可见文件爬取成功。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

静水深流(探索者)

优化算法慢慢学习

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

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

打赏作者

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

抵扣说明:

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

余额充值