python採用 多進程/多線程/協程 寫爬蟲以及性能對比,牛逼的分分鐘就將一個網站爬下來!

来源:http://www.cnblogs.com/huangguifeng/archive/2017/10/06/7632799.html
-Advertisement-
Play Games

首先我們來瞭解下python中的進程,線程以及協程! 從電腦硬體角度: 電腦的核心是CPU,承擔了所有的計算任務。一個CPU,在一個時間切片里只能運行一個程式。 從操作系統的角度: 進程和線程,都是一種CPU的執行單元。 進程:表示一個程式的上下文執行活動(打開、執行、保存...) 線程:進程執 ...


 

首先我們來瞭解下python中的進程,線程以及協程!

從電腦硬體角度:

電腦的核心是CPU,承擔了所有的計算任務。
一個CPU,在一個時間切片里只能運行一個程式。

 

從操作系統的角度:

進程和線程,都是一種CPU的執行單元。

進程:表示一個程式的上下文執行活動(打開、執行、保存...)

線程:進程執行程式時候的最小調度單位(執行a,執行b...)

一個程式至少有一個進程,一個進程至少有一個線程。

 

並行 和 併發:


並行:多個CPU核心,不同的程式就分配給不同的CPU來運行。可以讓多個程式同時執行。

cpu1 -------------
cpu2 -------------
cpu3 -------------
cpu4 -------------

併發:單個CPU核心,在一個時間切片里一次只能運行一個程式,如果需要運行多個程式,則串列執行。

cpu1  ----  ----

cpu1    ----  ----

 


多進程/多線程:
表示可以同時執行多個任務,進程和線程的調度是由操作系統自動完成。


進程:每個進程都有自己獨立的記憶體空間,不同進程之間的記憶體空間不共用。
進程之間的通信有操作系統傳遞,導致通訊效率低,切換開銷大。

線程:一個進程可以有多個線程,所有線程共用進程的記憶體空間,通訊效率高,切換開銷小。

共用意味著競爭,導致數據不安全,為了保護記憶體空間的數據安全,引入"互斥鎖"。

一個線程在訪問記憶體空間的時候,其他線程不允許訪問,必須等待之前的線程訪問結束,才能使用這個記憶體空間。

互斥鎖:一種安全有序的讓多個線程訪問記憶體空間的機制。

 

Python的多線程:

GIL 全局解釋器鎖:線程的執行許可權,在Python的進程里只有一個GIL。

一個線程需要執行任務,必須獲取GIL。

好處:直接杜絕了多個線程訪問記憶體空間的安全問題。
壞處:Python的多線程不是真正多線程,不能充分利用多核CPU的資源。

但是,在I/O阻塞的時候,解釋器會釋放GIL。


所以:

多進程:密集CPU任務,需要充分使用多核CPU資源(伺服器,大量的並行計算)的時候,用多進程。 multiprocessing
缺陷:多個進程之間通信成本高,切換開銷大。


多線程:密集I/O任務(網路I/O,磁碟I/O,資料庫I/O)使用多線程合適。
threading.Thread、multiprocessing.dummy
缺陷:同一個時間切片只能運行一個線程,不能做到高並行,但是可以做到高併發。


協程:又稱微線程,在單線程上執行多個任務,用函數切換,開銷極小。不通過操作系統調度,沒有進程、線程的切換開銷。genvent,monkey.patchall

多線程請求返回是無序的,那個線程有數據返回就處理那個線程,而協程返回的數據是有序的。

缺陷:單線程執行,處理密集CPU和本地磁碟IO的時候,性能較低。處理網路I/O性能還是比較高.

 

下麵以這個網站為例,採用三種方式爬取。爬取前250名的電影。。

https://movie.douban.com/top250?start=0

 通過分析網頁發現第2頁的url start=25,第3頁的url start=50,第3頁的start=75。因此可以得出這個網站每一頁的數局是通過遞增start這個參數獲取的。

一般不看第一頁的數據,第一頁的沒有參考價值。

 

這次我們主要爬取,電影名字跟評分。只是使用不同方式去對比下不同點,所以數據方面就不過多提取或者保存。只是簡單的將其爬取下列印出來看看。

第一:採用多進程 , multiprocessing 模塊。 當然這個耗時更網路好壞有關。在全部要請求都正常的情況下耗時15s多。

 

