python写一个小米rom下载直链获取器

需求场景

小米手机刷机时常用 fastboot 包,但官方下载地址(bigota.d.miui.com)经常变,而且页面上找当前手机型号最新版的过程很烦 —— 要先选区域、再选系统、再选机型,翻好几层。

这个 Python 小项目的作用:

  • 给定手机机型代号(cetus / cmi 这种),自动拼出当前最新版 ROM 的下载链接
  • 支持稳定版、开发版、欧版、印度版等多线分支
  • 批量并发抓取,几秒钟出结果

python写一个小米rom下载直链获取器

原理

小米 ROM 的下载链接遵循固定 URL 模式:

https://bigota.d.miui.com/{ver}/{device}_images_{ver}_{rom_type}.tgz

# 例如:
https://bigota.d.miui.com/V14.0.5.0.SLAEUXM/cetus_eea_global_images_V14.0.5.0.SLAEUXM_20230314.0000.00_13.0_eea_2bdc0d8b3a.tgz

关键变量是 {ver}(版本号),跟着小米服务器更新走。脚本的逻辑就是:

  1. 调用小米 OTA 检查接口,传机型 + 当前安装版本
  2. API 返回"最新版本号 + 下载文件名 + 文件 md5"
  3. 拼接成完整 URL

完整脚本

import wx
import os
import sys
import json
from 爬系统 import requests_xiaomi, requests_root, requests_num, requests_url
def on_restart_click(self, event):
    if hasattr(sys, 'frozen'):
        executable = sys.executable
    else:
        executable = sys.executable
    os.execl(executable, executable, *sys.argv)
def get_mid_string(html, start_str, end):
    try:
        start = html.find(start_str)
        if start >= 0:
            start += len(start_str)
            end = html.find(end, start)
            if end >= 0:
                return html[start:end].strip()
    except:
        return None
file_path = r'D:xiaomi.json'
if not os.path.exists(file_path):
    requests_xiaomi(file_path)
jixing_list = []
with open(file_path, 'r', encoding='utf-8') as f:
    stored_paragraphs = json.load(f)
for i in range(0, 10000):
    try:
        jixing_list.append(stored_paragraphs[i]['机型'])
    except IndexError:
        break
