kiến thức [Excel VBA] Sử dụng API để lấy dữ liệu từ nhà cung cấp dịch vụ nhất định

  • Người tạo chủ đề Người tạo chủ đề NguyenDang95
  • Ngày bắt đầu Ngày bắt đầu

NguyenDang95

Senior Member
Trong công việc, đôi khi chúng ta cần lấy, truy xuất dữ liệu theo tiêu chí nhất định từ một nhà cung cấp dịch vụ nào đó, ví dụ như lấy hóa đơn điện tử rồi đưa kết quả ra tập tin Excel hoặc tra cứu địa chỉ dựa vào tọa độ vĩ độ, kinh độ bằng Google GeoCoding chẳng hạn. Kết quả trả về thường là tập tin XML, JSON hay HTML, v.v, tuy nhiên phổ biến nhất vẫn là hai định dạng XML và JSON. Đối với định dạng XML, chúng ta có thể sử dụng thư viện Microsoft XML v6.0 để tạo yêu cầu (Request) đến nhà cung cấp dịch vụ và lọc ra kết quả mong muốn trong kết quả trả về và xuất ra tập tin Excel. Còn với định dạng JSON, VBA lại không có công cụ chính nào hỗ trợ xử lý định dạng này ngoài giải pháp của bên thứ ba VBA-JSON: https://github.com/VBA-tools/VBA-JSON.

Ví dụ: Viết một hàm tự tạo tra cứu nhiệt độ, trạng thái thời tiết của một địa điểm:
Chúng ta sẽ sử dụng dịch vụ tra cứu thời tiết từ nhà cung cấp dịch vụ OpenWeather, tất nhiên là việc sử dụng bản miễn phí sẽ có một số hạn chế nhất định so với bản trả phí.
Tiến hành đăng ký tài khoản, đăng ký lấy khóa API, nghiên cứu tài liệu để sẵn sàng viết hàm tự tạo.

1665741006181.png


Nghiên cứu tập tin XML trả về sau khi Request để lấy những dữ liệu mong muốn. Ở đây chúng ta chỉ quan tâm đến thông tin nhiệt độ và trạng thái thời tiết hiện tại (thẻ temperature và thẻ weather).

XML:
    <current>
    <city id="3163858" name="Zocca">
    <coord lon="10.99" lat="44.34"/>
    <country>IT</country>
    <timezone>7200</timezone>
    <sun rise="2022-08-30T04:36:27" set="2022-08-30T17:57:28"/>
    </city>
    <temperature value="298.48" min="297.56" max="300.05" unit="kelvin"/>
    <feels_like value="298.74" unit="kelvin"/>
    <humidity value="64" unit="%"/>
    <pressure value="1015" unit="hPa"/>
    <wind>
    <speed value="0.62" unit="m/s" name="Calm"/>
    <gusts value="1.18"/>
    <direction value="349" code="N" name="North"/>
    </wind>
    <clouds value="100" name="overcast clouds"/>
    <visibility value="10000"/>
    <precipitation value="3.37" mode="rain" unit="1h"/>
    <weather number="501" value="moderate rain" icon="10d"/>
    <lastupdate value="2022-08-30T14:45:57"/>
    </current>

Tiến hành viết hàm tự tạo, tham chiếu đến thư viện Microsoft XML v6.0:

Mã:
Option Explicit

Public Function CurrentWeather(Location As Variant, Optional Degree As Variant = "c") As Variant
    Dim objXMLHTTP As MSXML2.XMLHTTP60
    Dim objXMLDoc As MSXML2.DOMDocument60
    Dim objXMLNodeList As MSXML2.IXMLDOMNodeList
    Dim objXMLNode As MSXML2.IXMLDOMNode
    Dim strTempValue As String, strWeatherCondition As String
    Dim strURL As String, strResult As String
    Dim strAPI As String
    Dim strDegree As String
    strAPI = "9d06ba06acac21af5c90d5aa7ea59ddf"
    Select Case Degree
        Case "c"
            strURL = "https://api.openweathermap.org/data/2.5/weather?q=" & Location & "&appid=" & strAPI & "&lang=vi&units=metric&mode=xml"
            strDegree = " C, "
        Case "f"
            strURL = "https://api.openweathermap.org/data/2.5/weather?q=" & Location & "&appid=" & strAPI & "&lang=vi&units=imperial&mode=xml"
            strDegree = " F, "
        Case Else
            strURL = "https://api.openweathermap.org/data/2.5/weather?q=" & Location & "&appid=" & strAPI & "&lang=vi&units=standard&mode=xml"
            strDegree = " C, "
    End Select
    Set objXMLHTTP = New MSXML2.XMLHTTP60
    With objXMLHTTP
        .Open "GET", strURL, False
        .Send
        If .Status = 200 Then
            Set objXMLDoc = New MSXML2.DOMDocument60
            objXMLDoc.LoadXML .ResponseText
            Set objXMLNodeList = objXMLDoc.SelectNodes("current")
            For Each objXMLNode In objXMLNodeList
                strTempValue = objXMLNode.SelectSingleNode("temperature").Attributes.getNamedItem("value").Text
                strResult = strTempValue & strDegree
                strWeatherCondition = objXMLNode.SelectSingleNode("weather").Attributes.getNamedItem("value").Text
                strResult = strResult & strWeatherCondition
            Next
        End If
    End With
    CurrentWeather = strResult
    Set objXMLHTTP = Nothing
    Set objXMLNodeList = Nothing
    Set objXMLNode = Nothing
    Set objXMLDoc = Nothing