#!/usr/bin/env python2
# -*- coding=utf-8 -*-

from multiprocessing import Process, Queue

import time
from lxml import etree
import requests


class DouBanSpider(Process):
    def __init__(self, url, q):
        # 重寫寫父類的__init__方法
        super(DouBanSpider, self).__init__()
        self.url = url
        self.q = q
        self.headers = {
            'Host': 'movie.douban.com',
            'Referer': 'https://movie.douban.com/top250?start=225&filter=',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.104 Safari/537.36',
        }

    def run(self):
        self.parse_page()

    def send_request(self,url):
        '''
        用來發送請求的方法
        :return: 返回網頁源碼
        '''
        # 請求出錯時,重覆請求3次,
        i = 0
        while i <= 3:
            try:
                print u"[INFO]請求url:"+url
                return requests.get(url=url,headers=self.headers).content
            except Exception as e:
                print u'[INFO] %s%s'% (e,url)
                i += 1

    def parse_page(self):
        '''
        解析網站源碼,並採用xpath提取 電影名稱和平分放到隊列中
        :return:
        '''
        response = self.send_request(self.url)
        html = etree.HTML(response)
        # 獲取到一頁的電影數據
        node_list = html.xpath("//div[@class='info']")
        for move in node_list:
            # 電影名稱
            title = move.xpath('.//a/span/text()')[0]
            # 評分
            score = move.xpath('.//div[@class="bd"]//span[@class="rating_num"]/text()')[0]
           
            # 將每一部電影的名稱跟評分加入到隊列
            self.q.put(score + "\t" + title)


def main():
    # 創建一個隊列用來保存進程獲取到的數據
    q = Queue()
    base_url = 'https://movie.douban.com/top250?start='
    # 構造所有url
    url_list = [base_url+str(num) for num in range(0,225+1,25)]

    # 保存進程
    Process_list = []
    # 創建並啟動進程
    for url in url_list:
        p = DouBanSpider(url,q)
        p.start()
        Process_list.append(p)
    
    # 讓主進程等待子進程執行完成
    for i in Process_list:
        i.join()

    while not q.empty():
        print q.get()

if __name__=="__main__":
    
    start = time.time()
    main()
    print '[info]耗時:%s'%(time.time()-start)
Process多進程實現
#!/usr/bin/env python2
# -*- coding=utf-8 -*-

from multiprocessing import Process, Queue

import time
from lxml import etree
import requests


class DouBanSpider(Process):
    def __init__(self, url, q):
        # 重寫寫父類的__init__方法
        super(DouBanSpider, self).__init__()
        self.url = url
        self.q = q
        self.headers = {
            'Host': 'movie.douban.com',
            'Referer': 'https://movie.douban.com/top250?start=225&filter=',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.104 Safari/537.36',
        }

    def run(self):
        self.parse_page()

    def send_request(self,url):
        '''
        用來發送請求的方法
        :return: 返回網頁源碼
        '''
        # 請求出錯時,重覆請求3次,
        i = 0
        while i <= 3:
            try:
                print u"[INFO]請求url:"+url
                return requests.get(url=url,headers=self.headers).content
            except Exception as e:
                print u'[INFO] %s%s'% (e,url)
                i += 1

    def parse_page(self):
        '''
        解析網站源碼,並採用xpath提取 電影名稱和平分放到隊列中
        :return:
        '''
        response = self.send_request(self.url)
        html = etree.HTML(response)
        # 獲取到一頁的電影數據
        node_list = html.xpath("//div[@class='info']")
        for move in node_list:
            # 電影名稱
            title = move.xpath('.//a/span/text()')[0]
            # 評分
            score = move.xpath('.//div[@class="bd"]//span[@class="rating_num"]/text()')[0]
           
            # 將每一部電影的名稱跟評分加入到隊列
            self.q.put(score + "\t" + title)


def main():
    # 創建一個隊列用來保存進程獲取到的數據
    q = Queue()
    base_url = 'https://movie.douban.com/top250?start='
    # 構造所有url
    url_list = [base_url+str(num) for num in range(0,225+1,25)]

    # 保存進程
    Process_list = []
    # 創建並啟動進程
    for url in url_list:
        p = DouBanSpider(url,q)
        p.start()
        Process_list.append(p)
    
    # 讓主進程等待子進程執行完成
    for i in Process_list:
        i.join()

    while not q.empty():
        print q.get()

