Python3第三方组件最新版本追踪实现

1、说明

在安全基线中有一项要求就是注意软件版本是不是最新版本,检查是不是最新版本有两方面的工做一是查看当前使用的软件版本二是当前使用软件的最新版本。在以前的“安全基线自动化扫描、生成报告、加固的实现(以Tomcat为例)”中只是作了前一项把当前使用的软件版本查出来,并无作当前使用软件的最新版本。javascript

固然其实这也是没办法的事,由于基线扫描通常而言都是离线扫描,而追踪最新版本确定须要联网查询;通常就一个应用来讲,少说也会用到几十个第三方库,咱们不如索性把最新版本追踪功能单独独立出来实现。css

追踪最新版本,直观上就只是从监测页面上把最新版本提取出来没有什么难度,但实际上总有一些坑因此仍是值得提一提;另外本身通常重思路胜于重具体形式,因此某个功能用什么函数具体怎么实现就算写时很熟过后也常常忘记每次都得从新百度,因此也须要记一记。html

 

2、新版本追踪实现难点及处理办法

2.1 确认须要的内容在哪一个请求中返回

如今愈来愈多网站是restful的,因此当你打开某个url看到的内容并不必定直接是该url的请求中返回的;而爬虫工具(如requests)只会执行给定的请求(顶多会重定向一下),并不会解析和执行javascript进行后续相关联的请求,因此咱们第一步要确认咱们要提取的内容是在哪一个请求中返回的。java

肯定的方法推荐直接使用开发者工具的Network选项卡,好比Nginx中咱们要提取最新stable版本,操做以下:python

 

2.2 禁用javascript防止页面调整

有些页面返回后会被javascript调整(好比原来在div[3]的内容),咱们前面也说过爬虫工具通常不会分析和执行javascript,若是任由浏览器执行javascript那么浏览器的内容将和爬虫工具中的内容不一致,这将会致使后续从浏览器获取的提取路径不适用于爬虫工具,因此咱们须要在浏览器中禁用javascript。react

chrome禁用方式:设置----高级----内容设置----JavaScript----已屏蔽nginx

firefox禁用方式:打开about:config----过滤出javascript.enabled项----在其上双击将其值设为falsegit

 

2.3 获取标签提取路径

提取标签一般有css selector和xpath selector两种形式。手动去分析肯定css和xpath表达式那是比较费劲的,由于好比在<body>和你的目标标签中间有一个<div>标签有id,人工去看一是代码很长不必定能注意到,二是就算注意到了你还得肯定你的目标标签是否是这个<div>的内部。github

有一有一种快速的方式能获取css和xpath表达式呢,答案是有的,chrome和firefox的开发者工具就集成了。先选中目标内容,而后在其上右键,最后“Copy XPath”便可。chrome

firefox和chrome的区别是,firefox耿直一些xpath都是绝对路径,chrome智能一些若是中间有标签有id则会使用该标签作跳板,也所以通常我的喜欢用chrome的多一些。

 

2.4 requests_html与lxml.html在xpath上的区别

我的喜欢用xpath胜于css,python实现xpath上一般有requests_html和lxml.html两种形式,我的又喜欢requests_html多一点。

一是requests_html自然与requests相集成;二是requests_html筛选出来的标签能够直接查看其html内容而lxml.html须要使用tostring(element)方法(from lxml.html import tostring)打印才能看到标签的html内容;三是requests_html筛选出标签后该标签即以当前本身html的最外层标签做为根节点,而lxml.html无论怎么筛选始终以最初的那个标签为根结点即直接使用xpath("//a")出来的不是当前节点下的全部a标签而是最始页面下的全部a标签,虽然可使用xpath(".//a")的形式做折中但老是不太符合直接的认识。

固然,能够看到在代码中咱们也用到了lxml.html,因此lxml.html也不是一无可取,其用处就是使用requests_html解析github的releases页面始终提取不到标签而lxml.html能够。

 

2.5 反爬虫----User-Agent监测

不论是爬虫仍是其余一些攻击工具,出于道义,在实现时他们都会在User-Agent标识是本身(好比requests的User-Agent形如“User-Agent: python-requests/2.22.0”)。部分网站会监测User-Agent头,若是发现不是常规的浏览器会禁止访问或返回不同的内容。

而不论是爬虫仍是其余一些攻击工具,出于实用,在实现时他们都会提供一个参数来修改User-Agent。因此应对User-Agent监测,咱们只要使用相应参数把当前工具的User-Agent修改为和正常浏览器同样的User-Agent便可。

应该来讲监测版本号只须要访问一两个页面,不会发起大量请求,因此也就不会触发大量访问时针对User-Agent的限制规则,但为了统一咱们这里仍是使用随机User-Agent的方式。

咱们这里经过给headers[“User-Agent”]赋值覆盖原先requests的User-Agent;另外注意咱们这里的用词,不是自定义的headers替换原先的headers而只是自定义的headers项去覆盖原先的headers的项,也就是说若是原先的headers存在的头若是自定义的headers中没从新赋值那该头部就仍保持原先的值(而不是就没了)。