End Function

Hàm CurrentWeather ở trên có hai tham số: Tham số thứ nhất Location lấy giá trị kiểu chuỗi đại diện cho tên thành phố viết bằng tiếng Anh (cái này hơi bất cập một chút vì hỗ trợ mỗi tiếng Anh), tham số thứ hai Degree là tham số tùy chọn, lấy giá trị kiểu chuỗi gồm "c" sẽ cho kết quả nhiệt độ trả về là độ C (Celsius) và "f" sẽ cho kết quả nhiệt độ trả về là độ F (Fahrenheit), mặc định là giá trị "c".

Chạy thử trong Excel để xem kết quả:

1665741718487.png


Một số ví dụ khác về việc sử dụng API trong VBA:
  1. Đọc dữ liệu của một vùng (Range) trong tập tin Google Sheets
Giả sử chúng ta có một tập tin Google Sheets đã bật quyền chia sẻ như sau:

2022-10-16.png


Chúng ta muốn đọc dữ liệu trong vùng A1:E5. Như vậy để làm được việc này, chúng ta cần đăng ký dịch vụ Sheets API và lấy khóa API, sau đó đọc tài liệu để nắm được các bước viết macro:

2022-10-16 (1).png


2022-10-16 (2).png


Tiến hành viết macro:
Mã:
Option Explicit

Private Sub GetGoogleSheetsRangeValues()
    Dim objWinHttpRequest As WinHttp.WinHttpRequest
    Dim objDict As Scripting.Dictionary
    Dim i As Integer, j As Integer
    Set objWinHttpRequest = New WinHttp.WinHttpRequest
    With objWinHttpRequest
        .Open "GET", "https://sheets.googleapis.com/v4/spreadsheets/1C0pYTtba4H7rg8MKLMVdYt9k6F3kz1MNZX6dw3kUrXo/values/Sheet1!A1:E5?key=API_KEY"
        .Send
        If .Status = 200 Then
            ' VBA-JSON: https://github.com/VBA-tools/VBA-JSON
            Set objDict = JsonConverter.ParseJson(.ResponseText)
            For i = 1 To objDict.Item("values").Count
                For j = 1 To objDict.Item("values")(i).Count
                    Debug.Print objDict.Item("values")(i)(j)
                Next
            Next
        End If
    End With
    Set objWinHttpRequest = Nothing
End Sub

Private Function Quote(Text As String) As String
    Quote = Chr(34) & Text & Chr(34)
End Function

So sánh kết quả trả về với nội dung trong sheet:

1665915091609.png


2. Tương tác với dịch vụ lưu trữ và chia sẻ trực tuyến FShare:
FShare là một trong nhiều dịch vụ lưu trữ và chia sẻ trực tuyến phổ biến ở Việt Nam. Với việc dịch vụ này hỗ trợ API, chúng ta có thể dễ dàng viết macro để tương tác với dịch dụ này.
https://www.fshare.vn/api-doc
Trong ví dụ này chỉ đề cập đến thao tác đăng nhập vào dịch vụ này bằng API.
Tiến hành nghiên cứu tài liệu:

1665919035011.png


Khi đăng ký sử dụng API thành công, phía FShare sẽ gửi cho chúng ta một email với nội dung như sau:

Screenshot 2022-10-16 182650.png


Thông tin đăng nhập được gửi qua phần request body. Tiến hành viết macro:

Mã:
Option Explicit

