《自拍教程55》Python 批量驗證1000個apk(附練手素材)

接上一篇案例:Python_批量下載1000個apk,我們只講瞭如何下載,
其實市場部提供的表格,不僅僅包含了apk的下載地址,還有apk的版本號,還有MD5信息等,
如何確保你下載的這1000個apk,是下載過程中未出錯,版本號對的上,MD5信息也對上?
附:市場部提供的包含apk版本號,md5信息的表。

本案例主要介紹:如何快速地實現對已經下載好的app進行批量地驗證。


準備階段
  1. 本篇只講驗證apk的版本號與md5信息,不講下載。
  2. 需要確保aapt已經成功地加入到了環境變量中去
  3. “aapt dump bagding XXX.apk | findstr version”命令可以解析某個apk的versionName信息。
  4. Windows操作系統可以用“certutil -hashfile XXX.apk MD5”命令可以計算某apk的MD5哈希值,
    MD5是用於驗證文件下載過程完整性的常用的一套計算方法,確保被下載的文件,在網絡傳輸過程中,未被篡改或者損壞。
  5. Linux操作系統可以用“md5sum XXX.apk”命令來計算某個apk的MD5哈希值。
  6. 其實Python的hashlib模塊,也可以進行MD5哈希值的計算,可不受操作系統影響。
  7. 上一篇案例,我們已經下載好了的apk是放在“downloaded_apk”文件下,os.listdir()函數可以列出文件夾下的所有apk文件。
  8. 涉及Excel讀寫操作,依舊推薦openpyxl, 需要考慮與原始Excel上的版本號及MD5值自動做對比,
    所以我們增加了2列用於做驗證對比, 如果值相同,我們回填Ok,如果值不相同,我們回填差異值並標記紅底色。

Python批處理腳本形式

記住批處理腳本的精髓:批量順序執行語句

# coding=utf-8

import os
import re
import openpyxl
from openpyxl.styles import PatternFill

# 第一步:再生成整個excel表格的字典,key是apk中文名稱,value是一個列表[所在行數, 版本號,MD5]
print("正在生成apk信息索引字典...")
apkinfo_dict = {}
apkname_col = 2
apkversion_col = 4
apkmd5_col = 6
apkversion_col_newadd = 5  # 新加的版本驗證列
apkmd5_col_newadd = 7  # 新加的MD5驗證列
error_fill = PatternFill(fill_type='solid', fgColor="FF3300")  # 標記紅色底色
excel = openpyxl.load_workbook('Top_1000_apks.xlsx')  # 讀取excel裏邊的內容
table = excel.active
rows = table.max_row
for r in range(2, rows + 1):  # 跟excel的第一行標題行無關,從第二行文字內容開始做替換工作
    apk_name = table.cell(row=r, column=apkname_col).value  # 獲取apk名稱
    apk_version = table.cell(row=r, column=apkversion_col).value  # 獲取apk名稱
    apk_md5 = table.cell(row=r, column=apkmd5_col).value  # 獲取apk名稱
    apkinfo_dict[apk_name] = [r, apk_version, apk_md5]
print(apkinfo_dict)

# 第二步:再讀取downloaded_apk文件夾下的所有文件,並進行對比及回填操作
curpath = os.getcwd()
apk_dir = os.path.join(curpath, "downloaded_apk")
apk_list = os.listdir(apk_dir)
for apk in apk_list:
    print("正在進行%s的版本和MD5值對比與回填操作..." % apk)
    apk_path = os.path.join(apk_dir, apk)
    file_name = apk.replace(".apk", "")  # 獲取apk文件名,去掉後綴
    s1 = os.popen("aapt.exe dump badging %s | findstr version" % apk_path).read()
    version_name = re.findall(r"versionName=\'(.*)\'", s1)[0]
    print(version_name)
    s2 = os.popen("certutil -hashfile %s MD5" % apk_path).read()
    md5_value = s2.splitlines()[1]
    md5_value = md5_value.replace(" ", "")
    print(md5_value)

    r = apkinfo_dict[file_name][0]  # 獲得該apk所在行號

    # 假如版本號匹配的上,回填Ok,假如匹配不上,回填新的版本號
    if version_name == apkinfo_dict[file_name][1]:
        table.cell(row=r, column=apkversion_col_newadd).value = "OK"
    else:
        table.cell(row=r, column=apkversion_col_newadd).value = version_name
        table.cell(row=r, column=apkversion_col_newadd).fill = error_fill  # 標記紅色底色

    # 假如MD5匹配的上,回填Ok,假如匹配不上,回填新的MD5
    if md5_value == apkinfo_dict[file_name][2]:
        table.cell(row=r, column=apkmd5_col_newadd).value = "OK"
    else:
        table.cell(row=r, column=apkmd5_col_newadd).value = md5_value
        table.cell(row=r, column=apkmd5_col_newadd).fill = error_fill  # 標記紅色底色