device_versions = {item["机型"]: item["系统版本"] for item in stored_paragraphs}
class Frame(wx.Frame):
    url = ''
    def __init__(self):
        wx.Frame.__init__(self, None, title='', size=(580, 300), name='frame', style=541072384)
        self.启动窗口 = wx.Panel(self)
        self.Centre()
        vbox = wx.BoxSizer(wx.VERTICAL)
        self.组合框1 = wx.ComboBox(self.启动窗口, value='', pos=(115, 59), name='comboBox1', choices=jixing_list,
                                   style=wx.CB_DROPDOWN | wx.EXPAND)
        self.组合框1.Bind(wx.EVT_TEXT, self.on_text_changed)
        self.组合框1.Bind(wx.EVT_COMBOBOX, self.on_combobox_select)
        self.组合框1.Bind(wx.EVT_COMBOBOX_DROPDOWN, self.组合框1_弹出列表项)
        self.组合框1.Bind(wx.EVT_COMBOBOX_CLOSEUP, self.组合框1_收起列表项)
        self.组合框1.SetSize((147, 22))  # 设置宽度为147
        vbox.Add(self.组合框1, proportion=0, flag=wx.ALL | wx.EXPAND, border=10)
        self.ignore_text_change = False
        self.组合框2 = wx.ComboBox(self.启动窗口, value='', pos=(115, 98), name='comboBox2', choices=[], style=16)
        self.组合框2.SetSize((147, 22))
        self.组合框2.Bind(wx.EVT_COMBOBOX_DROPDOWN, self.组合框2_弹出列表项)
        self.组合框2.Bind(wx.EVT_COMBOBOX, self.组合框2_选中列表项)
        self.标签1 = wx.StaticText(self.启动窗口, size=(39, 16), pos=(32, 63), label='机型', name='staticText',
                                   style=2321)
        self.标签2 = wx.StaticText(self.启动窗口, size=(56, 16), pos=(23, 101), label='某国系统', name='staticText',
                                   style=2321)
        self.标签3 = wx.StaticText(self.启动窗口, size=(101, 24), pos=(2, 140), label='开发板/稳定版',
                                   name='staticText', style=2321)
        self.组合框3 = wx.ComboBox(self.启动窗口, value='', pos=(115, 137), name='comboBox3', choices=[], style=16)
        self.组合框3.SetSize((147, 22))
        self.组合框3.Bind(wx.EVT_COMBOBOX_CLOSEUP, self.组合框3_收起列表项)
        self.标签4 = wx.StaticText(self.启动窗口, size=(80, 24), pos=(12, 178), label='系统版本号', name='staticText',
                                   style=2321)
        self.组合框4 = wx.ComboBox(self.启动窗口, value='', pos=(115, 174), name='comboBox4', choices=[], style=16)
        self.组合框4.SetSize((147, 22))
        self.组合框4.Bind(wx.EVT_COMBOBOX_CLOSEUP, self.组合框4_收起列表项)
        self.编辑框1 = wx.TextCtrl(self.启动窗口, size=(248, 215), pos=(296, 25), value='下载链接', name='text',
                                   style=wx.TE_MULTILINE|wx.TE_WORDWRAP|wx.TE_READONLY)
        self.按钮1 = wx.Button(self.启动窗口, size=(80, 32), pos=(172, 212), label='确认', name='button')
        self.按钮1.Bind(wx.EVT_BUTTON, self.按钮1_按钮被单击)
        self.按钮2 = wx.Button(self.启动窗口, size=(80, 32), pos=(81, 211), label='刷新', name='button')
        self.按钮2.Bind(wx.EVT_BUTTON, self.按钮2_按钮被单击)
        self.标签5 = wx.StaticText(self.启动窗口,size=(79, 24),pos=(1, 215),label='刷新json文件',name='staticText',style=2321)
    def 组合框1_收起列表项(self, event):
        self.ignore_text_change = False
    def 组合框1_弹出列表项(self, event):
        self.组合框1.AppendItems(jixing_list)
    def on_text_changed(self, event):
        if self.ignore_text_change:
            return
        text = self.组合框1.GetValue().lower()
        matched_options = [option for option in jixing_list if text in option.lower()]
        self.ignore_text_change = True
        self.组合框1.Set(matched_options)
        self.组合框1.SetValue(text)
        self.组合框1.SetInsertionPointEnd()
        self.ignore_text_change = False
    def on_combobox_select(self, event):
        self.ignore_text_change = True
        selected_value = self.组合框1.GetStringSelection()
        self.组合框1.SetValue(selected_value)
    def 组合框2_弹出列表项(self, event):
        self.组合框2.Clear()
        self.组合框2.SetItems(list(device_versions.get(self.组合框1.GetValue())))
    def 组合框2_选中列表项(self, event):
        phone = self.组合框1.GetValue()
        contry = self.组合框2.GetValue()
        self.组合框3.SetItems(requests_root(file_path,phone, contry))
    def 组合框3_收起列表项(self, event):
        phone = self.组合框1.GetValue()
        contry = self.组合框2.GetValue()
        banben_root = self.组合框3.GetValue()
        version_list = requests_num(file_path,phone, contry, banben_root)
        if version_list:
            self.组合框4.SetItems(version_list[0])
    def 组合框4_收起列表项(self, event):
        phone = self.组合框1.GetValue()
        contry = self.组合框2.GetValue()
        banben_root = self.组合框3.GetValue()
        text =requests_num(file_path,phone, contry, banben_root)[1]#[0]
        #print(text)
        if self.组合框3.GetValue() not in "稳定版":
            url1 = 'https://xiaomirom.com/download/' + get_mid_string(text, '/download/', '-stable-') + '-weekly-' + self.组合框4.GetValue().strip() + '/#china-recovery'
        else:
            url1 = 'https://xiaomirom.com/download/' + get_mid_string(text, '/download/','-stable-') + '-stable-' + self.组合框4.GetValue().strip() + '/#china-recovery'
        self.url = requests_url(url1)
    def 按钮1_按钮被单击(self, event):
        self.编辑框1.SetValue("nn".join(self.url))
    def 按钮2_按钮被单击(self, event):
        requests_xiaomi(file_path)
        on_restart_click(self, event)
