Python3實現簡單可學習的手寫體識別

来源:http://www.cnblogs.com/PyLearn/archive/2017/10/20/7701476.html
-Advertisement-
Play Games

0.目錄 1. "前言" 2. "通過pymssql與資料庫的交互" 3. "通過pyqt與界面的交互" 4. "UI與資料庫的交互" 5. "最後的main主函數" 1.前言 版本:Python3.6.1 + PyQt5 + SQL Server 2012 以前一直覺得,機器學習、手寫體識別這種程 ...


0.目錄

1.前言

2.通過pymssql與資料庫的交互

3.通過pyqt與界面的交互

4.UI與資料庫的交互

5.最後的main主函數

1.前言

版本:Python3.6.1 + PyQt5 + SQL Server 2012

以前一直覺得,機器學習、手寫體識別這種程式都是很高大上很難的,直到偶然看到了這個視頻,聽了老師講的思路後,瞬間覺得原來這個並不是那麼的難,原來我還是有可能做到的。

於是我開始順著思路打算用Python、PyQt、SQLServer做一個出來,看看能不能行。然而中間遇到了太多的問題,資料庫方面的問題有十幾個,PyQt方面的問題有接近一百個,還有數十個Python基礎語法的問題。但好在,通過不斷的Google,終於湊出了這麼一個成品來。

最終還是把都湊在一個函數里的代碼重構了一下,改寫成了4個模塊:
main.py、Learning.py、LearningDB.py、LearningUI.py
其中LearningDB實現python與資料庫的交互,LearningUI實現界面的交互,Learning繼承LearningUI類添加上了與LearningDB資料庫類的交互,最後通過main主函數模塊運行程式。
其中涉及資料庫的知識可參考之前的文章:Python3操作SQL Server資料庫,涉及PyQt的知識可參考:Python3使用PyQt5製作簡單的畫板/手寫板

手寫體識別的主要思路是將手寫的字,用一個列表記錄其所經過的點,劃分為一個九宮格,然後數每個格子中點的數目,將數目轉化為所占總點數的百分比。然後兩兩保存的九維數,求他們之間的距離,距離越近代表越接近。

2.通過pymssql與資料庫的交互

因為使用程式之前先需要建表,建表我就直接使用SQL語句執行了:

create database PyLearningDB

drop table table0
create table table0
(dim0 int not null,
dim1 int not null,
dim2 int not null,
dim3 int not null,
dim4 int not null,
dim5 int not null,
dim6 int not null,
dim7 int not null,
dim8 int not null)

drop table table1
create table table1
(dim0 int not null,
dim1 int not null,
dim2 int not null,
dim3 int not null,
dim4 int not null,
dim5 int not null,
dim6 int not null,
dim7 int not null,
dim8 int not null)

drop table table2
create table table2
(dim0 int not null,
dim1 int not null,
dim2 int not null,
dim3 int not null,
dim4 int not null,
dim5 int not null,
dim6 int not null,
dim7 int not null,
dim8 int not null)

drop table table3
create table table3
(dim0 int not null,
dim1 int not null,
dim2 int not null,
dim3 int not null,
dim4 int not null,
dim5 int not null,
dim6 int not null,
dim7 int not null,
dim8 int not null)

drop table table4
create table table4
(dim0 int not null,
dim1 int not null,
dim2 int not null,
dim3 int not null,
dim4 int not null,
dim5 int not null,
dim6 int not null,
dim7 int not null,
dim8 int not null)

drop table table5
create table table5
(dim0 int not null,
dim1 int not null,
dim2 int not null,
dim3 int not null,
dim4 int not null,
dim5 int not null,
dim6 int not null,
dim7 int not null,
dim8 int not null)

drop table table6
create table table6
(dim0 int not null,
dim1 int not null,
dim2 int not null,
dim3 int not null,
dim4 int not null,
dim5 int not null,
dim6 int not null,
dim7 int not null,
dim8 int not null)

drop table table7
create table table7
(dim0 int not null,
dim1 int not null,
dim2 int not null,
dim3 int not null,
dim4 int not null,
dim5 int not null,
dim6 int not null,
dim7 int not null,
dim8 int not null)

drop table table8
create table table8
(dim0 int not null,
dim1 int not null,
dim2 int not null,
dim3 int not null,
dim4 int not null,
dim5 int not null,
dim6 int not null,
dim7 int not null,
dim8 int not null)

drop table table9
create table table9
(dim0 int not null,
dim1 int not null,
dim2 int not null,
dim3 int not null,
dim4 int not null,
dim5 int not null,
dim6 int not null,
dim7 int not null,
dim8 int not null)

