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,那就成玄學了;每一個庫都得本身寫代碼實現追蹤的):

相關文章
相關標籤/搜索