总的代码逻辑以下:

user_agents = [
    'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.170 Safari/537.36',
    'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:60.0) Gecko/20100101 Firefox/60.0',
    'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
]

headers["User-Agent"] = select_one_user_agent()

response = session.get(url, headers=headers, verify=False)

 

2.6 反爬虫----页面内容会随时间变化

实际追踪中发现网站www.elastic.co页面标签的id值会随时间变化,好比前几分钟返回的标签的id多是“react-tabs-13165”,但过几分钟再访问可能就变成了“react-tabs-12345”。

这时若是是使用chrome获取xpath,chrome会以该id为跳板(如“//*[@id="react-tabs-13165"]/div[1]/div[1]/div[2]”),这个表达式在这几分钟多是对的,但再过几分钟就提取不到想要的内容了。我这里的处理办法是直接使用firefox的绝对路径形式。

 

2.7 请求超时处理

请求超时抛出异常是很常见的,咱们一是不能说任由异常抛出导致程序中断,二是不能说当前页面请求超时了就直接跳过;应当的作法是继续请求当前页面直到成功。

我这是使用捕获异常就再次递归的方式进行处理,推荐封装成一个函数,而后全部url请求都使用该函数实现,这样就不会有多处url请求而后多处都要进行处理了。

def get_url_response(url):
    session = HTMLSession()
    print(f"request start: {url}")
    try:
        response = session.get(url, headers=headers, verify=False)
    except:
        print(f"request error, will to try again: {url}")
        # 若是异常递归调用
        return get_url_response(url)
    print(f"request success: {url}")
    session.close()
    # 最终的出口只有这一个
    return response

 

2.8 最新版本提取的五种状况

 第一种----页面只有一个系列只有一个版本,直接提取。好比nginx,最新的稳定版本总在下图那个标签那里,每次只要提取该标签的内容便可获得最新的稳定版本。

第二种----有时间顺序有多个版本只有一个分支,直接提取第一个(或最后一个)。好比mycat只有一个分支,其最后发布的版本必定是其最新版本,因此咱们每次只要去读取其最新发布的那个位置就能获取到他的最新版本。

第三种----有时间顺序有多个版本有多个分支,取出版本字符串各系统取找到的第一个。好比ceph,从上往下第一个12.x版本就是12系列的最新版本,因此找到第一个包含"v12."字眼的直接return便可;其余13系列14系统同样。但这种方法若是新出一个系列那会漏掉(如v16系列)

 

 第四种----没有时间顺序有多个版本只有一个分支,那就从全部版本号字符串中提取出版本号进行比较,选出最大的那个版本号。版本号比较说明见“Python3版本号比较代码实现”。

第五种----没有时间顺序有多个版本有多个分支,这种状况就先筛选出系列,而后各系列进行版本号比较便可。

 

2.9 读写excel文件

python操做excel其余不懂但感受openpyxl就挺好用,下面简单说一下使用方法:

import openpyxl

# 1、文件操做
# 建立一个新的excel文件
mywb = openpyxl.Workbook()
# 打开一个已有excel文件,返回的对象与建立新excel文件同样
mywb = openpyxl.load_workbook('test.xlsx')

# 2、表操做
# 查看当前excel文件的全部表
mywb.get_sheet_names()
# 建立新表,test_sheet是新建立的表的表名
mysheet = mywb.create_sheet("test_sheet")
# 获取已有表对象,返回的对象与建立新表同样
# test_sheet改为本身要获取的表的表名
# 旧方法:mysheet = mywb.get_sheet_by_name("test_sheet")
mysheet = mywb["test_sheet"]

# 3、表内容操做
# 查看指定单元格内容,以A3单元格为例
mysheet["A3"].value
# 修改指定单元格内容,以A3单元格为例
mysheet["A3"] = "new value"

# 4、保存excel文件
# test.xlsx改为本身想要的名字
# 比较不符合认知的是,即使mywb是打开已有文件得来的也要有文件名参数
mywb.save("test.xlsx")

 

3、代码实现

3.1 目录结构说明

|
|--Common.py--公共函数文件
|
|--Config.py--配置文件
|
|--LibExample.xlsx--版本监测示例文件
|
|--LibLatestVersionTracer.py--最新版本追踪实现的主要文件,一个库一个函数,名字形如"xxx_latest_version_tracer()"
|
|--README.md--说明文件

 

3.2 环境构建

语言:Python3

依赖库:pip install requests-html lxml openpyxl

使用:python LibLatestVersionTracer.py

源代码:https://github.com/PrettyUp/LibLatestVersionTracer

 

3.3 实现效果演示

原始输入excel表格以下(其实并不从中读取数据用处只是帮助定位后边的最新版本和连接应输出到哪):

运行程序后新输出表格以下,新填写了Latest Version和Monitor URL列。(要强调我这里不是随便按格式写个库在上边,运行程序而后就得出了其最新版本和版本监测URL,那就成玄学了;每一个库都得本身写代码实现追踪的):

相关文章
相关标签/搜索