LearningDB.py程式如下:

'''
    LearningDB類
    功能:定義資料庫類,包含一個學習函數learn_data和一個識別函數identify_data
    作者:PyLearn
    博客: http://www.cnblogs.com/PyLearn/
    最後修改日期: 2017/10/18
'''
import math
import pymssql

class LearningDB():

    def __init__(self):
        self.conn = pymssql.connect(host='127.0.0.1',
                                    user='sa',
                                    password='123',
                                    database='PyLearningDB',
                                    charset='utf8')
        self.cursor = self.conn.cursor()
        self.sql = ''
        self.distance = 0.0
        self.conn.close()

    def learn_data(self, table, dim):
        '''
            學習數據,將數據存到對應的資料庫
            table指定哪個表,dim是維度數組
        '''
        learn_result = False

        try:
            if table < 0 or table > 9:
                raise Exception("錯誤!table的值為%d!" % table)
            for num in dim:
                if num < 0:
                    raise Exception("錯誤!dim的值不能小於0!")

            self.conn = pymssql.connect(host='127.0.0.1',
                                        user='sa',
                                        password='123',
                                        database='PyLearningDB',
                                        charset='utf8')
            self.cursor = self.conn.cursor()
            self.sql = 'insert into table%d values(%d, %d, %d, %d, %d, %d, %d, %d, %d)' %(
                table, dim[0], dim[1], dim[2], dim[3], dim[4], dim[5], dim[6], dim[7], dim[8])
            self.cursor.execute(self.sql)
            self.conn.commit()
            learn_result = True
        except Exception as ex_learn:
            self.conn.rollback()
            raise ex_learn
        finally:
            self.conn.close()
        return learn_result

    def identify_data(self, test_data):
        '''
            識別數據,將數據一一對比,返回最接近的近似值
        '''
        try:
            table_data = []
            for i in range(10):
                table_data.append(self.__get_data(i, test_data))

            #返回table_data中最小值的索引
            return table_data.index(min(table_data))
        except Exception as ex_identify:
            raise ex_identify

    def __get_data(self, table, test_data):
        '''
            取出table表中所有數據
            並與測試數據進行比較,返回最小值
            如果table表中無數據,則全部取0
        '''
        try:
            if table < 0 or table > 9:
                raise Exception("錯誤!table的值不能為%d!" % table)
            self.conn = pymssql.connect(host='127.0.0.1',
                                        user='sa',
                                        password='123',
                                        database='PyLearningDB',
                                        charset='utf8')
            self.cursor = self.conn.cursor()
            self.sql = 'select * from table%d' % table
            self.cursor.execute(self.sql)
            receive_sql = self.cursor.fetchall()

            if not receive_sql:
                new_receive_sql = [(0, 0, 0, 0, 0, 0, 0, 0, 0)]
            else:
                new_receive_sql = receive_sql
        finally:
            self.conn.close()
        #計算最小值
        dim_data = []
        for receive_data in new_receive_sql:
            dim_data.append(self.__distance_data(test_data, receive_data))
        #返回dimData中最小值
        return min(dim_data)

    def __distance_data(self, test_data, table_data):
        '''
            求九維空間中兩點之間的距離
        '''
        self.distance = 0.0
        for i in range(9):
            self.distance += (test_data[i] - table_data[i]) ** 2
        return math.sqrt(self.distance)

3.通過pyqt與界面的交互

LearningUI.py程式如下:

'''
    LearningUI類
    功能:生成UI界面,以及定義事件處理方法
    作者:PyLearn
    博客: http://www.cnblogs.com/PyLearn/
    最後修改日期: 2017/10/18
'''
from PyQt5.QtWidgets import (QWidget, QPushButton, QLabel, QComboBox, QDesktopWidget)
from PyQt5.QtGui import (QPainter, QPen, QFont)
from PyQt5.QtCore import Qt