print("對比及回填結束,並保存到了New_Top_1000_apks.xlsx,請查閱...")
excel.save("New_Top_1000_apks.xlsx")
os.system("pause")

Python面向過程函數形式

面向過程函數的編程思維應該是這樣的:
你需要多少個功能(函數),才能做成這個事,
最好把功能(函數)都儘量封裝好,只暴露一些的參數接口即可。
在命令行工具熟練運用後,就可以考慮儘量用Python模塊來實現命令行工具的功能,
比如certutil或md5sum就儘量不用了,而考慮用hashlib模塊來代替,
減少對某個命令行工具的依賴,這樣可移植性更強些(減少了對操作系統的限制)。

# coding=utf-8

import os
import re
import openpyxl
from openpyxl.styles import PatternFill
import hashlib

# 定義一些本模塊(當前.py文件)可能都需要調用的“全局變量”
apkname_col = 2
apkversion_col = 4
apkmd5_col = 6
apkversion_col_newadd = 5  # 新加的版本驗證列
apkmd5_col_newadd = 7  # 新加的MD5驗證列
error_fill = PatternFill(fill_type='solid', fgColor="FF3300")  # 標記紅色底色


def parse_apk_excel(excel_file):
    '''用於生成apk信息索引字典'''
    print("正在生成apk信息索引字典...")
    apk_info_dict = {}
    excel = openpyxl.load_workbook(excel_file)  # 讀取excel裏邊的內容
    table = excel.active
    rows = table.max_row
    for r in range(2, rows + 1):  # 跟excel的第一行標題行無關,從第二行文字內容開始做替換工作
        apk_name = table.cell(row=r, column=apkname_col).value  # 獲取apk名稱
        apk_version = table.cell(row=r, column=apkversion_col).value  # 獲取apk名稱
        apk_md5 = table.cell(row=r, column=apkmd5_col).value  # 獲取apk名稱
        apk_info_dict[apk_name] = [r, apk_version, apk_md5]
    return apk_info_dict, excel, table


def get_apk_version(apk_path):
    s = os.popen("aapt.exe dump badging %s | findstr version" % apk_path).read()
    version_name = re.findall(r"versionName=\'(.*)\'", s)[0]
    print(version_name)
    return version_name


def get_apk_md5(apk_path):
    with open(apk_path, "rb") as hf:
        apk_md5 = hashlib.md5(hf.read()).hexdigest()
        print(apk_md5)
        return apk_md5


def compare_rewrite(apk_info_dict, excel, table):
    curpath = os.getcwd()
    apk_dir = os.path.join(curpath, "downloaded_apk")
    apk_list = os.listdir(apk_dir)
    for apk in apk_list:
        print("正在進行%s的版本和MD5值對比與回填操作..." % apk)
        file_name = apk.replace(".apk", "")  # 獲取apk文件名,去掉後綴
        apk_path = os.path.join(apk_dir, apk)
        version_name = get_apk_version(apk_path)
        md5_value = get_apk_md5(apk_path)
        r = apk_info_dict[file_name][0]  # 獲得該apk所在行號

        # 假如版本號匹配的上,回填Ok,假如匹配不上,回填新的版本號
        if version_name == apk_info_dict[file_name][1]:
            table.cell(row=r, column=apkversion_col_newadd).value = "OK"
        else:
            table.cell(row=r, column=apkversion_col_newadd).value = version_name
            table.cell(row=r, column=apkversion_col_newadd).fill = error_fill  # 標記紅色底色

        # 假如MD5匹配的上,回填Ok,假如匹配不上,回填新的MD5
        if md5_value == apk_info_dict[file_name][2]:
            table.cell(row=r, column=apkmd5_col_newadd).value = "OK"
        else:
            table.cell(row=r, column=apkmd5_col_newadd).value = md5_value
            table.cell(row=r, column=apkmd5_col_newadd).fill = error_fill  # 標記紅色底色

    print("對比及回填結束,並保存到了New_Top_1000_apks.xlsx,請查閱...")
    excel.save("New_Top_1000_apks.xlsx")


apk_info_dict, excel, table = parse_apk_excel("Top_1000_apks.xlsx")  # 獲取索引字典
compare_rewrite(apk_info_dict, excel, table)  # 開始對比及回填
os.system("pause")

Python面向對象類形式

面向對象類的編程思維應該是這樣的:
如果給你一個空白的世界,在這個世界裏你需要哪些種類的事物,
這些種類的事物都具備哪些共有的屬性與方法,
這些種類(類)的事物(對象),和其他種類(其他類)的事物(其他對象)有什麼關係。
儘量把這些類封裝好,只暴露對外的屬性(變量)和方法(函數)即可。

# coding=utf-8