if __name__=="__main__":
    
    start = time.time()
    main()
    print '[info]耗時:%s'%(time.time()-start)

  

 

 

 

  採用多線程時,耗時10.4s

 

#!/usr/bin/env python2
# -*- coding=utf-8 -*-

from threading import Thread
from Queue import Queue
import time
from lxml import etree
import requests


class DouBanSpider(Thread):
    def __init__(self, url, q):
        # 重寫寫父類的__init__方法
        super(DouBanSpider, self).__init__()
        self.url = url
        self.q = q
        self.headers = {
            'Cookie': 'll="118282"; bid=ctyiEarSLfw; ps=y; __yadk_uid=0Sr85yZ9d4bEeLKhv4w3695OFOPoedzC; dbcl2="155150959:OEu4dds1G1o"; as="https://sec.douban.com/b?r=https%3A%2F%2Fbook.douban.com%2F"; ck=fTrQ; _pk_id.100001.4cf6=c86baf05e448fb8d.1506160776.3.1507290432.1507283501.; _pk_ses.100001.4cf6=*; __utma=30149280.1633528206.1506160772.1507283346.1507290433.3; __utmb=30149280.0.10.1507290433; __utmc=30149280; __utmz=30149280.1506160772.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utma=223695111.1475767059.1506160772.1507283346.1507290433.3; __utmb=223695111.0.10.1507290433; __utmc=223695111; __utmz=223695111.1506160772.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); push_noty_num=0; push_doumail_num=0',
            'Host': 'movie.douban.com',
            'Referer': 'https://movie.douban.com/top250?start=225&filter=',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.104 Safari/537.36',
        }

    def run(self):
        self.parse_page()

    def send_request(self,url):
        '''
        用來發送請求的方法
        :return: 返回網頁源碼
        '''
        # 請求出錯時,重覆請求3次,
        i = 0
        while i <= 3:
            try:
                print u"[INFO]請求url:"+url
                html = requests.get(url=url,headers=self.headers).content
            except Exception as e:
                print u'[INFO] %s%s'% (e,url)
                i += 1
            else:
                return html

    def parse_page(self):
        '''
        解析網站源碼,並採用xpath提取 電影名稱和平分放到隊列中
        :return:
        '''
        response = self.send_request(self.url)
        html = etree.HTML(response)
        # 獲取到一頁的電影數據
        node_list = html.xpath("//div[@class='info']")
        for move in node_list:
            # 電影名稱
            title = move.xpath('.//a/span/text()')[0]
            # 評分
            score = move.xpath('.//div[@class="bd"]//span[@class="rating_num"]/text()')[0]

            # 將每一部電影的名稱跟評分加入到隊列
            self.q.put(score + "\t" + title)


def main():
    # 創建一個隊列用來保存進程獲取到的數據
    q = Queue()
    base_url = 'https://movie.douban.com/top250?start='
    # 構造所有url
    url_list = [base_url+str(num) for num in range(0,225+1,25)]

    # 保存線程
    Thread_list = []
    # 創建並啟動線程
    for url in url_list:
        p = DouBanSpider(url,q)
        p.start()
        Thread_list.append(p)

    # 讓主線程等待子線程執行完成
    for i in Thread_list:
        i.join()

    while not q.empty():
        print q.get()

if __name__=="__main__":

    start = time.time()
    main()
    print '[info]耗時:%s'%(time.time()-start)
thread
#!/usr/bin/env python2
# -*- coding=utf-8 -*-

from threading import Thread
from Queue import Queue
import time
from lxml import etree
import requests