class LearningUI(QWidget):

    def __init__(self):
        super(LearningUI, self).__init__()

        self.__init_ui()

        #設置只有滑鼠按下時才跟蹤移動,否則不按的時候也在畫畫
        self.setMouseTracking(False)
        #self.pos_xy保存所有繪畫的點
        self.pos_xy = []
        #設置pos_x、pos_y方便計算
        self.pos_x = []
        self.pos_y = []

        #設置關聯事件
        self.btn_learn.clicked.connect(self.btn_learn_on_clicked)
        self.btn_recognize.clicked.connect(self.btn_recognize_on_clicked)
        self.btn_clear.clicked.connect(self.btn_clear_on_clicked)

    def __init_ui(self):
        '''
            定義UI界面:
            三個按鈕:學習、識別、清屏
                btn_learn、btn_recognize、btn_clear
            一個組合框:選擇0-9
                combo_table
            兩條標簽:請在屏幕空白處用滑鼠輸入0-9中的某一個數字進行識別!
                      2017/10/10 by PyLearn
            一條輸出識別結果的標簽
                label_output
        '''
        #添加三個按鈕,分別是學習、識別、清屏
        self.btn_learn = QPushButton("學習", self)
        self.btn_learn.setGeometry(50, 400, 70, 40)
        self.btn_recognize = QPushButton("識別", self)
        self.btn_recognize.setGeometry(320, 400, 70, 40)
        self.btn_clear = QPushButton("清屏", self)
        self.btn_clear.setGeometry(420, 400, 70, 40)

        #添加一個組合框,選擇0-9
        self.combo_table = QComboBox(self)
        for i in range(10):
            self.combo_table.addItem("%d" % i)
        self.combo_table.setGeometry(150, 400, 70, 40)

        #添加兩條標簽
        self.label_head = QLabel('請在屏幕空白處用滑鼠輸入0-9中的某一個數字進行識別!', self)
        self.label_head.move(75, 50)
        self.label_end = QLabel('2017/10/10 by PyLearn', self)
        self.label_end.move(375, 470)

        #添加一條輸出識別結果的標簽
        '''
        setStyleSheet設置邊框大小、顏色
        setFont設置字體大小、形狀、加粗
        setAlignment設置文本居中
        '''
        self.label_output = QLabel('', self)
        self.label_output.setGeometry(50, 100, 150, 250)
        self.label_output.setStyleSheet("QLabel{border:1px solid black;}")
        self.label_output.setFont(QFont("Roman times", 100, QFont.Bold))
        self.label_output.setAlignment(Qt.AlignCenter)

        '''
        setFixedSize()固定了窗體的寬度與高度
        self.center()將窗體居中顯示
        setWindowTitle()設置窗體的標題
        '''
        self.setFixedSize(550, 500)
        self.center()
        self.setWindowTitle('0-9手寫體識別(機器學習中的"HelloWorld!")')

    def center(self):
        '''
            視窗居中顯示
        '''
        qt_center = self.frameGeometry()
        desktop_center = QDesktopWidget().availableGeometry().center()
        qt_center.moveCenter(desktop_center)
        self.move(qt_center.topLeft())

    def paintEvent(self, event):
        '''
            首先判斷pos_xy列表中是不是至少有兩個點了
            然後將pos_xy中第一個點賦值給point_start
            利用中間變數pos_tmp遍歷整個pos_xy列表
                point_end = pos_tmp

                判斷point_end是否是斷點,如果是
                    point_start賦值為斷點
                    continue
                判斷point_start是否是斷點,如果是
                    point_start賦值為point_end
                    continue

                畫point_start到point_end之間的線
                point_start = point_end
            這樣,不斷地將相鄰兩個點之間畫線,就能留下滑鼠移動軌跡了
        '''
        painter = QPainter()
        painter.begin(self)
        pen = QPen(Qt.black, 2, Qt.SolidLine)
        painter.setPen(pen)

        if len(self.pos_xy) > 1:
            point_start = self.pos_xy[0]
            for pos_tmp in self.pos_xy:
                point_end = pos_tmp

                if point_end == (-1, -1):
                    point_start = point_end
                    continue
                if point_start == (-1, -1):
                    point_start = point_end
                    continue

                painter.drawLine(point_start[0], point_start[1], point_end[0], point_end[1])
                point_start = point_end
        painter.end()

    def mouseReleaseEvent(self, event):
        '''
            重寫滑鼠按住後鬆開的事件
            在每次鬆開後向pos_xy列表中添加一個斷點(-1, -1)
            然後在繪畫時判斷一下是不是斷點就行了
            是斷點的話就跳過去,不與之前的連續
        '''
        pos_test = (-1, -1)
        self.pos_xy.append(pos_test)

        self.update()

    def mouseMoveEvent(self, event):
        '''
            按住滑鼠移動:將移動的點加入self.pos_xy列表
        '''
        #self.pos_x和self.pos_y總是比self.pos_xy少一到兩個點,還不知道原因在哪
        self.pos_x.append(event.pos().x())
        self.pos_y.append(event.pos().y())

        #中間變數pos_tmp提取當前點
        pos_tmp = (event.pos().x(), event.pos().y())
        #pos_tmp添加到self.pos_xy中
        self.pos_xy.append(pos_tmp)

        self.update()

    def btn_learn_on_clicked(self):
        '''
            需要用到資料庫,因此在在子類中實現
        '''
        pass
    def btn_recognize_on_clicked(self):
        '''
            需要用到資料庫,因此在在子類中實現
        '''
        pass

    def btn_clear_on_clicked(self):
        '''
            按下清屏按鈕:
                將列表賦值為空
                將輸出識別結果的標簽賦值為空
            然後刷新界面,重新繪畫即可清屏
        '''
        self.pos_xy = []
        self.pos_x = []
        self.pos_y = []
        self.label_output.setText('')
        self.update()

    def get_pos_xy(self):
        '''
            將手寫體在平面上分為9個格子
            計算每個格子里點的數量
            然後點的數量轉化為占總點數的百分比
            接著返回一個數組dim[9]
            橫軸依次是min_x、min2_x、max2_x、max_x
            縱軸依次是min_y、min2_y、max2_y、max_y
        '''
        if not self.pos_xy:
            return None

        pos_count = len(self.pos_x)
        max_x = max(self.pos_x)
        max_y = max(self.pos_y)
        min_x = min(self.pos_x)
        min_y = min(self.pos_y)
        dim = [0, 0, 0, 0, 0, 0, 0, 0, 0]

        dis_x = (max_x - min_x) // 3
        dis_y = (max_y - min_y) // 3

        min2_x = min_x + dis_x
        min2_y = min_y + dis_y
        max2_x = max_x - dis_x
        max2_y = max_y - dis_y

        for i in range(len(self.pos_x)):
            if self.pos_y[i] >= min_y and self.pos_y[i] < min2_y:
                if self.pos_x[i] >= min_x and self.pos_x[i] < min2_x:
                    dim[0] += 1
                    continue
                if self.pos_x[i] >= min2_x and self.pos_x[i] < max2_x:
                    dim[1] += 1
                    continue
                if self.pos_x[i] >= max2_x and self.pos_x[i] <= max_x:
                    dim[2] += 1
                    continue
            elif self.pos_y[i] >= min2_y and self.pos_y[i] < max2_y:
                if self.pos_x[i] >= min_x and self.pos_x[i] < min2_x:
                    dim[3] += 1
                    continue
                if self.pos_x[i] >= min2_x and self.pos_x[i] < max2_x:
                    dim[4] += 1
                    continue
                if self.pos_x[i] >= max2_x and self.pos_x[i] <= max_x:
                    dim[5] += 1
                    continue
            elif self.pos_y[i] >= max2_y and self.pos_y[i] <= max_y:
                if self.pos_x[i] >= min_x and self.pos_x[i] < min2_x:
                    dim[6] += 1
                    continue
                if self.pos_x[i] >= min2_x and self.pos_x[i] < max2_x:
                    dim[7] += 1
                    continue
                if self.pos_x[i] >= max2_x and self.pos_x[i] <= max_x:
                    dim[8] += 1
                    continue
            else:
                pos_count -= 1
                continue
        #將數量轉化為所占百分比
        for num in dim:
            num = num * 100 // pos_count

        return dim

