《自拍教程55》Python_批量验证1000个apk(附练手素材)

和自甴很熟 提交于 2020-04-07 19:22:07

接上一篇案例: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
也可关注“武散人”微信订阅号,随时接受文章推送。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!