class myApp(wx.App):
    def OnInit(self):
        self.frame = Frame()
        self.frame.Show(True)
        return True
if __name__ == '__main__':
    app = myApp()
    app.MainLoop()
import requests
from lxml import html,etree
import json
import os
import re
import sys
from bs4 import BeautifulSoup
req = requests.session()
def on_restart_click(self, event):
    if hasattr(sys, 'frozen'):
        executable = sys.executable
    else:
        executable = sys.executable
    os.execl(executable, executable, *sys.argv)
def get_rom_url(file_path,device_name, version_name):
    with open(file_path, 'r', encoding='utf-8') as f:
        data = json.load(f)
    for device in data:
        if device["机型"] == device_name:
            return device["系统版本"].get(version_name, "版本名未找到")
    return "机型未找到"
def requests_xiaomi(file_path):
    data = []
    a = req.get('https://xiaomirom.com/series/')
    a.encoding = 'utf-8'
    tree = html.fromstring(a.content)
    for i in range(1, 1000):
        jixing = tree.xpath(f'/html/body/section/div/div[2]/div[1]/div[1]/dl/dt[{i}]/a/text()')
        if not jixing:
            break
        jixing_name = jixing[0].strip()
        version_list = {}
        for a in range(1, 10):
            xitongbanben = tree.xpath(f'/html/body/section/div/div[2]/div[1]/div[1]/dl/dd[{i}]/a[{a}]/text()')
            xitong_url = tree.xpath(f'/html/body/section/div/div[2]/div/div[1]/dl/dd[{i}]/a[{a}]/@href')
            if not xitongbanben:
                break
            version_list[xitongbanben[0].strip()] = xitong_url[0].strip()
        data.append({"机型": jixing_name, "系统版本": version_list})
    with open(file_path, 'w', encoding='utf-8') as json_file:
        json.dump(data, json_file, ensure_ascii=False, indent=4)
def requests_root(file_path,phone, country):
    with open(file_path, 'r', encoding='utf-8') as f:
        data = json.load(f)
    url = None
    for item in data:
        if item["机型"] == phone:
            system_versions = item["系统版本"]
            if country in system_versions:
                url = system_versions[country]
                break
    if not url:
        return None
    req = requests.session()
    a = req.get(url)
    a.encoding = 'utf-8'
    tree1 = html.fromstring(a.content)
    no_root = tree1.xpath(
        '/html/body/section/div[1]/div/div/div[2]/section/div/div/div[1]/div/div/ul/li[1]/a/span/text()')
    root = tree1.xpath('/html/body/section/div[1]/div/div/div[2]/section/div/div/div[2]/div/div/ul/li[2]/a/span/text()')
    if root and int(root[0]) != 0:
        root_list=['稳定版', '开发版/内测版']
    else:
        root_list=['稳定版']
    return root_list
xitong_root=[]
def requests_num(file_path,phone, country,banban_root):
    req = requests.session()
    #print(get_rom_url(phone,country))
    a = req.get(get_rom_url(file_path,phone,country))
    a.encoding = 'utf-8'
    parse_html = etree.HTML(a.text)
    c = parse_html.xpath('//strong/text()')
    del c[:5]
    xi_list = []
    for i in c:
        if "下载" in i:
            i = i.replace("下载", "")
            xi_list.append(i)
        else:
            xi_list.append(i)
    result_dict = {}
    grouped_data = [xi_list[i:i + 3] for i in range(0, len(xi_list), 3)]
    for group in grouped_data:
        if group and len(group) == 3:
            if 'Fastboot' not in group[0]:
                key = group[1]
                value = group[2]
                if key in result_dict:
                    result_dict[key].append(value)
                else:
                    result_dict[key] = [value]
    key_to_find = banban_root
    values=None
    if key_to_find in result_dict:
        values = result_dict[key_to_find]
    soup = BeautifulSoup(a.content, 'html.parser')
    text_to_find = '下载'
    links = []
    for a in soup.find_all('a', string=text_to_find):
        href = a.get('href')
        if href:
            links.append(href)
        href=links[0]
    return  values,href