4.UI與資料庫的交互

Learning.py程式如下:

'''
    Learning類
    功能:重寫LearningUI類中的兩個用到了資料庫的方法:
         類中添加一個LearningDB類對象的數據成員self.learn_db
    作者:PyLearn
    博客: http://www.cnblogs.com/PyLearn/
    最後修改日期: 2017/10/18
'''
from PyQt5.QtWidgets import QMessageBox
from LearningUI import LearningUI
from LearningDB import LearningDB

class Learning(LearningUI):
    '''
        Learning實現btn_learn_on_clicked和btn_recognize_on_clicked兩個方法
    '''
    def __init__(self):
        super(Learning, self).__init__()

        #學習函數learn_data(table, dim)和一個識別函數identify_data(test_data)
        self.learn_db = LearningDB()

    def btn_learn_on_clicked(self):
        if not self.pos_xy:
            QMessageBox.critical(self, "註意", "請先寫入您要學習的數字!")
            return None

        #獲取要學習的數字learn_num
        learn_num = self.combo_table.currentIndex()

        #彈出確認對話框
        qbox = QMessageBox()
        qbox.setIcon(QMessageBox.Information)
        qbox.setWindowTitle("請確認")
        qbox.setText("學習數字 %d ?" % learn_num)
        qbox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
        qbox.setDefaultButton(QMessageBox.No)
        qbox.button(QMessageBox.Yes).setText("是")
        qbox.button(QMessageBox.No).setText("否")
        reply = qbox.exec()

        #判斷對話框結果,執行程式
        if reply == QMessageBox.Yes:
            learn_result = False
            learn_dim = self.get_pos_xy()
            if learn_dim:
                learn_result = self.learn_db.learn_data(learn_num, learn_dim)
            else:
                print('get_pos_xy()函數返回空值')
                return None

            if learn_result:
                QMessageBox.about(self, "提示", "學習成功!")
            else:
                QMessageBox.about(self, "提示", "學習失敗!")
        else:
            return None

    def btn_recognize_on_clicked(self):
        #如果沒有進行繪畫,警告後退出
        if not self.pos_xy:
            QMessageBox.critical(self, "註意", "請先寫入您要識別的數字!")
            return None
        else:
            recognize_num = 0
            recognize_dim = self.get_pos_xy()
            if recognize_dim:
                recognize_num = self.learn_db.identify_data(recognize_dim)
            else:
                print('recognize_dim為空')
                return None
            self.label_output.setText('%d' % recognize_num)