class DouBanSpider(Thread):
    def __init__(self, url, q):
        # 重寫寫父類的__init__方法
        super(DouBanSpider, self).__init__()
        self.url = url
        self.q = q
        self.headers = {
            'Cookie': 'll="118282"; bid=ctyiEarSLfw; ps=y; __yadk_uid=0Sr85yZ9d4bEeLKhv4w3695OFOPoedzC; dbcl2="155150959:OEu4dds1G1o"; as="https://sec.douban.com/b?r=https%3A%2F%2Fbook.douban.com%2F"; ck=fTrQ; _pk_id.100001.4cf6=c86baf05e448fb8d.1506160776.3.1507290432.1507283501.; _pk_ses.100001.4cf6=*; __utma=30149280.1633528206.1506160772.1507283346.1507290433.3; __utmb=30149280.0.10.1507290433; __utmc=30149280; __utmz=30149280.1506160772.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utma=223695111.1475767059.1506160772.1507283346.1507290433.3; __utmb=223695111.0.10.1507290433; __utmc=223695111; __utmz=223695111.1506160772.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); push_noty_num=0; push_doumail_num=0',
            'Host': 'movie.douban.com',
            'Referer': 'https://movie.douban.com/top250?start=225&filter=',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.104 Safari/537.36',
        }

    def run(self):
        self.parse_page()

    def send_request(self,url):
        '''
        用來發送請求的方法
        :return: 返回網頁源碼
        '''
        # 請求出錯時,重覆請求3次,
        i = 0
        while i <= 3:
            try:
                print u"[INFO]請求url:"+url
                html = requests.get(url=url,headers=self.headers).content
            except Exception as e:
                print u'[INFO] %s%s'% (e,url)
                i += 1
            else:
                return html

    def parse_page(self):
        '''
        解析網站源碼,並採用xpath提取 電影名稱和平分放到隊列中
        :return:
        '''
        response = self.send_request(self.url)
        html = etree.HTML(response)
        # 獲取到一頁的電影數據
        node_list = html.xpath("//div[@class='info']")
        for move in node_list:
            # 電影名稱
            title = move.xpath('.//a/span/text()')[0]
            # 評分
            score = move.xpath('.//div[@class="bd"]//span[@class="rating_num"]/text()')[0]

            # 將每一部電影的名稱跟評分加入到隊列
            self.q.put(score + "\t" + title)


def main():
    # 創建一個隊列用來保存進程獲取到的數據
    q = Queue()
    base_url = 'https://movie.douban.com/top250?start='
    # 構造所有url
    url_list = [base_url+str(num) for num in range(0,225+1,25)]

    # 保存線程
    Thread_list = []
    # 創建並啟動線程
    for url in url_list:
        p = DouBanSpider(url,q)
        p.start()
        Thread_list.append(p)

    # 讓主線程等待子線程執行完成
    for i in Thread_list:
        i.join()

    while not q.empty():
        print q.get()

if __name__=="__main__":

    start = time.time()
    main()
    print '[info]耗時:%s'%(time.time()-start)

  

 

 

 

採用協程爬取,耗時15S,

#!/usr/bin/env python2
# -*- coding=utf-8 -*-

from Queue import Queue
import time
from lxml import etree
import requests
import gevent

# 打上猴子補丁
from gevent import monkey
monkey.patch_all()

class DouBanSpider(object):
    def __init__(self):
        # 創建一個隊列用來保存進程獲取到的數據
        self.q = Queue()
        self.headers = {
            'Cookie': 'll="118282"; bid=ctyiEarSLfw; ps=y; __yadk_uid=0Sr85yZ9d4bEeLKhv4w3695OFOPoedzC; dbcl2="155150959:OEu4dds1G1o"; as="https://sec.douban.com/b?r=https%3A%2F%2Fbook.douban.com%2F"; ck=fTrQ; _pk_id.100001.4cf6=c86baf05e448fb8d.1506160776.3.1507290432.1507283501.; _pk_ses.100001.4cf6=*; __utma=30149280.1633528206.1506160772.1507283346.1507290433.3; __utmb=30149280.0.10.1507290433; __utmc=30149280; __utmz=30149280.1506160772.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utma=223695111.1475767059.1506160772.1507283346.1507290433.3; __utmb=223695111.0.10.1507290433; __utmc=223695111; __utmz=223695111.1506160772.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); push_noty_num=0; push_doumail_num=0',
            'Host': 'movie.douban.com',
            'Referer': 'https://movie.douban.com/top250?start=225&filter=',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.104 Safari/537.36',
        }

    def run(self,url):
        self.parse_page(url)

    def send_request(self,url):
        '''
        用來發送請求的方法
        :return: 返回網頁源碼
        '''
        # 請求出錯時,重覆請求3次,
        i = 0
        while i <= 3:
            try:
                print u"[INFO]請求url:"+url
                html = requests.get(url=url,headers=self.headers).content
            except Exception as e:
                print u'[INFO] %s%s'% (e,url)
                i += 1
            else:
                return html

    def parse_page(self,url):
        '''
        解析網站源碼,並採用xpath提取 電影名稱和平分放到隊列中
        :return:
        '''
        response = self.send_request(url)
        html = etree.HTML(response)
        # 獲取到一頁的電影數據
        node_list = html.xpath("//div[@class='info']")
        for move in node_list:
            # 電影名稱
            title = move.xpath('.//a/span/text()')[0]
            # 評分
            score = move.xpath('.//div[@class="bd"]//span[@class="rating_num"]/text()')[0]

            # 將每一部電影的名稱跟評分加入到隊列
            self.q.put(score + "\t" + title)


    def main(self):


        base_url = 'https://movie.douban.com/top250?start='
        # 構造所有url
        url_list = [base_url+str(num) for num in range(0,225+1,25)]
        # 創建協程並執行
        job_list = [gevent.spawn(self.run,url) for url in url_list]
        # 讓線程等待所有任務完成,再繼續執行。
        gevent.joinall(job_list)

        while not self.q.empty():
            print self.q.get()