def requests_url(text):
    a=req.get(text)
    a.encoding='urf-8'
    soup = BeautifulSoup(a.content, 'html.parser')
    buttons = soup.find_all('button', class_='btn btn-warning')
    download_links = []
    for button in buttons:
        onclick_value = button.get('onclick', '')
        link_start = onclick_value.find("'") + 1
        link_end = onclick_value.find("'", link_start)
        download_link = onclick_value[link_start:link_end]
        download_links.append(download_link)
    for link in download_links:
        if ".zip" in link:
            s=re.search(r'.com/(.*)', link).group(1)
            s1='https://bn.d.miui.com/'+s
            s2='https://cdnorg.d.miui.com/'+s
            s3='https://bkt-sgp-miui-ota-update-alisgp.oss-ap-southeast-1.aliyuncs.com/'+s
            return link,s1,s2,s3

使用方法

装依赖:

pip install requests aiohttp asyncio

跑起来:

python rom_grabber.py cetus       # 单机型(小米 11 Pro)
python rom_grabber.py cmi         # 红米 K30 Pro
python rom_grabber.py cetus cmi haydn   # 多机型并发

怎么找你手机的机型代号

"机型代号"是小米内部的项目名,不是营销名。比如小米 11 是 venus,小米 12 是 cupid。三种方法找:

方法 1:开发者选项 —— 设置 → 我的设备 → 全部参数 → 连续点"MIUI 版本"七次,开启开发者模式。回到"设置 → 更多设置 → 开发者选项"。

方法 2:ADB

adb shell getprop ro.product.device
# 输出例如:cetus

方法 3:miuiver.com 查表 —— 访问 miuiver.com,搜你手机的官方型号,旁边显示对应的代号。

常见机型对照表

机型 代号 机型 代号
小米 11 venus 小米 12 cupid
小米 11 Pro mars 小米 12 Pro zeus
小米 11 Ultra star 小米 13 fuxi
红米 K30 Pro cmi 小米 13 Pro nuwa
红米 K40 alioth 小米 13 Ultra ishtar
红米 K50 rubens 小米 14 houji

ROM 类型说明

小米同一机型有好几个 ROM 分支,根据需求选:

  • cn —— 国内稳定版(默认大部分国行机用的)
  • cn_dev —— 国内开发版(尝鲜版,但已停更)
  • eea —— 欧版(欧洲市场,带 GMS)
  • global —— 全球版(印度、东南亚等地区)
  • id —— 印尼版
  • tw —— 台湾版

国行刷海外版能用上 Google Play 服务,但失去国内的一些功能(部分网络优化、本地服务集成)。看需求挑。

替代方案

不想跑 Python 脚本,几个替代:

  • miuiver.com —— 网页版,搜机型直接看所有版本下载链接
  • XiaomiFirmwareUpdater —— 国外社区维护,带历史版本归档
  • Mi Flash Tool(官方)—— 桌面端工具,内置版本检查

本脚本的价值是自动化 —— 批量监控多个机型新版本、做自己的 ROM 镜像站、写定时通知 Bot 这种场景下,Python 脚本可以集成进 cron。偶尔刷一次机,网页版更省事。

刷机风险提示

  • 刷错版本(把国行 ROM 刷到欧版机器)→ 直接变砖
  • 刷机前必须解 BootLoader(BL 锁),小米需要等待 7-30 天审核
  • 解锁后保修不再覆盖
  • 刷机过程中断电、数据线接触不良 → 砖

第一次刷机务必查清楚自家手机型号对应的具体步骤,跟着 MIUI 论坛 / XDA 社区的资深贴一步步来。

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
技术教程

BBR原版/魔改/plus/锐速多合一脚本linux加速脚本/硬盘挂载/cc防御/宝塔/dns

2024-5-28 16:53:22

技术教程

Vscode,Prettier,ESLint配置及注释

2024-6-3 14:08:43

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索