Public Sub LoginToFShare()
    Dim objWinHTTP As WinHttp.WinHttpRequest
    Dim strRequestBody As String
    strRequestBody = "{" & _
                Quote("user_email") & ": " & Quote("Account_Name") & ", " & _
                Quote("password") & ": " & Quote("Account_Password") & ", " & _
                Quote("app_key") & ": " & Quote("app_key") & " " & _
                "}"
    Set objWinHTTP = New WinHttp.WinHttpRequest
    With objWinHTTP
        .Open "POST", "https://api.fshare.vn/api/user/login"
        .SetRequestHeader "User-Agent", "User_Agent"
        .SetRequestHeader "Accept", "application/json"
        .Send strRequestBody
        If .Status = 200 Then
            Debug.Print .ResponseText
        Else: Debug.Print .ResponseText
        End If
    End With
    Set objWinHTTP = Nothing
End Sub

Private Function Quote(Text As String) As String
    Quote = Chr(34) & Text & Chr(34)
End Function

Nếu không có gì sai sót thì response text nhận được sẽ có nội dung đại loại như sau:
JSON:
{
  "code": 200,
  "msg": "Login successfully!",
  "token": "884fde3ee0a7fa60998",
  "session_id": "ksuku8vdfqd"
}

Từ bước này, chúng ta có thể thực hiện một số thao tác quản lý tập tin trên FShare, để biết thêm chi tiết, vui lòng tìm hiểu thêm tại đây:
https://www.fshare.vn/api-doc#/
 
Sửa lần cuối:
Hiện tại zalo và fb cho người dùng tiếp cận API nào nhỉ, nhờ bạn hướng dẫn giúp ạ
 
Hiện tại zalo và fb cho người dùng tiếp cận API nào nhỉ, nhờ bạn hướng dẫn giúp ạ
Với Zalo thím ngâm cứu cái này thử xem (cái này mình thấy lằng nhằng quá): Zalo DotNet SDK
https://developers.zalo.me/docs/sdk/dotnet-sdk/tai-lieu/bat-dau-nhanh-post-1734
Còn Facebook thì phải dùng đến giải pháp của bên thứ ba (nhưng mà lên Stack Overflow thấy báo bug nhiều quá):
https://github.com/facebook-csharp-sdk/facebook-csharp-sdk

Mấy cái này mình chưa làm bao giờ nên không thể chia sẻ được gì thêm với thím.
Với VBA thì không sử dụng được những giải pháp trên, tuy nhiên thím có thể tạo một Class Library bằng C# hoặc VB.Net chẳng hạn, dựa vào thư viện ban đầu kể trên để viết các class, thuộc tính, phương thức sao cho phù hợp với nhu cầu của bản thân, make assembly COM-visible rồi trong VBA tham chiếu đến tập tin .tlb sinh ra từ Class Library là có thể viết được macro.
Ví dụ: Create a DLL by CSharp or VB.Net for VBA
https://www.geeksengine.com/article/create-dll.html
 
Tiếp nối bài viết về chủ đề "làm việc với FShare API", nay mình có viết sẵn Class Module giúp người dùng có thể dễ dàng viết macro đơn giản nhất có thể mà không cần phải GET, POST, request body hay quan tâm JSON là gì. Chi tiết ở đây: https://github.com/nguyendang95/FShareVBALibrary/
Để sử dụng, trong cửa sổ soạn thảo code của VBA, người dùng chọn File | Import File và tiến hành nhập tất cả tập tin .cls đã tải xuống ở trên.
Do mình không có tài khoản VIP nên không thể kiểm tra kỹ xem có lỗi không, thím nào rành thì có thể sửa lại nếu cần.
 
Một ví dụ khác về sử dụng (REST) API trong VBA.
Notion là một ứng dụng ghi chú và quản lý công việc do Notion Labs, Inc. phát triển. Với việc ứng dụng này cung cấp API, chúng ta có thể viết macro VBA để tạo HTTP Request tương tác với dịch vụ này thông qua thư viện WinHTTPRequest XMLHTTP.

Viết một class module để xác thực với Notion và lấy, lưu trữ access token. Yêu cầu người dùng phải tạo Integration dạng public (không phải internal) và lấy thông tin client id lẫn client secret của Integration đó.
Máy tính phải cài đặt trước:

1669432377683.png


1669434839203.png
 

Tệp đính kèm

Sửa lần cuối:
Sau khi viết class module nói trên, tiến hành viết macro chạy thử xem thế nào.
Giả sử người dùng có một database tên là Task List với mã ID là 0fec8b48bdd04aa2bd4d873a06335718 và muốn lấy thông tin về database này:

