使用python爬取jar包、npm包、go包的最新版本

場景

公司內網有maven倉庫,掃描之後發現很多組件有漏洞,主要是因爲版本太老。因此需要將這些漏洞組件的最新版導入內部的maven私服。

問題

這麼多漏洞組件,一個個去中央倉庫找最新版,顯然不科學。因此要整個腳本來做這件事情。

方案

  1. 衆所周知,jar包的中央倉庫是https://mvnrepository.com/,但是這個網站有反爬蟲機制,使用腳本發出的請求不會得到響應結果。替代方案是Maven Central Repository Search,這也是官方提供的,“Official search by the maintainers of Maven Central Repository”。更令人激動的是,這個網站提供了REST API!不用你費盡心機的去解析網頁,人家直接給你接口。
  2. npm包,訪問"https://registry.npmjs.org/{npm_package}/latest",從該頁面可以抓取最新版。
  3. go module,訪問"https://pkg.go.dev/{go_pkg }?tab=versions",從該頁面可以抓取最新版。
  4. 這就萬事大吉了嗎?並不!這裏有個大坑:什麼是最新版本?上面這些網站按照時間戳排序返回的最新版,不一定是我們需要的。比如4.0.0-RC2.3.183.2.53個版本,按時間排序,不穩定的4.0.0-RC和太老的2.3.18排在了前面。但我們需要的是3.2.5版本。這就涉及到版本號排序算法。關於版本號排序,python官方是支持的。但支持有限:(1)不能處理超過3位的版本號,1.2.3.4這種會報錯;(2)不能處理字母,如2.12.2-Final2.12.RELEASE。因此,需要做一些額外處理,將滿足需求的最新版本摘出來。這個有點難,有些開發者提供的包版本號命名不規範,特別長的、帶日期的、帶特殊字符的、alpha、beta的等等都有。

代碼

可能需要的一些包:

import math
import re
import traceback
from typing import Dict, List
import requests
from bs4 import BeautifulSoup

from distutils.version import StrictVersion
from natsort import natsorted
from soupsieve import match

獲取jar包的最新版本:

def process_maven(group_id, artifact_id):
    try:
        url = "https://search.maven.org/solrsearch/select?q=g:{} a:{}&core=gav&rows=5&wt=json".format(group_id, artifact_id)
        response = requests.get(url)
        json = response.json()
        docs = json["response"]["docs"]
        versions = []
        for doc in docs:
            version = str(doc["v"])
            if ("alpha" in version.lower() or "beta" in version.lower() or "dev" in version.lower() or "rc" in version.lower() ):
                continue
            else:
                versions.append(version)
        if len(versions) == 0:
            print("沒有找到符合要求的版本,g={}, a={}".format(group_id, artifact_id))
            continue
        latest_version = get_lastest_version(versions)

        return latest_version
    except Exception as e:
        traceback.print_exc()
        print("error: %s. artifact_id=%s." % (e, artifact_id))
        print("response:", response)


def get_lastest_version(versions: List[str]) -> str:
    d = dict()
    for version in versions:  # version=4.3.10-RELEASE
        # 這種做法有問題  1.1.73.android: invalid version number '1.1.73.0000000'
        # fix_version = re.sub(r"[a-zA-Z-]", "0", version)

        # 最多隻取前三位 1.2.3.4 -> 1.2.3
        fix_version = version
        if version.count(".") > 2:
            i = version.rfind(".")
            fix_version = version[:i]

        # 1.2.Final -> 1.2.
        a = re.findall(r"[0-9.]", fix_version)
        # 1.2. -> 1.2
        if a[-1] == ".":
            a.pop()
        fix_version = "".join(a)

        # 排除掉以日期命名的版本號,這種版本號不正式,如 20030418.083655
        if len(fix_version.split(".")[0]) >= 8:
            continue

        # 31.1-jre 31.1-android
        if fix_version not in d:
            d[fix_version] = version

    l = sorted(d, key=StrictVersion)
    return d[l[-1]]

獲取npm包的最新版本:

def process_npm(npm):
    try:
        response = requests.get(url="https://registry.npmjs.org/" + npm + "/latest").json()
        version = response["version"]
        return version
    except Exception as e:
        print("error: %s. npm=%s." % (e, npm))
        print("response:", response)

獲取go module的最新版本:

def process_go(go_pkg):
    try:
        response = requests.get(url="https://pkg.go.dev/" + go_pkg + "?tab=versions").content
        soup = BeautifulSoup(response, "html.parser")
        version_str = soup.find("a", class_="js-versionLink")
        version = version_str.contents[0]
        return version
    except Exception as e:
        print("error: %s. go_pkg=%s." % (e, go_pkg))
        print("response:", response)

本文同步發佈於:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章