import os
import re
import openpyxl
from openpyxl.styles import PatternFill
import hashlib

# 定義一些本模塊(當前.py文件)可能都需要調用的“全局變量”
apkname_col = 2
apkversion_col = 4
apkmd5_col = 6
apkversion_col_newadd = 5  # 新加的版本驗證列
apkmd5_col_newadd = 7  # 新加的MD5驗證列
error_fill = PatternFill(fill_type='solid', fgColor="FF3300")  # 標記紅色底色


class ExcelParser():
    def __init__(self, excel_file):
        self._excel_file = excel_file  # 沒必要暴露到外界,加_

    def parse_apk_excel(self):  # 這是需要暴露的方法(函數),不能加_
        '''用於生成apk信息索引字典'''
        print("正在生成apk信息索引字典...")
        apk_info_dict = {}
        excel = openpyxl.load_workbook(self._excel_file)  # 讀取excel裏邊的內容
        table = excel.active
        rows = table.max_row
        for r in range(2, rows + 1):  # 跟excel的第一行標題行無關,從第二行文字內容開始做替換工作
            apk_name = table.cell(row=r, column=apkname_col).value  # 獲取apk名稱
            apk_version = table.cell(row=r, column=apkversion_col).value  # 獲取apk名稱
            apk_md5 = table.cell(row=r, column=apkmd5_col).value  # 獲取apk名稱
            apk_info_dict[apk_name] = [r, apk_version, apk_md5]
        return apk_info_dict, excel, table


def get_apk_version(apk_path):
    s = os.popen("aapt.exe dump badging %s | findstr version" % apk_path).read()
    version_name = re.findall(r"versionName=\'(.*)\'", s)[0]
    print(version_name)
    return version_name


def get_apk_md5(apk_path):
    with open(apk_path, "rb") as hf:
        apk_md5 = hashlib.md5(hf.read()).hexdigest()
        print(apk_md5)
        return apk_md5


def compare_rewrite(apk_info_dict, excel, table):
    curpath = os.getcwd()
    apk_dir = os.path.join(curpath, "downloaded_apk")
    apk_list = os.listdir(apk_dir)
    for apk in apk_list:
        print("正在進行%s的版本和MD5值對比與回填操作..." % apk)
        file_name = apk.replace(".apk", "")  # 獲取apk文件名,去掉後綴
        apk_path = os.path.join(apk_dir, apk)
        version_name = get_apk_version(apk_path)
        md5_value = get_apk_md5(apk_path)
        r = apk_info_dict[file_name][0]  # 獲得該apk所在行號

        # 假如版本號匹配的上,回填Ok,假如匹配不上,回填新的版本號
        if version_name == apk_info_dict[file_name][1]:
            table.cell(row=r, column=apkversion_col_newadd).value = "OK"
        else:
            table.cell(row=r, column=apkversion_col_newadd).value = version_name
            table.cell(row=r, column=apkversion_col_newadd).fill = error_fill  # 標記紅色底色

        # 假如MD5匹配的上,回填Ok,假如匹配不上,回填新的MD5
        if md5_value == apk_info_dict[file_name][2]:
            table.cell(row=r, column=apkmd5_col_newadd).value = "OK"
        else:
            table.cell(row=r, column=apkmd5_col_newadd).value = md5_value
            table.cell(row=r, column=apkmd5_col_newadd).fill = error_fill  # 標記紅色底色

    print("對比及回填結束,並保存到了New_Top_1000_apks.xlsx,請查閱...")
    excel.save("New_Top_1000_apks.xlsx")


if __name__ == '__main__':
    e_obj = ExcelParser("Top_1000_apks.xlsx")
    apk_info_dict, excel, table = e_obj.parse_apk_excel()  # 獲取索引字典
    compare_rewrite(apk_info_dict, excel, table)  # 開始對比及回填
    os.system("pause")

本案例練手素材下載

跳轉到自拍教程官網下載素材
武散人出品,請放心下載並使用!

運行方式與效果

確保Android設備通過USB線與電腦連接了,adb設備有效連接,
以上代碼的3種實現形式都可以直接運行,比如保存爲verify_apks.py並和downloaded_apk文件夾還有Top_1000_apks.xlsx放在同一個文件夾下,
建議python verify_apks.py運行,當然也可以雙擊運行。
運行效果如下:

最終會新生成一個New_Top_1000_apks.xlsx, 其驗證及回填效果如下,
紅色的是代表實際下載下來的apk與市場部提供的Excel上的版本信息及Md5不一樣的標註。


更多更好的原創文章,請訪問官方網站:www.zipython.com
自拍教程(自動化測試Python教程,武散人編著)
原文鏈接:https://www.zipython.com/#/detail?id=f13a1efe25424b679e663a63fb64a10c
也可關注“武散人”微信訂閱號,隨時接受文章推送。

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