if __name__=="__main__":
    start = time.time()
    douban = DouBanSpider()
    douban.main()
    print '[info]耗時:%s'%(time.time()-start)
gevent

 

 

 

 

用了多進程,多線程,協程,實現的代碼都一樣,沒有測試出明顯的那個好!都不分上下,可能跟網路,或者伺服器配置有關。

但理論上來說線程,協程在I/O密集的操作性能是要高於進程的。

 

也可能是我的方法有問題,還望大神們指教!

 


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

-Advertisement-
Play Games
更多相關文章
  • WPF佈局控制項與子控制項的HorizontalAlignment/VerticalAlignment屬性之間的關係: 1、Canvas/WrapPanel控制項: 其子控制項的HorizontalAlignment/VerticalAlignment屬性無效。2、Grid控制項: 其子控制項的Horizont ...
  • 在C#中,值類型和引用類型是相當重要的兩個概念,必須在設計類型的時候就決定類型實例的行為。如果在編寫代碼時不能理解引用類型和值類型的區別,那麼將會給代碼帶來不必要的異常。很多人就是因為沒有弄清楚這兩個概念從而在編程過程中遇到了很多問題,在這裡博主淺談對值類型和引用類型的認識。 首先從概念上看,值類型 ...
  • 一、前言 久聞EF大名,之前做C/S產品用的是Dapper對SqlLite進行ORM。然後接觸公司授權系統後發現用的是EntityFramework對SQLSever進行ORM。授權系統里用的是DBFirst,增刪查改使用Linq To Entity,覺得非常方便。本篇篇幅較短,老司機可直接略過 二 ...
  • 1.post請求 2.get請求 3.ajax跨站請求 在處理函數中需要加入:context.Response.AppendHeader("Access-Control-Allow-Origin", "*"); ...
  • 1 概述 1 概述 本篇文章主要是關於JqGrid的,主要功能包括使用JqGrid增刪查改,導入導出,廢話不多說,直接進入正題。 2 Demo相關 2 Demo相關 2.1 Demo展示 第一部分 第二部分 2.2 源碼和DB下載 國慶回來上傳到github上。 3 公共模塊 3 公共模塊 3.1 ...
  • 一、ResultSet之滾動結果集 ResultSet表示結果集,它是一個二維的表格。ResultSet內部維護一個行游標(游標),ResultSet提供了一系列的方法來移動游標: void beforeFirst():把游標放到第一行的前面,這也是游標預設的位置; void afterLast() ...
  • 一位從vb系轉到JAVA的朋友在一個類中寫了一個類似VB中MSGBOX的方法 ...
  • JDBC(Java DataBase Connectivity,java資料庫連接)是一種用於執行SQL語句的Java API,可以為多種關係資料庫提供統一訪問,它由一組用Java語言編寫的類和介面組成。JDBC提供了一種基準,據此可以構建更高級的工具和介面,使資料庫開發人員能夠編寫資料庫應用程式, ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...