1669434739398.png


Macro cùng với kết quả trả về:

1669435099426.png
 
Sửa lần cuối:
Hi thím hiện tại mình đang cần những cái như thím nói ( lấy thông tin từ file hóa đơn xml) thím có thể nói rõ hơn về việc sử dụng vba ko? thanks thím. Ngoài ra mình muốn lấy tất cả file xml từ trên web của tông cục thuế thì có cách nào được ko thím. Về khoản này mình không rõ nên nhờ thím giải ngố giúp mình.
 
Hi thím hiện tại mình đang cần những cái như thím nói ( lấy thông tin từ file hóa đơn xml) thím có thể nói rõ hơn về việc sử dụng vba ko? thanks thím. Ngoài ra mình muốn lấy tất cả file xml từ trên web của tông cục thuế thì có cách nào được ko thím. Về khoản này mình không rõ nên nhờ thím giải ngố giúp mình.
Thím nghiên cứu cái này xem sao nhé:
https://www.giaiphapexcel.com/diend...ơn-điện-tử-áp-dụng-nghị-định-123-2020.159633/
 
em chào bác @NguyenDang95
em có thắc mắc:
1. trong bài viết bác hay đề cập đến đăng kí dịch vụ api có phải là đăng kí để lấy địa chỉ api của 1 website phải không ạ?
2. khi vào 1 trang web sao mình biết format xml của web như thế nào ạ? coi bằng cách nào thế?
em cảm ơn ạ
 
em chào bác @NguyenDang95
em có thắc mắc:
1. trong bài viết bác hay đề cập đến đăng kí dịch vụ api có phải là đăng kí để lấy địa chỉ api của 1 website phải không ạ?
2. khi vào 1 trang web sao mình biết format xml của web như thế nào ạ? coi bằng cách nào thế?
em cảm ơn ạ
1. Tức là sử dụng API của một nhà cung cấp nào đó (vd: Google Drive API dùng để quản lý tệp trên Drive của người dùng).
2. Thím có thể đọc tài liệu về API của nhà cung cấp dịch vụ để xem API đó hỗ trợ những định dạng ResponseText nào (XML, JSON, HTML hay Text).
 
1. Tức là sử dụng API của một nhà cung cấp nào đó (vd: Google Drive API dùng để quản lý tệp trên Drive của người dùng).
2. Thím có thể đọc tài liệu về API của nhà cung cấp dịch vụ để xem API đó hỗ trợ những định dạng ResponseText nào (XML, JSON, HTML hay Text).
em vẫn chưa hiểu lắm số 1
ví dụ các web em lấy data thì em vào phần network của trang web và lấy link api thôi, chứ có phải đăng ký API gì đâu ạ ?
 
em vẫn chưa hiểu lắm số 1
ví dụ các web em lấy data thì em vào phần network của trang web và lấy link api thôi, chứ có phải đăng ký API gì đâu ạ ?
Bài viết này của mình chỉ đề cập đến việc sử dụng API của nhà cung cấp dịch vụ nhất định và có tài liệu hướng dẫn sử dụng, tức là có thể miễn phí hoặc mất phí, còn việc dò API thông qua tab Network trong DevTools (nhấn phím F12) thì không phải lúc nào cũng hiệu quả.
Ví dụ, API tạo một List mới trong SharePoint site:

1675564307725.png
 
Bài viết này của mình chỉ đề cập đến việc sử dụng API của nhà cung cấp dịch vụ nhất định và có tài liệu hướng dẫn sử dụng, tức là có thể miễn phí hoặc mất phí, còn việc dò API thông qua tab Network trong DevTools (nhấn phím F12) thì không phải lúc nào cũng hiệu quả.
Ví dụ, API tạo một List mới trong SharePoint site:

Xem tệp đính kèm 1644659
dạ vâng, em cảm ơn bác ạ, xin đa tạ
 
em đang lấy data từ link json nhưng bị lỗi

https://masboard.masvn.com/api/v1/m...=HPG&from=20230124&to=20230224&fetchCount=100

1677200152936.png


mong anh chị giúp đỡ, em cảm ơn nhiều ạ

code:


Sub ParseJson()

strURL = "https://masboard.masvn.com/api/v1/m...=A32&from=20230124&to=20230224&fetchCount=100"
Dim objWinHttp As Object
Set objWinHttp = CreateObject("WinHttp.WinHttpRequest.5.1")
With objWinHttp
.Open "GET", strURL, True
.SetRequestHeader "Accept", "application/json"
.Send
.WaitForResponse