5.最後的main主函數

'''
    主函數main
    功能:生成Learning對象,進入主迴圈
    作者:PyLearn
    博客: http://www.cnblogs.com/PyLearn/
    最後修改日期: 2017/10/18
'''
import sys
from PyQt5.QtWidgets import QApplication
from Learning import Learning

if __name__ == '__main__':
    app = QApplication(sys.argv)

    py_learning = Learning()
    py_learning.show()

    sys.exit(app.exec_())

將以上4個程式放在同一個目錄下,直接執行main.py就行了。
運行界面如下:

學習數字4:

第一次運行需要全部學習至少一次才能有一點正確率。
識別3:


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Socket 通常被稱之為套接字,已經成為Windows,mac等操作系統共同遵守的網路編程標準。可以用它通過網路來實現不同電腦之間的通信,也可以實現相同主機內的不同進程之間的通信。用socket進行網路開發需要瞭解伺服器和客戶端的socket原語。有如下原語: socket():建立socket ...
  • Count Color Time Limit : 2000/1000ms (Java/Other) Memory Limit : 131072/65536K (Java/Other) Total Submission(s) : 6 Accepted Submission(s) : 1 ... ...
  • JavaIO基礎內容 IO技術概述 Output 把記憶體中的數據存儲到持久化設備上這個動作稱為輸出(寫)Output操作 Input 把持久設備上的數據讀取到記憶體中的這個動作稱為輸入(讀)Input操作 上面兩種操作成為IO操作 File類 File類是文件和目錄路徑名的抽象表示形式,Java中把文 ...
  • 游戲效果如下圖: 游戲下載地址: https://pan.baidu.com/s/1pLPwzBP ...
  • 題目描述 輸入n個正整數,(1<=n<=10000),要求輸出最長的連號的長度。(連號指從小到大連續自然數) 輸入輸出格式 輸入格式: 第一行,一個數n; 第二行,n個正整數,之間用空格隔開。 輸出格式: 一個數,最長連號的個數。 輸入輸出樣例 輸入樣例#1: 10 3 5 6 2 3 4 5 6 ...
  • 目背景 (本道題目木有以藏歌曲……不用猜了……) 《愛與愁的故事第一彈·heartache》最終章。 吃完pizza,月落烏啼知道超出自己的預算了。為了不在愛與愁大神面前獻醜,只好還是硬著頭皮去算錢…… 題目描述 算完錢後,月落烏啼想著:“你TMD坑我,(以下用閩南語讀)歸粒靠杯靠畝誒,(以下用英讀 ...
  • 1 狀態管理 a) 什麼是狀態管理?將瀏覽器與web伺服器之間多次交互當做一個整體處理,並且將多次交互涉及的數據(即狀態)保存下來. b) 如何進行狀態管理? c) Cookie d) 讀取CookieCookie[] request.getCookies();註:有可能返回null;String ...
  • 第二章 取代netcat 一開始對於下麵這段代碼不是太理解: 之後在網上查詢了關於socket.recv函數的詳細說明: recv先檢查套接字s的接收緩衝區,如果s接收緩衝區中沒有數據或者協議正在接收數據,那麼recv就一直等待,直到協議把數據接收完畢。當協議把數據接收完畢,recv函數就把s的接收 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...