If .Status = 200 Then
Dim objJson As Scripting.Dictionary

Set objJson = JsonConverter.ParseJson(.ResponseText)

Dim i As Long

If objJson.Count > 0 Then
For i = 1 To objJson.Count
Debug.Print objJson(i)("vo")
Next

End If
Else: MsgBox "An error occurred"

End If

End With
End Sub
 
em đang lấy data từ link json nhưng bị lỗi

https://masboard.masvn.com/api/v1/m...=HPG&from=20230124&to=20230224&fetchCount=100

Xem tệp đính kèm 1680695

mong anh chị giúp đỡ, em cảm ơn nhiều ạ

code:


Sub ParseJson()

strURL = "https://masboard.masvn.com/api/v1/m...=A32&from=20230124&to=20230224&fetchCount=100"
Dim objWinHttp As Object
Set objWinHttp = CreateObject("WinHttp.WinHttpRequest.5.1")
With objWinHttp
.Open "GET", strURL, True
.SetRequestHeader "Accept", "application/json"
.Send
.WaitForResponse

If .Status = 200 Then
Dim objJson As Scripting.Dictionary

Set objJson = JsonConverter.ParseJson(.ResponseText)

Dim i As Long

If objJson.Count > 0 Then
For i = 1 To objJson.Count
Debug.Print objJson(i)("vo")
Next

End If
Else: MsgBox "An error occurred"

End If

End With
End Sub
Thím cần sửa lại như sau (API này yêu cầu phải kèm theo header chứa giá trị UserAgent:

Mã:
Option Explicit

Sub ParseJson()
    Const strURL As String = "https://masboard.masvn.com/api/v1/market/symbolHistory?symbol=A32&from=20230124&to=20230224&fetchCount=100"
    Dim objWinHttp As Object
    Set objWinHttp = CreateObject("WinHttp.WinHttpRequest.5.1")
    With objWinHttp
        .Open "GET", strURL, True
        .SetRequestHeader "Accept", "application/json"
        .SetRequestHeader "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.50"
        .Send
        .WaitForResponse
        If .Status = 200 Then
            Dim objJson As New Collection
            Set objJson = JsonConverter.ParseJson(.ResponseText)
            Dim i As Long
            If objJson.Count > 0 Then
                For i = 1 To objJson.Count
                    If IsNull(objJson.Item(i)("vo")) Then
                        Debug.Print vbNullString
                    Else: Debug.Print objJson.Item(i)("vo")
                    End If
                Next
            End If
            Else: MsgBox "An error occurred"
        End If
    End With
End Sub

Để cho tiện, thím có thể tạo một sub hoặc function chứa một vài tham số như symbol, from, to và fetchCount để macro trở nên linh hoạt hơn.
 
Thím cần sửa lại như sau (API này yêu cầu phải kèm theo header chứa giá trị UserAgent:

Mã:
Option Explicit

Sub ParseJson()
    Const strURL As String = "https://masboard.masvn.com/api/v1/market/symbolHistory?symbol=A32&from=20230124&to=20230224&fetchCount=100"
    Dim objWinHttp As Object
    Set objWinHttp = CreateObject("WinHttp.WinHttpRequest.5.1")
    With objWinHttp
        .Open "GET", strURL, True
        .SetRequestHeader "Accept", "application/json"
        .SetRequestHeader "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.50"
        .Send
        .WaitForResponse
        If .Status = 200 Then
            Dim objJson As New Collection
            Set objJson = JsonConverter.ParseJson(.ResponseText)
            Dim i As Long
            If objJson.Count > 0 Then
                For i = 1 To objJson.Count
                    If IsNull(objJson.Item(i)("vo")) Then
                        Debug.Print vbNullString
                    Else: Debug.Print objJson.Item(i)("vo")
                    End If
                Next
            End If
            Else: MsgBox "An error occurred"
        End If
    End With
End Sub

Để cho tiện, thím có thể tạo một sub hoặc function chứa một vài tham số như symbol, from, to và fetchCount để macro trở nên linh hoạt hơn.
em cảm ơn bác @NguyenDang95
em cũng nói thật là em copy code rồi tự mò học theo thôi chứ em gà mờ lắm

(nhân tiện cho em hỏi bên voz không có chỗ copy code nhanh như bên giaiphapexcel bác nhỉ? xin đa tạ)
 
Sửa lần cuối:

Thống kê chủ đề

Ngày tạo
NguyenDang95,
Người trả lời cuối
culicuiga10,
Trả lời
53
Lượt xem
14.250
Quay lại
Lên đầu trang