Nhân dịp sinh nhật diễn đàn GPE sắp tới, mình muốn đóng góp một chút quà nhỏ cho cộng đồng. Mình có viết một đoạn code Python hỗ trợ tra cứu tự động thông tin Mã số thuế doanh nghiệp và MST cá nhân hàng loạt từ trang https://tracuunnt.gdt.gov.vn/tcnnt/mstdn.jsp
Điểm nổi bật của công cụ này là tốc độ: 1.000 MST chỉ mất khoảng 5 - 10 phút, trả về kết quả file Excel đầy đủ thông tin (Tên, trạng thái, địa chỉ...), giúp anh chị em kế toán tiết kiệm được rất nhiều thời gian so với việc tra tay.
Vì code viết bằng Python nên cần cài đặt một số thư viện môi trường ban đầu. Để hỗ trợ các bạn chưa quen với lập trình:
Hỗ trợ cài đặt: Bạn nào cần dùng gấp cứ inbox trực tiếp qua diễn đàn hoặc gửi email cho mình qua địa chỉ: doanlong49@gmail.com. Mình sẽ hỗ trợ cài đặt miễn phí qua UltraView cho mọi người.
Chia sẻ mã nguồn: Đúng ngày sinh nhật diễn đàn, mình sẽ upload toàn bộ file code lên đây để ai muốn tự nghiên cứu hoặc phát triển thêm đều có thể tải về
Nhân dịp sinh nhật diễn đàn GPE sắp tới, mình muốn đóng góp một chút quà nhỏ cho cộng đồng. Mình có viết một đoạn code Python hỗ trợ tra cứu tự động thông tin Mã số thuế doanh nghiệp và MST cá nhân hàng loạt từ trang https://tracuunnt.gdt.gov.vn/tcnnt/mstdn.jsp
Điểm nổi bật của công cụ này là tốc độ: 1.000 MST chỉ mất khoảng 5 - 10 phút, trả về kết quả file Excel đầy đủ thông tin (Tên, trạng thái, địa chỉ...), giúp anh chị em kế toán tiết kiệm được rất nhiều thời gian so với việc tra tay.
Vì code viết bằng Python nên cần cài đặt một số thư viện môi trường ban đầu. Để hỗ trợ các bạn chưa quen với lập trình:
Hỗ trợ cài đặt: Bạn nào cần dùng gấp cứ inbox trực tiếp qua diễn đàn hoặc gửi email cho mình qua địa chỉ: doanlong49@gmail.com. Mình sẽ hỗ trợ cài đặt miễn phí qua UltraView cho mọi người.
Chia sẻ mã nguồn: Đúng ngày sinh nhật diễn đàn, mình sẽ upload toàn bộ file code lên đây để ai muốn tự nghiên cứu hoặc phát triển thêm đều có thể tải về
Dạ, ban đầu mình định hỗ trợ cài đặt UltraView cho từng bạn, nhưng do số lượng các bạn nhờ hỗ trợ hiện tại khá nhiều và quỹ thời gian cá nhân của mình có hạn, mình quyết định sẽ upload toàn bộ mã nguồn lên vào cuối tuần này để mọi người chủ động tải về tìm hiểu và sử dụng sớm. Chúc diễn đàn GPE của chúng ta ngày càng phát triển!
Mình chia sẻ đoạn code này với tinh thần hỗ trợ cộng đồng làm nghề. Rất mong các bạn sử dụng nó vào mục đích tốt, giúp đỡ các thành viên khác trong công việc và tuyệt đối KHÔNG dùng mã nguồn này để thương mại hóa hoặc kiếm tiền. Hãy giữ đúng tinh thần chia sẻ vốn có của diễn đàn chúng ta.
Dạ, ban đầu mình định hỗ trợ cài đặt UltraView cho từng bạn, nhưng do số lượng các bạn nhờ hỗ trợ hiện tại khá nhiều và quỹ thời gian cá nhân của mình có hạn, mình quyết định sẽ upload toàn bộ mã nguồn lên vào cuối tuần này để mọi người chủ động tải về tìm hiểu và sử dụng sớm. Chúc diễn đàn GPE của chúng ta ngày càng phát triển!
Mình chia sẻ đoạn code này với tinh thần hỗ trợ cộng đồng làm nghề. Rất mong các bạn sử dụng nó vào mục đích tốt, giúp đỡ các thành viên khác trong công việc và tuyệt đối KHÔNG dùng mã nguồn này để thương mại hóa hoặc kiếm tiền. Hãy giữ đúng tinh thần chia sẻ vốn có của diễn đàn chúng ta.
Do mình không có quyền sửa bài viết bài 1 nên mình đăng bài ở đây.
Hôm nay, ngày 22/07/2026, mình xin chia sẻ toàn bộ đoạn code Python tra cứu MST này đến cộng đồng Giải Pháp Excel. Do số lượng các bạn nhờ hỗ trợ cài đặt khá nhiều và quỹ thời gian cá nhân có hạn, mình tải lên sớm để mọi người có thể chủ động tải về nghiên cứu, tùy chỉnh và sử dụng ngay.
Bước 1: Download python về cài đặt ưu tiên phiên bản 3.11 > 3.14 vì hỗ trợ đầy đủ thư viện. Link download Python Releases for Windows | Python.org. Tải bên Python Releases for Windows
Thông thường Python sẽ nằm ở: C:\Users\Ten_User\AppData\Local\Programs\Python
Hoặc nếu bạn cài cho tất cả người dùng: C:\Program Files\Python3x
Bước 2: Tải và cài đặt Tesseract OCR (Dùng để giải Captcha)
Lưu ý: Tesseract thường sẽ nằm ở: C:\Program Files\Tesseract-OCR\tesseract.exe nếu cài chỉ dùng riêng cho user C:\Users\Ten_User\AppData\Local\Programs\Tesseract-OCR\tesseract.exe
Bước 3: Cài đặt các thư viện cần thiết của python:
Mở cửa sổ Command Prompt (CMD) trên máy tính và dán dòng lệnh sau bấm Enter:
Đây là phiên bản mã nguồn mở cơ bản nhất dành cho các bạn yêu thích lập trình tìm hiểu và tự phát triển thêm (ví dụ: đóng gói thành file .exe chạy độc lập không cần cài môi trường Python).
Quy định sử dụng: Tuyệt đối KHÔNG sử dụng mã nguồn này (hoặc các phiên bản nâng cấp từ mã nguồn này) vào bất kỳ mục đích thương mại/thu phí nào. Trường hợp chia sẻ lại công khai trên các nền tảng khác, vui lòng ghi rõ nguồn từ Diễn đàn Giải Pháp Excel (GPE).
Code Python Tra Cứu MST Cá Nhân
Python:
import requests
from PIL import Image
import pytesseract
from bs4 import BeautifulSoup
import pandas as pd
import time
import os
# Thêm thư viện này để tắt các dòng cảnh báo màu đỏ phiền phức khi bypass SSL
from urllib3.exceptions import InsecureRequestWarning
# Tắt cảnh báo InsecureRequestWarning trên toàn bộ hệ thống
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
# ==================================================
# Cấu hình
# ==================================================
URL = "https://tracuunnt.gdt.gov.vn/tcnnt/mstcn.jsp"
CAPTCHA_URL = "https://tracuunnt.gdt.gov.vn/tcnnt/captcha.png?uid="
pytesseract.pytesseract.tesseract_cmd = (
r"C:\Users\Ten_User\AppData\Local\Programs\Tesseract-OCR\tesseract.exe"
)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
INPUT_EXCEL = os.path.join(BASE_DIR, "ListTaxCodeCN_1.xlsx")
OUTPUT_EXCEL = os.path.join(BASE_DIR, "TaxLookupResultCN_1.xlsx")
CAPTCHA_PIC = "captchaCN_1.png"
# ==================================================
# Tạo Session
# ==================================================
def create_session():
session = requests.Session()
# BỎ QUA KIỂM TRA CHỨNG CHỈ SSL CHO TOÀN BỘ SESSION
session.verify = False
session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
})
response = session.get(URL)
print("Khởi tạo Session Status:", response.status_code)
return session
# ==================================================
# Download Captcha
# ==================================================
def download_captcha(session, filename=CAPTCHA_PIC):
captcha_path = os.path.join(BASE_DIR, filename)
print("Đang chờ 1 giây trước khi lấy captcha...")
time.sleep(1)
img = session.get(f"{CAPTCHA_URL}{int(time.time()*1000)}")
with open(captcha_path, "wb") as f:
f.write(img.content)
return captcha_path
# ==================================================
# OCR Captcha
# ==================================================
def read_captcha(filename=CAPTCHA_PIC, show_image=False):
captcha_path = os.path.join(BASE_DIR, filename)
image = Image.open(captcha_path)
if show_image:
image.show()
captcha = pytesseract.image_to_string(
image,
config=r'--oem 3 --psm 8 -c tessedit_char_whitelist=abcdefghijklmnopqrstuvwxyz0123456789'
)
captcha = captcha.lower().strip()
return captcha
# ==================================================
# Tra cứu MST
# ==================================================
def lookup_tax(session, mst, captcha):
payload = {
"cm": "cm",
"mst": mst,
"fullname": "",
"address": "",
"cmt": "",
"captcha": captcha
}
response = session.post(URL, data=payload)
return response.text
# ==================================================
# Parse HTML
# ==================================================
def parse_html(html_text):
soup = BeautifulSoup(html_text, "html.parser")
# 1. Sai mã captcha
error_p = soup.find("p", style=lambda value: value and "color:red" in value.replace(" ", ""))
if error_p:
text_error = error_p.get_text().strip()
if "Vui lòng nhập đúng mã xác nhận" in text_error:
print(" -> Web thông báo: Sai mã Captcha!")
return [], True, False
# Kiểm tra khu vực bảng kết quả
result_div = soup.find("div", id="resultContainer")
if result_div is None:
return [], False, False
table = result_div.find("table")
if not table:
return [], False, False
# 2. Đúng captcha nhưng Không tìm thấy MST phù hợp
table_text = table.get_text()
if "Không tìm thấy người nộp thuế nào phù hợp" in table_text:
print(" -> Web thông báo: Không tìm thấy người nộp thuế (MST không tồn tại)!")
return [], False, True
# 3. Thành công, bóc tách dữ liệu
rows = []
for tr in table.find_all("tr")[1:]:
cols = tr.find_all("td")
if len(cols) != 5:
continue
rows.append({
"STT": cols[0].get_text(strip=True),
"MST": cols[1].get_text(strip=True),
"Tên người nộp thuế": cols[2].get_text(" ", strip=True),
"Cơ quan thuế": cols[3].get_text(" ", strip=True),
"Trạng thái": cols[4].get_text(" ", strip=True)
})
return rows, False, False
# ==================================================
# Quy trình xử lý cho một Mã Số Thuế (Trả về cả trạng thái văn bản)
# ==================================================
def process_single_mst(session, mst, max_attempts=20):
attempt = 0
while attempt < max_attempts:
attempt += 1
print(f" [Thử Captcha lần {attempt}/{max_attempts}]")
download_captcha(session)
captcha = read_captcha(show_image=False)
if not captcha:
print(" OCR trống, thử lại mã khác...")
continue
print(f" OCR đọc được: '{captcha}'")
html_response = lookup_tax(session, mst, captcha)
rows, is_wrong_captcha, is_invalid_mst = parse_html(html_response)
if rows:
return rows, "Tìm thành công"
if is_invalid_mst:
return None, "Web thông báo: Không tìm thấy người nộp thuế (MST không tồn tại)!"
if is_wrong_captcha:
print(" Sai mã Captcha, đang lấy mã mới...")
# Hết số lần thử mà vẫn chưa vượt qua captcha hoặc không có bảng dữ liệu
status_msg = f"Đã thử {max_attempts} lần lỗi captcha cho MST {mst}"
print(f" {status_msg}. Chuyển MST tiếp theo.")
return None, status_msg
# ==================================================
# Xuất dữ liệu bảng chi tiết kết quả tra cứu
# ==================================================
def export_result_excel(df):
try:
df.to_excel(
OUTPUT_EXCEL,
index=False,
engine="openpyxl"
)
print(
f"Đã ghi {len(df)} dòng."
)
except PermissionError:
print(
"Vui lòng đóng file Excel."
)
# ==================================================
# Ghi ngược trạng thái vào file ListTaxCodeCN.xlsx
# ==================================================
def update_input_file_status(df):
try:
df.to_excel(INPUT_EXCEL, sheet_name="Sheet1", index=False, engine="openpyxl")
print(f"-> Đã ghi nhận cột Status ngược lại file gốc: {INPUT_EXCEL}")
except PermissionError:
print("LỖI: Vui lòng đóng file ListTaxCodeCN.xlsx để script ghi trạng thái Status!")
# ==================================================
# Main
# ==================================================
def main():
if not os.path.exists(INPUT_EXCEL):
print(f"Lỗi: Không tìm thấy file {INPUT_EXCEL}!")
return
try:
# Đọc trọn vẹn file đầu vào để giữ lại cấu trúc các cột ban đầu
input_df = pd.read_excel(INPUT_EXCEL, sheet_name="Sheet1", dtype={"TaxCode": str})
if "TaxCode" not in input_df.columns:
print("Lỗi: Không tìm thấy cột có tên 'TaxCode'!")
return
# Tạo sẵn cột Status nếu chưa có trong file
if "Status" not in input_df.columns:
input_df["Status"] = ""
print(f"Tìm thấy tổng cộng {len(input_df)} dòng trong file Excel cần kiểm tra.")
except Exception as e:
print(f"Lỗi khi đọc file Excel đầu vào: {e}")
return
session = create_session()
all_rows_data = [] # List tạm chứa thông tin chi tiết bóc tách từ web
# Duyệt theo Index của DataFrame đầu vào để dễ ghi ngược trạng thái từng dòng
for idx, row in input_df.iterrows():
mst = str(row["TaxCode"]).strip()
# Bỏ qua các dòng trống (NaN) không có MST
if not mst or mst == "nan":
continue
print(f"\n==================================================")
print(f"TIẾN HÀNH TRA CỨU [{idx+1}/{len(input_df)}]: MST {mst}")
print(f"==================================================")
# Quy trình xử lý lấy ra cả dữ liệu (nếu có) và dòng thông báo trạng thái tương ứng
rows, status_message = process_single_mst(session, mst, max_attempts=20)
# Ghi nhận trạng thái vào cột Status tại đúng dòng hiện tại
input_df.at[idx, "Status"] = status_message
if rows:
all_results.extend(rows) if 'all_results' in locals() else all_rows_data.extend(rows)
print(f" -> {status_message}.")
else:
print(f" -> Trạng thái ghi nhận: {status_message}")
# 1. Xuất file chứa thông tin chi tiết bóc tách thành công (TaxLookupResultCN.xlsx)
if all_rows_data:
print(f"\n--- Đang xuất bảng chi tiết người nộp thuế TNCN ---")
final_detail_df = pd.DataFrame(all_rows_data)
export_result_excel(final_detail_df)
# 2. Ghi đè ngược toàn bộ cột Status về lại file danh sách gốc (ListTaxCodeCN.xlsx)
print(f"\n--- Đang ghi cập nhật trạng thái về file danh sách gốc ---")
update_input_file_status(input_df)
print("\n Hoàn thành toàn bộ quy trình phối hợp tra cứu và quản lý!")
if __name__ == "__main__":
main()
Code Python Tra Cứu MST Doanh Nghiệp
Python:
import requests
from PIL import Image
import pytesseract
from bs4 import BeautifulSoup
import pandas as pd
import time
import os
# Thêm thư viện này để tắt các dòng cảnh báo màu đỏ phiền phức khi bypass SSL
from urllib3.exceptions import InsecureRequestWarning
# Tắt cảnh báo InsecureRequestWarning trên toàn bộ hệ thống
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
# ==================================================
# Cấu hình
# ==================================================
URL = "https://tracuunnt.gdt.gov.vn/tcnnt/mstdn.jsp"
CAPTCHA_URL = "https://tracuunnt.gdt.gov.vn/tcnnt/captcha.png?uid="
pytesseract.pytesseract.tesseract_cmd = (
r"C:\Users\Ten_User\AppData\Local\Programs\Tesseract-OCR\tesseract.exe"
)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
INPUT_EXCEL = os.path.join(BASE_DIR, "ListTaxCodeCT_1.xlsx")
OUTPUT_EXCEL = os.path.join(BASE_DIR, "TaxLookupResultCT_1.xlsx")
CAPTCHA_PIC = "captchaCT_1.png"
# ==================================================
# Tạo Session
# ==================================================
def create_session():
session = requests.Session()
# BỎ QUA KIỂM TRA CHỨNG CHỈ SSL CHO TOÀN BỘ SESSION
session.verify = False
session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
})
response = session.get(URL)
print("Khởi tạo Session Status:", response.status_code)
return session
# ==================================================
# Download Captcha
# ==================================================
def download_captcha(session, filename=CAPTCHA_PIC):
captcha_path = os.path.join(BASE_DIR, filename)
print("Đang chờ 1 giây trước khi lấy captcha...")
time.sleep(1)
img = session.get(f"{CAPTCHA_URL}{int(time.time()*1000)}")
with open(captcha_path, "wb") as f:
f.write(img.content)
return captcha_path
# ==================================================
# OCR Captcha
# ==================================================
def read_captcha(filename=CAPTCHA_PIC, show_image=False):
captcha_path = os.path.join(BASE_DIR, filename)
image = Image.open(captcha_path)
if show_image:
image.show()
captcha = pytesseract.image_to_string(
image,
config=r'--oem 3 --psm 8 -c tessedit_char_whitelist=abcdefghijklmnopqrstuvwxyz0123456789'
)
captcha = captcha.lower().strip()
return captcha
# ==================================================
# Tra cứu MST
# ==================================================
def lookup_tax(session, mst, captcha):
payload = {
"cm": "cm",
"mst": mst,
"fullname": "",
"address": "",
"cmt": "",
"captcha": captcha
}
response = session.post(URL, data=payload)
return response.text
# ==================================================
# Parse HTML (Giữ cấu trúc 6 cột cho Tab Doanh nghiệp)
# ==================================================
def parse_html(html_text):
soup = BeautifulSoup(html_text, "html.parser")
# 1. Sai mã captcha
error_p = soup.find("p", style=lambda value: value and "color:red" in value.replace(" ", ""))
if error_p:
text_error = error_p.get_text().strip()
if "Vui lòng nhập đúng mã xác nhận" in text_error:
print(" -> Web thông báo: Sai mã Captcha!")
return [], True, False
# Kiểm tra khu vực bảng kết quả
result_div = soup.find("div", id="resultContainer")
if result_div is None:
return [], False, False
table = result_div.find("table")
if not table:
return [], False, False
# 2. Đúng captcha nhưng Không tìm thấy MST phù hợp
table_text = table.get_text()
if "Không tìm thấy người nộp thuế nào phù hợp" in table_text:
print(" -> Web thông báo: Không tìm thấy người nộp thuế (MST không tồn tại)!")
return [], False, True
# 3. Thành công, bóc tách dữ liệu dạng List (Cấu trúc 6 cột đầy đủ)
rows = []
for tr in table.find_all("tr")[1:]:
cols = tr.find_all("td")
if len(cols) != 6:
continue
rows.append({
"STT": cols[0].get_text(strip=True),
"MST": cols[1].get_text(strip=True),
"Tên người nộp thuế": cols[2].get_text(" ", strip=True),
"Địa chỉ": cols[3].get_text(" ", strip=True),
"Cơ quan thuế": cols[4].get_text(" ", strip=True),
"Trạng thái": cols[5].get_text(" ", strip=True)
})
return rows, False, False
# ==================================================
# Quy trình xử lý cho một Mã Số Thuế
# ==================================================
def process_single_mst(session, mst, max_attempts=20):
attempt = 0
while attempt < max_attempts:
attempt += 1
print(f" [Thử Captcha lần {attempt}/{max_attempts}]")
download_captcha(session)
captcha = read_captcha(show_image=False)
if not captcha:
print(" OCR trống, thử lại mã khác...")
continue
print(f" OCR đọc được: '{captcha}'")
html_response = lookup_tax(session, mst, captcha)
rows, is_wrong_captcha, is_invalid_mst = parse_html(html_response)
if rows:
return rows, "Tìm thành công"
if is_invalid_mst:
return None, "Web thông báo: Không tìm thấy người nộp thuế (MST không tồn tại)!"
if is_wrong_captcha:
print(" Sai mã Captcha, đang lấy mã mới...")
# Hết số lần thử mà vẫn chưa vượt qua captcha hoặc lỗi hệ thống
status_msg = f"Đã thử {max_attempts} lần lỗi captcha cho MST {mst}"
print(f" {status_msg}. Chuyển MST tiếp theo.")
return None, status_msg
# ==================================================
# Xuất dữ liệu bảng chi tiết kết quả tra cứu
# ==================================================
def export_excel(df):
try:
df.to_excel(
OUTPUT_EXCEL,
index=False,
engine="openpyxl"
)
print(
f"Đã ghi {len(df)} dòng."
)
except PermissionError:
print(
"Vui lòng đóng file Excel."
)
# ==================================================
# Ghi ngược trạng thái vào file danh sách gốc
# ==================================================
def update_input_file_status(df):
try:
df.to_excel(INPUT_EXCEL, sheet_name="Sheet1", index=False, engine="openpyxl")
print(f"-> Đã ghi nhận thành công cột Status ngược lại file danh sách gốc: {INPUT_EXCEL}")
except PermissionError:
print("LỖI: Vui lòng đóng file ListTaxCodeCN.xlsx để script có quyền ghi trạng thái Status!")
# ==================================================
# Main (Duyệt theo dòng DataFrame để gán chính xác trạng thái)
# ==================================================
def main():
if not os.path.exists(INPUT_EXCEL):
print(f"Lỗi: Không tìm thấy file {INPUT_EXCEL}!")
return
try:
# Đọc dữ liệu đầu vào và ép cột TaxCode thành chuỗi (String)
input_df = pd.read_excel(INPUT_EXCEL, sheet_name="Sheet1", dtype={"TaxCode": str})
if "TaxCode" not in input_df.columns:
print("Lỗi: Không tìm thấy cột có tên 'TaxCode'!")
return
# Tự động tạo cột Status nếu trong file gốc chưa có sẵn
if "Status" not in input_df.columns:
input_df["Status"] = ""
print(f"Tìm thấy tổng cộng {len(input_df)} dòng dữ liệu trong file cần kiểm tra.")
except Exception as e:
print(f"Lỗi khi đọc file Excel đầu vào: {e}")
return
session = create_session()
all_results = []
# Duyệt tuần tự qua từng Index của DataFrame để gán Status tương ứng
for idx, row in input_df.iterrows():
mst = str(row["TaxCode"]).strip()
# Bỏ qua dòng trống không chứa mã số thuế
if not mst or mst == "nan":
continue
print(f"\n==================================================")
print(f"TIẾN HÀNH TRA CỨU [{idx+1}/{len(input_df)}]: MST {mst}")
print(f"==================================================")
# Lấy đồng thời dữ liệu chi tiết (nếu thành công) và thông báo trạng thái tương ứng
rows, status_message = process_single_mst(session, mst, max_attempts=20)
# Cập nhật thông điệp trạng thái vào đúng dòng hiện hành trên RAM
input_df.at[idx, "Status"] = status_message
if rows:
all_results.extend(rows)
print(f" -> Kết quả: {status_message}.")
else:
print(f" -> Kết quả: {status_message}")
# 1. Đóng gói xuất file chi tiết các mã tìm thành công (TaxLookupResult.xlsx)
if all_results:
print(f"\n--- Đang đóng gói dữ liệu chi tiết kết quả tra cứu ---")
final_df = pd.DataFrame(all_results)
export_excel(final_df)
else:
print("\nKhông bóc tách được dữ liệu chi tiết nào.")
# 2. Ghi đè ngược lại toàn bộ DataFrame (đã cập nhật cột Status) về lại file gốc
print(f"\n--- Đang cập nhật trạng thái về lại file danh sách gốc ---")
update_input_file_status(input_df)
print("\n Hoàn thành toàn bộ quy trình tra cứu phối hợp!")
if __name__ == "__main__":
main()
Do mình không có quyền sửa bài viết bài 1 nên mình đăng bài ở đây.
Hôm nay, ngày 22/07/2026, mình xin chia sẻ toàn bộ đoạn code Python tra cứu MST này đến cộng đồng Giải Pháp Excel. Do số lượng các bạn nhờ hỗ trợ cài đặt khá nhiều và quỹ thời gian cá nhân có hạn, mình tải lên sớm để mọi người có thể chủ động tải về nghiên cứu, tùy chỉnh và sử dụng ngay.
Bước 1: Download python về cài đặt ưu tiên phiên bản 3.11 > 3.14 vì hỗ trợ đầy đủ thư viện. Link download Python Releases for Windows | Python.org. Tải bên Python Releases for Windows
Thông thường Python sẽ nằm ở: C:\Users\Ten_User\AppData\Local\Programs\Python
Hoặc nếu bạn cài cho tất cả người dùng: C:\Program Files\Python3x
Bước 2: Tải và cài đặt Tesseract OCR (Dùng để giải Captcha)
Lưu ý: Tesseract thường sẽ nằm ở: C:\Program Files\Tesseract-OCR\tesseract.exe nếu cài chỉ dùng riêng cho user C:\Users\Ten_User\AppData\Local\Programs\Tesseract-OCR\tesseract.exe
Bước 3: Cài đặt các thư viện cần thiết của python:
Mở cửa sổ Command Prompt (CMD) trên máy tính và dán dòng lệnh sau bấm Enter:
Đây là phiên bản mã nguồn mở cơ bản nhất dành cho các bạn yêu thích lập trình tìm hiểu và tự phát triển thêm (ví dụ: đóng gói thành file .exe chạy độc lập không cần cài môi trường Python).
Quy định sử dụng: Tuyệt đối KHÔNG sử dụng mã nguồn này (hoặc các phiên bản nâng cấp từ mã nguồn này) vào bất kỳ mục đích thương mại/thu phí nào. Trường hợp chia sẻ lại công khai trên các nền tảng khác, vui lòng ghi rõ nguồn từ Diễn đàn Giải Pháp Excel (GPE).
Code Python Tra Cứu MST Cá Nhân
Python:
import requests
from PIL import Image
import pytesseract
from bs4 import BeautifulSoup
import pandas as pd
import time
import os
# Thêm thư viện này để tắt các dòng cảnh báo màu đỏ phiền phức khi bypass SSL
from urllib3.exceptions import InsecureRequestWarning
# Tắt cảnh báo InsecureRequestWarning trên toàn bộ hệ thống
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
# ==================================================
# Cấu hình
# ==================================================
URL = "https://tracuunnt.gdt.gov.vn/tcnnt/mstcn.jsp"
CAPTCHA_URL = "https://tracuunnt.gdt.gov.vn/tcnnt/captcha.png?uid="
pytesseract.pytesseract.tesseract_cmd = (
r"C:\Users\Ten_User\AppData\Local\Programs\Tesseract-OCR\tesseract.exe"
)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
INPUT_EXCEL = os.path.join(BASE_DIR, "ListTaxCodeCN_1.xlsx")
OUTPUT_EXCEL = os.path.join(BASE_DIR, "TaxLookupResultCN_1.xlsx")
CAPTCHA_PIC = "captchaCN_1.png"
# ==================================================
# Tạo Session
# ==================================================
def create_session():
session = requests.Session()
# BỎ QUA KIỂM TRA CHỨNG CHỈ SSL CHO TOÀN BỘ SESSION
session.verify = False
session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
})
response = session.get(URL)
print("Khởi tạo Session Status:", response.status_code)
return session
# ==================================================
# Download Captcha
# ==================================================
def download_captcha(session, filename=CAPTCHA_PIC):
captcha_path = os.path.join(BASE_DIR, filename)
print("Đang chờ 1 giây trước khi lấy captcha...")
time.sleep(1)
img = session.get(f"{CAPTCHA_URL}{int(time.time()*1000)}")
with open(captcha_path, "wb") as f:
f.write(img.content)
return captcha_path
# ==================================================
# OCR Captcha
# ==================================================
def read_captcha(filename=CAPTCHA_PIC, show_image=False):
captcha_path = os.path.join(BASE_DIR, filename)
image = Image.open(captcha_path)
if show_image:
image.show()
captcha = pytesseract.image_to_string(
image,
config=r'--oem 3 --psm 8 -c tessedit_char_whitelist=abcdefghijklmnopqrstuvwxyz0123456789'
)
captcha = captcha.lower().strip()
return captcha
# ==================================================
# Tra cứu MST
# ==================================================
def lookup_tax(session, mst, captcha):
payload = {
"cm": "cm",
"mst": mst,
"fullname": "",
"address": "",
"cmt": "",
"captcha": captcha
}
response = session.post(URL, data=payload)
return response.text
# ==================================================
# Parse HTML
# ==================================================
def parse_html(html_text):
soup = BeautifulSoup(html_text, "html.parser")
# 1. Sai mã captcha
error_p = soup.find("p", style=lambda value: value and "color:red" in value.replace(" ", ""))
if error_p:
text_error = error_p.get_text().strip()
if "Vui lòng nhập đúng mã xác nhận" in text_error:
print(" -> Web thông báo: Sai mã Captcha!")
return [], True, False
# Kiểm tra khu vực bảng kết quả
result_div = soup.find("div", id="resultContainer")
if result_div is None:
return [], False, False
table = result_div.find("table")
if not table:
return [], False, False
# 2. Đúng captcha nhưng Không tìm thấy MST phù hợp
table_text = table.get_text()
if "Không tìm thấy người nộp thuế nào phù hợp" in table_text:
print(" -> Web thông báo: Không tìm thấy người nộp thuế (MST không tồn tại)!")
return [], False, True
# 3. Thành công, bóc tách dữ liệu
rows = []
for tr in table.find_all("tr")[1:]:
cols = tr.find_all("td")
if len(cols) != 5:
continue
rows.append({
"STT": cols[0].get_text(strip=True),
"MST": cols[1].get_text(strip=True),
"Tên người nộp thuế": cols[2].get_text(" ", strip=True),
"Cơ quan thuế": cols[3].get_text(" ", strip=True),
"Trạng thái": cols[4].get_text(" ", strip=True)
})
return rows, False, False
# ==================================================
# Quy trình xử lý cho một Mã Số Thuế (Trả về cả trạng thái văn bản)
# ==================================================
def process_single_mst(session, mst, max_attempts=20):
attempt = 0
while attempt < max_attempts:
attempt += 1
print(f" [Thử Captcha lần {attempt}/{max_attempts}]")
download_captcha(session)
captcha = read_captcha(show_image=False)
if not captcha:
print(" OCR trống, thử lại mã khác...")
continue
print(f" OCR đọc được: '{captcha}'")
html_response = lookup_tax(session, mst, captcha)
rows, is_wrong_captcha, is_invalid_mst = parse_html(html_response)
if rows:
return rows, "Tìm thành công"
if is_invalid_mst:
return None, "Web thông báo: Không tìm thấy người nộp thuế (MST không tồn tại)!"
if is_wrong_captcha:
print(" Sai mã Captcha, đang lấy mã mới...")
# Hết số lần thử mà vẫn chưa vượt qua captcha hoặc không có bảng dữ liệu
status_msg = f"Đã thử {max_attempts} lần lỗi captcha cho MST {mst}"
print(f" {status_msg}. Chuyển MST tiếp theo.")
return None, status_msg
# ==================================================
# Xuất dữ liệu bảng chi tiết kết quả tra cứu
# ==================================================
def export_result_excel(df):
try:
df.to_excel(
OUTPUT_EXCEL,
index=False,
engine="openpyxl"
)
print(
f"Đã ghi {len(df)} dòng."
)
except PermissionError:
print(
"Vui lòng đóng file Excel."
)
# ==================================================
# Ghi ngược trạng thái vào file ListTaxCodeCN.xlsx
# ==================================================
def update_input_file_status(df):
try:
df.to_excel(INPUT_EXCEL, sheet_name="Sheet1", index=False, engine="openpyxl")
print(f"-> Đã ghi nhận cột Status ngược lại file gốc: {INPUT_EXCEL}")
except PermissionError:
print("LỖI: Vui lòng đóng file ListTaxCodeCN.xlsx để script ghi trạng thái Status!")
# ==================================================
# Main
# ==================================================
def main():
if not os.path.exists(INPUT_EXCEL):
print(f"Lỗi: Không tìm thấy file {INPUT_EXCEL}!")
return
try:
# Đọc trọn vẹn file đầu vào để giữ lại cấu trúc các cột ban đầu
input_df = pd.read_excel(INPUT_EXCEL, sheet_name="Sheet1", dtype={"TaxCode": str})
if "TaxCode" not in input_df.columns:
print("Lỗi: Không tìm thấy cột có tên 'TaxCode'!")
return
# Tạo sẵn cột Status nếu chưa có trong file
if "Status" not in input_df.columns:
input_df["Status"] = ""
print(f"Tìm thấy tổng cộng {len(input_df)} dòng trong file Excel cần kiểm tra.")
except Exception as e:
print(f"Lỗi khi đọc file Excel đầu vào: {e}")
return
session = create_session()
all_rows_data = [] # List tạm chứa thông tin chi tiết bóc tách từ web
# Duyệt theo Index của DataFrame đầu vào để dễ ghi ngược trạng thái từng dòng
for idx, row in input_df.iterrows():
mst = str(row["TaxCode"]).strip()
# Bỏ qua các dòng trống (NaN) không có MST
if not mst or mst == "nan":
continue
print(f"\n==================================================")
print(f"TIẾN HÀNH TRA CỨU [{idx+1}/{len(input_df)}]: MST {mst}")
print(f"==================================================")
# Quy trình xử lý lấy ra cả dữ liệu (nếu có) và dòng thông báo trạng thái tương ứng
rows, status_message = process_single_mst(session, mst, max_attempts=20)
# Ghi nhận trạng thái vào cột Status tại đúng dòng hiện tại
input_df.at[idx, "Status"] = status_message
if rows:
all_results.extend(rows) if 'all_results' in locals() else all_rows_data.extend(rows)
print(f" -> {status_message}.")
else:
print(f" -> Trạng thái ghi nhận: {status_message}")
# 1. Xuất file chứa thông tin chi tiết bóc tách thành công (TaxLookupResultCN.xlsx)
if all_rows_data:
print(f"\n--- Đang xuất bảng chi tiết người nộp thuế TNCN ---")
final_detail_df = pd.DataFrame(all_rows_data)
export_result_excel(final_detail_df)
# 2. Ghi đè ngược toàn bộ cột Status về lại file danh sách gốc (ListTaxCodeCN.xlsx)
print(f"\n--- Đang ghi cập nhật trạng thái về file danh sách gốc ---")
update_input_file_status(input_df)
print("\n Hoàn thành toàn bộ quy trình phối hợp tra cứu và quản lý!")
if __name__ == "__main__":
main()
Code Python Tra Cứu MST Doanh Nghiệp
Python:
import requests
from PIL import Image
import pytesseract
from bs4 import BeautifulSoup
import pandas as pd
import time
import os
# Thêm thư viện này để tắt các dòng cảnh báo màu đỏ phiền phức khi bypass SSL
from urllib3.exceptions import InsecureRequestWarning
# Tắt cảnh báo InsecureRequestWarning trên toàn bộ hệ thống
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
# ==================================================
# Cấu hình
# ==================================================
URL = "https://tracuunnt.gdt.gov.vn/tcnnt/mstdn.jsp"
CAPTCHA_URL = "https://tracuunnt.gdt.gov.vn/tcnnt/captcha.png?uid="
pytesseract.pytesseract.tesseract_cmd = (
r"C:\Users\Ten_User\AppData\Local\Programs\Tesseract-OCR\tesseract.exe"
)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
INPUT_EXCEL = os.path.join(BASE_DIR, "ListTaxCodeCT_1.xlsx")
OUTPUT_EXCEL = os.path.join(BASE_DIR, "TaxLookupResultCT_1.xlsx")
CAPTCHA_PIC = "captchaCT_1.png"
# ==================================================
# Tạo Session
# ==================================================
def create_session():
session = requests.Session()
# BỎ QUA KIỂM TRA CHỨNG CHỈ SSL CHO TOÀN BỘ SESSION
session.verify = False
session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
})
response = session.get(URL)
print("Khởi tạo Session Status:", response.status_code)
return session
# ==================================================
# Download Captcha
# ==================================================
def download_captcha(session, filename=CAPTCHA_PIC):
captcha_path = os.path.join(BASE_DIR, filename)
print("Đang chờ 1 giây trước khi lấy captcha...")
time.sleep(1)
img = session.get(f"{CAPTCHA_URL}{int(time.time()*1000)}")
with open(captcha_path, "wb") as f:
f.write(img.content)
return captcha_path
# ==================================================
# OCR Captcha
# ==================================================
def read_captcha(filename=CAPTCHA_PIC, show_image=False):
captcha_path = os.path.join(BASE_DIR, filename)
image = Image.open(captcha_path)
if show_image:
image.show()
captcha = pytesseract.image_to_string(
image,
config=r'--oem 3 --psm 8 -c tessedit_char_whitelist=abcdefghijklmnopqrstuvwxyz0123456789'
)
captcha = captcha.lower().strip()
return captcha
# ==================================================
# Tra cứu MST
# ==================================================
def lookup_tax(session, mst, captcha):
payload = {
"cm": "cm",
"mst": mst,
"fullname": "",
"address": "",
"cmt": "",
"captcha": captcha
}
response = session.post(URL, data=payload)
return response.text
# ==================================================
# Parse HTML (Giữ cấu trúc 6 cột cho Tab Doanh nghiệp)
# ==================================================
def parse_html(html_text):
soup = BeautifulSoup(html_text, "html.parser")
# 1. Sai mã captcha
error_p = soup.find("p", style=lambda value: value and "color:red" in value.replace(" ", ""))
if error_p:
text_error = error_p.get_text().strip()
if "Vui lòng nhập đúng mã xác nhận" in text_error:
print(" -> Web thông báo: Sai mã Captcha!")
return [], True, False
# Kiểm tra khu vực bảng kết quả
result_div = soup.find("div", id="resultContainer")
if result_div is None:
return [], False, False
table = result_div.find("table")
if not table:
return [], False, False
# 2. Đúng captcha nhưng Không tìm thấy MST phù hợp
table_text = table.get_text()
if "Không tìm thấy người nộp thuế nào phù hợp" in table_text:
print(" -> Web thông báo: Không tìm thấy người nộp thuế (MST không tồn tại)!")
return [], False, True
# 3. Thành công, bóc tách dữ liệu dạng List (Cấu trúc 6 cột đầy đủ)
rows = []
for tr in table.find_all("tr")[1:]:
cols = tr.find_all("td")
if len(cols) != 6:
continue
rows.append({
"STT": cols[0].get_text(strip=True),
"MST": cols[1].get_text(strip=True),
"Tên người nộp thuế": cols[2].get_text(" ", strip=True),
"Địa chỉ": cols[3].get_text(" ", strip=True),
"Cơ quan thuế": cols[4].get_text(" ", strip=True),
"Trạng thái": cols[5].get_text(" ", strip=True)
})
return rows, False, False
# ==================================================
# Quy trình xử lý cho một Mã Số Thuế
# ==================================================
def process_single_mst(session, mst, max_attempts=20):
attempt = 0
while attempt < max_attempts:
attempt += 1
print(f" [Thử Captcha lần {attempt}/{max_attempts}]")
download_captcha(session)
captcha = read_captcha(show_image=False)
if not captcha:
print(" OCR trống, thử lại mã khác...")
continue
print(f" OCR đọc được: '{captcha}'")
html_response = lookup_tax(session, mst, captcha)
rows, is_wrong_captcha, is_invalid_mst = parse_html(html_response)
if rows:
return rows, "Tìm thành công"
if is_invalid_mst:
return None, "Web thông báo: Không tìm thấy người nộp thuế (MST không tồn tại)!"
if is_wrong_captcha:
print(" Sai mã Captcha, đang lấy mã mới...")
# Hết số lần thử mà vẫn chưa vượt qua captcha hoặc lỗi hệ thống
status_msg = f"Đã thử {max_attempts} lần lỗi captcha cho MST {mst}"
print(f" {status_msg}. Chuyển MST tiếp theo.")
return None, status_msg
# ==================================================
# Xuất dữ liệu bảng chi tiết kết quả tra cứu
# ==================================================
def export_excel(df):
try:
df.to_excel(
OUTPUT_EXCEL,
index=False,
engine="openpyxl"
)
print(
f"Đã ghi {len(df)} dòng."
)
except PermissionError:
print(
"Vui lòng đóng file Excel."
)
# ==================================================
# Ghi ngược trạng thái vào file danh sách gốc
# ==================================================
def update_input_file_status(df):
try:
df.to_excel(INPUT_EXCEL, sheet_name="Sheet1", index=False, engine="openpyxl")
print(f"-> Đã ghi nhận thành công cột Status ngược lại file danh sách gốc: {INPUT_EXCEL}")
except PermissionError:
print("LỖI: Vui lòng đóng file ListTaxCodeCN.xlsx để script có quyền ghi trạng thái Status!")
# ==================================================
# Main (Duyệt theo dòng DataFrame để gán chính xác trạng thái)
# ==================================================
def main():
if not os.path.exists(INPUT_EXCEL):
print(f"Lỗi: Không tìm thấy file {INPUT_EXCEL}!")
return
try:
# Đọc dữ liệu đầu vào và ép cột TaxCode thành chuỗi (String)
input_df = pd.read_excel(INPUT_EXCEL, sheet_name="Sheet1", dtype={"TaxCode": str})
if "TaxCode" not in input_df.columns:
print("Lỗi: Không tìm thấy cột có tên 'TaxCode'!")
return
# Tự động tạo cột Status nếu trong file gốc chưa có sẵn
if "Status" not in input_df.columns:
input_df["Status"] = ""
print(f"Tìm thấy tổng cộng {len(input_df)} dòng dữ liệu trong file cần kiểm tra.")
except Exception as e:
print(f"Lỗi khi đọc file Excel đầu vào: {e}")
return
session = create_session()
all_results = []
# Duyệt tuần tự qua từng Index của DataFrame để gán Status tương ứng
for idx, row in input_df.iterrows():
mst = str(row["TaxCode"]).strip()
# Bỏ qua dòng trống không chứa mã số thuế
if not mst or mst == "nan":
continue
print(f"\n==================================================")
print(f"TIẾN HÀNH TRA CỨU [{idx+1}/{len(input_df)}]: MST {mst}")
print(f"==================================================")
# Lấy đồng thời dữ liệu chi tiết (nếu thành công) và thông báo trạng thái tương ứng
rows, status_message = process_single_mst(session, mst, max_attempts=20)
# Cập nhật thông điệp trạng thái vào đúng dòng hiện hành trên RAM
input_df.at[idx, "Status"] = status_message
if rows:
all_results.extend(rows)
print(f" -> Kết quả: {status_message}.")
else:
print(f" -> Kết quả: {status_message}")
# 1. Đóng gói xuất file chi tiết các mã tìm thành công (TaxLookupResult.xlsx)
if all_results:
print(f"\n--- Đang đóng gói dữ liệu chi tiết kết quả tra cứu ---")
final_df = pd.DataFrame(all_results)
export_excel(final_df)
else:
print("\nKhông bóc tách được dữ liệu chi tiết nào.")
# 2. Ghi đè ngược lại toàn bộ DataFrame (đã cập nhật cột Status) về lại file gốc
print(f"\n--- Đang cập nhật trạng thái về lại file danh sách gốc ---")
update_input_file_status(input_df)
print("\n Hoàn thành toàn bộ quy trình tra cứu phối hợp!")
if __name__ == "__main__":
main()
Em đã dựng và chạy thử thành công, cảm ơn anh đã chỉ dẫn chi tiết tận tình.
Trước đây em đã từng sử dụng Tesseract-OCR để gắp nộp dung trong mấy file PDF, mà em chưa nghĩ tới vụ giải mã capcha như anh, hôm nay em đã học hỏi được thêm. Xin cảm ơn.!
Cám ơn tác giả rất nhiều để không cần cài thêm Tesseract-OCR\tesseract.exe mình đã text thử python314; và các thư viện ddddocr; pandas; openpyxl sau đó biên dịch ra file .exe là chạy tốt.
Lần nữa cám ơn tác giả đã đầu tư công, sức vật lộn với một loạt code để chia sẽ anh em nghiên cứu.
Do mình không có quyền sửa bài viết bài 1 nên mình đăng bài ở đây.
Hôm nay, ngày 22/07/2026, mình xin chia sẻ toàn bộ đoạn code Python tra cứu MST này đến cộng đồng Giải Pháp Excel. Do số lượng các bạn nhờ hỗ trợ cài đặt khá nhiều và quỹ thời gian cá nhân có hạn, mình tải lên sớm để mọi người có thể chủ động tải về nghiên cứu, tùy chỉnh và sử dụng ngay.
Bước 1: Download python về cài đặt ưu tiên phiên bản 3.11 > 3.14 vì hỗ trợ đầy đủ thư viện. Link download Python Releases for Windows | Python.org. Tải bên Python Releases for Windows
Thông thường Python sẽ nằm ở: C:\Users\Ten_User\AppData\Local\Programs\Python
Hoặc nếu bạn cài cho tất cả người dùng: C:\Program Files\Python3x
Bước 2: Tải và cài đặt Tesseract OCR (Dùng để giải Captcha)
Lưu ý: Tesseract thường sẽ nằm ở: C:\Program Files\Tesseract-OCR\tesseract.exe nếu cài chỉ dùng riêng cho user C:\Users\Ten_User\AppData\Local\Programs\Tesseract-OCR\tesseract.exe
Bước 3: Cài đặt các thư viện cần thiết của python:
Mở cửa sổ Command Prompt (CMD) trên máy tính và dán dòng lệnh sau bấm Enter:
Đây là phiên bản mã nguồn mở cơ bản nhất dành cho các bạn yêu thích lập trình tìm hiểu và tự phát triển thêm (ví dụ: đóng gói thành file .exe chạy độc lập không cần cài môi trường Python).
Quy định sử dụng: Tuyệt đối KHÔNG sử dụng mã nguồn này (hoặc các phiên bản nâng cấp từ mã nguồn này) vào bất kỳ mục đích thương mại/thu phí nào. Trường hợp chia sẻ lại công khai trên các nền tảng khác, vui lòng ghi rõ nguồn từ Diễn đàn Giải Pháp Excel (GPE).
Code Python Tra Cứu MST Cá Nhân
Python:
import requests
from PIL import Image
import pytesseract
from bs4 import BeautifulSoup
import pandas as pd
import time
import os
# Thêm thư viện này để tắt các dòng cảnh báo màu đỏ phiền phức khi bypass SSL
from urllib3.exceptions import InsecureRequestWarning
# Tắt cảnh báo InsecureRequestWarning trên toàn bộ hệ thống
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
# ==================================================
# Cấu hình
# ==================================================
URL = "https://tracuunnt.gdt.gov.vn/tcnnt/mstcn.jsp"
CAPTCHA_URL = "https://tracuunnt.gdt.gov.vn/tcnnt/captcha.png?uid="
pytesseract.pytesseract.tesseract_cmd = (
r"C:\Users\Ten_User\AppData\Local\Programs\Tesseract-OCR\tesseract.exe"
)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
INPUT_EXCEL = os.path.join(BASE_DIR, "ListTaxCodeCN_1.xlsx")
OUTPUT_EXCEL = os.path.join(BASE_DIR, "TaxLookupResultCN_1.xlsx")
CAPTCHA_PIC = "captchaCN_1.png"
# ==================================================
# Tạo Session
# ==================================================
def create_session():
session = requests.Session()
# BỎ QUA KIỂM TRA CHỨNG CHỈ SSL CHO TOÀN BỘ SESSION
session.verify = False
session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
})
response = session.get(URL)
print("Khởi tạo Session Status:", response.status_code)
return session
# ==================================================
# Download Captcha
# ==================================================
def download_captcha(session, filename=CAPTCHA_PIC):
captcha_path = os.path.join(BASE_DIR, filename)
print("Đang chờ 1 giây trước khi lấy captcha...")
time.sleep(1)
img = session.get(f"{CAPTCHA_URL}{int(time.time()*1000)}")
with open(captcha_path, "wb") as f:
f.write(img.content)
return captcha_path
# ==================================================
# OCR Captcha
# ==================================================
def read_captcha(filename=CAPTCHA_PIC, show_image=False):
captcha_path = os.path.join(BASE_DIR, filename)
image = Image.open(captcha_path)
if show_image:
image.show()
captcha = pytesseract.image_to_string(
image,
config=r'--oem 3 --psm 8 -c tessedit_char_whitelist=abcdefghijklmnopqrstuvwxyz0123456789'
)
captcha = captcha.lower().strip()
return captcha
# ==================================================
# Tra cứu MST
# ==================================================
def lookup_tax(session, mst, captcha):
payload = {
"cm": "cm",
"mst": mst,
"fullname": "",
"address": "",
"cmt": "",
"captcha": captcha
}
response = session.post(URL, data=payload)
return response.text
# ==================================================
# Parse HTML
# ==================================================
def parse_html(html_text):
soup = BeautifulSoup(html_text, "html.parser")
# 1. Sai mã captcha
error_p = soup.find("p", style=lambda value: value and "color:red" in value.replace(" ", ""))
if error_p:
text_error = error_p.get_text().strip()
if "Vui lòng nhập đúng mã xác nhận" in text_error:
print(" -> Web thông báo: Sai mã Captcha!")
return [], True, False
# Kiểm tra khu vực bảng kết quả
result_div = soup.find("div", id="resultContainer")
if result_div is None:
return [], False, False
table = result_div.find("table")
if not table:
return [], False, False
# 2. Đúng captcha nhưng Không tìm thấy MST phù hợp
table_text = table.get_text()
if "Không tìm thấy người nộp thuế nào phù hợp" in table_text:
print(" -> Web thông báo: Không tìm thấy người nộp thuế (MST không tồn tại)!")
return [], False, True
# 3. Thành công, bóc tách dữ liệu
rows = []
for tr in table.find_all("tr")[1:]:
cols = tr.find_all("td")
if len(cols) != 5:
continue
rows.append({
"STT": cols[0].get_text(strip=True),
"MST": cols[1].get_text(strip=True),
"Tên người nộp thuế": cols[2].get_text(" ", strip=True),
"Cơ quan thuế": cols[3].get_text(" ", strip=True),
"Trạng thái": cols[4].get_text(" ", strip=True)
})
return rows, False, False
# ==================================================
# Quy trình xử lý cho một Mã Số Thuế (Trả về cả trạng thái văn bản)
# ==================================================
def process_single_mst(session, mst, max_attempts=20):
attempt = 0
while attempt < max_attempts:
attempt += 1
print(f" [Thử Captcha lần {attempt}/{max_attempts}]")
download_captcha(session)
captcha = read_captcha(show_image=False)
if not captcha:
print(" OCR trống, thử lại mã khác...")
continue
print(f" OCR đọc được: '{captcha}'")
html_response = lookup_tax(session, mst, captcha)
rows, is_wrong_captcha, is_invalid_mst = parse_html(html_response)
if rows:
return rows, "Tìm thành công"
if is_invalid_mst:
return None, "Web thông báo: Không tìm thấy người nộp thuế (MST không tồn tại)!"
if is_wrong_captcha:
print(" Sai mã Captcha, đang lấy mã mới...")
# Hết số lần thử mà vẫn chưa vượt qua captcha hoặc không có bảng dữ liệu
status_msg = f"Đã thử {max_attempts} lần lỗi captcha cho MST {mst}"
print(f" {status_msg}. Chuyển MST tiếp theo.")
return None, status_msg
# ==================================================
# Xuất dữ liệu bảng chi tiết kết quả tra cứu
# ==================================================
def export_result_excel(df):
try:
df.to_excel(
OUTPUT_EXCEL,
index=False,
engine="openpyxl"
)
print(
f"Đã ghi {len(df)} dòng."
)
except PermissionError:
print(
"Vui lòng đóng file Excel."
)
# ==================================================
# Ghi ngược trạng thái vào file ListTaxCodeCN.xlsx
# ==================================================
def update_input_file_status(df):
try:
df.to_excel(INPUT_EXCEL, sheet_name="Sheet1", index=False, engine="openpyxl")
print(f"-> Đã ghi nhận cột Status ngược lại file gốc: {INPUT_EXCEL}")
except PermissionError:
print("LỖI: Vui lòng đóng file ListTaxCodeCN.xlsx để script ghi trạng thái Status!")
# ==================================================
# Main
# ==================================================
def main():
if not os.path.exists(INPUT_EXCEL):
print(f"Lỗi: Không tìm thấy file {INPUT_EXCEL}!")
return
try:
# Đọc trọn vẹn file đầu vào để giữ lại cấu trúc các cột ban đầu
input_df = pd.read_excel(INPUT_EXCEL, sheet_name="Sheet1", dtype={"TaxCode": str})
if "TaxCode" not in input_df.columns:
print("Lỗi: Không tìm thấy cột có tên 'TaxCode'!")
return
# Tạo sẵn cột Status nếu chưa có trong file
if "Status" not in input_df.columns:
input_df["Status"] = ""
print(f"Tìm thấy tổng cộng {len(input_df)} dòng trong file Excel cần kiểm tra.")
except Exception as e:
print(f"Lỗi khi đọc file Excel đầu vào: {e}")
return
session = create_session()
all_rows_data = [] # List tạm chứa thông tin chi tiết bóc tách từ web
# Duyệt theo Index của DataFrame đầu vào để dễ ghi ngược trạng thái từng dòng
for idx, row in input_df.iterrows():
mst = str(row["TaxCode"]).strip()
# Bỏ qua các dòng trống (NaN) không có MST
if not mst or mst == "nan":
continue
print(f"\n==================================================")
print(f"TIẾN HÀNH TRA CỨU [{idx+1}/{len(input_df)}]: MST {mst}")
print(f"==================================================")
# Quy trình xử lý lấy ra cả dữ liệu (nếu có) và dòng thông báo trạng thái tương ứng
rows, status_message = process_single_mst(session, mst, max_attempts=20)
# Ghi nhận trạng thái vào cột Status tại đúng dòng hiện tại
input_df.at[idx, "Status"] = status_message
if rows:
all_results.extend(rows) if 'all_results' in locals() else all_rows_data.extend(rows)
print(f" -> {status_message}.")
else:
print(f" -> Trạng thái ghi nhận: {status_message}")
# 1. Xuất file chứa thông tin chi tiết bóc tách thành công (TaxLookupResultCN.xlsx)
if all_rows_data:
print(f"\n--- Đang xuất bảng chi tiết người nộp thuế TNCN ---")
final_detail_df = pd.DataFrame(all_rows_data)
export_result_excel(final_detail_df)
# 2. Ghi đè ngược toàn bộ cột Status về lại file danh sách gốc (ListTaxCodeCN.xlsx)
print(f"\n--- Đang ghi cập nhật trạng thái về file danh sách gốc ---")
update_input_file_status(input_df)
print("\n Hoàn thành toàn bộ quy trình phối hợp tra cứu và quản lý!")
if __name__ == "__main__":
main()
Code Python Tra Cứu MST Doanh Nghiệp
Python:
import requests
from PIL import Image
import pytesseract
from bs4 import BeautifulSoup
import pandas as pd
import time
import os
# Thêm thư viện này để tắt các dòng cảnh báo màu đỏ phiền phức khi bypass SSL
from urllib3.exceptions import InsecureRequestWarning
# Tắt cảnh báo InsecureRequestWarning trên toàn bộ hệ thống
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
# ==================================================
# Cấu hình
# ==================================================
URL = "https://tracuunnt.gdt.gov.vn/tcnnt/mstdn.jsp"
CAPTCHA_URL = "https://tracuunnt.gdt.gov.vn/tcnnt/captcha.png?uid="
pytesseract.pytesseract.tesseract_cmd = (
r"C:\Users\Ten_User\AppData\Local\Programs\Tesseract-OCR\tesseract.exe"
)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
INPUT_EXCEL = os.path.join(BASE_DIR, "ListTaxCodeCT_1.xlsx")
OUTPUT_EXCEL = os.path.join(BASE_DIR, "TaxLookupResultCT_1.xlsx")
CAPTCHA_PIC = "captchaCT_1.png"
# ==================================================
# Tạo Session
# ==================================================
def create_session():
session = requests.Session()
# BỎ QUA KIỂM TRA CHỨNG CHỈ SSL CHO TOÀN BỘ SESSION
session.verify = False
session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
})
response = session.get(URL)
print("Khởi tạo Session Status:", response.status_code)
return session
# ==================================================
# Download Captcha
# ==================================================
def download_captcha(session, filename=CAPTCHA_PIC):
captcha_path = os.path.join(BASE_DIR, filename)
print("Đang chờ 1 giây trước khi lấy captcha...")
time.sleep(1)
img = session.get(f"{CAPTCHA_URL}{int(time.time()*1000)}")
with open(captcha_path, "wb") as f:
f.write(img.content)
return captcha_path
# ==================================================
# OCR Captcha
# ==================================================
def read_captcha(filename=CAPTCHA_PIC, show_image=False):
captcha_path = os.path.join(BASE_DIR, filename)
image = Image.open(captcha_path)
if show_image:
image.show()
captcha = pytesseract.image_to_string(
image,
config=r'--oem 3 --psm 8 -c tessedit_char_whitelist=abcdefghijklmnopqrstuvwxyz0123456789'
)
captcha = captcha.lower().strip()
return captcha
# ==================================================
# Tra cứu MST
# ==================================================
def lookup_tax(session, mst, captcha):
payload = {
"cm": "cm",
"mst": mst,
"fullname": "",
"address": "",
"cmt": "",
"captcha": captcha
}
response = session.post(URL, data=payload)
return response.text
# ==================================================
# Parse HTML (Giữ cấu trúc 6 cột cho Tab Doanh nghiệp)
# ==================================================
def parse_html(html_text):
soup = BeautifulSoup(html_text, "html.parser")
# 1. Sai mã captcha
error_p = soup.find("p", style=lambda value: value and "color:red" in value.replace(" ", ""))
if error_p:
text_error = error_p.get_text().strip()
if "Vui lòng nhập đúng mã xác nhận" in text_error:
print(" -> Web thông báo: Sai mã Captcha!")
return [], True, False
# Kiểm tra khu vực bảng kết quả
result_div = soup.find("div", id="resultContainer")
if result_div is None:
return [], False, False
table = result_div.find("table")
if not table:
return [], False, False
# 2. Đúng captcha nhưng Không tìm thấy MST phù hợp
table_text = table.get_text()
if "Không tìm thấy người nộp thuế nào phù hợp" in table_text:
print(" -> Web thông báo: Không tìm thấy người nộp thuế (MST không tồn tại)!")
return [], False, True
# 3. Thành công, bóc tách dữ liệu dạng List (Cấu trúc 6 cột đầy đủ)
rows = []
for tr in table.find_all("tr")[1:]:
cols = tr.find_all("td")
if len(cols) != 6:
continue
rows.append({
"STT": cols[0].get_text(strip=True),
"MST": cols[1].get_text(strip=True),
"Tên người nộp thuế": cols[2].get_text(" ", strip=True),
"Địa chỉ": cols[3].get_text(" ", strip=True),
"Cơ quan thuế": cols[4].get_text(" ", strip=True),
"Trạng thái": cols[5].get_text(" ", strip=True)
})
return rows, False, False
# ==================================================
# Quy trình xử lý cho một Mã Số Thuế
# ==================================================
def process_single_mst(session, mst, max_attempts=20):
attempt = 0
while attempt < max_attempts:
attempt += 1
print(f" [Thử Captcha lần {attempt}/{max_attempts}]")
download_captcha(session)
captcha = read_captcha(show_image=False)
if not captcha:
print(" OCR trống, thử lại mã khác...")
continue
print(f" OCR đọc được: '{captcha}'")
html_response = lookup_tax(session, mst, captcha)
rows, is_wrong_captcha, is_invalid_mst = parse_html(html_response)
if rows:
return rows, "Tìm thành công"
if is_invalid_mst:
return None, "Web thông báo: Không tìm thấy người nộp thuế (MST không tồn tại)!"
if is_wrong_captcha:
print(" Sai mã Captcha, đang lấy mã mới...")
# Hết số lần thử mà vẫn chưa vượt qua captcha hoặc lỗi hệ thống
status_msg = f"Đã thử {max_attempts} lần lỗi captcha cho MST {mst}"
print(f" {status_msg}. Chuyển MST tiếp theo.")
return None, status_msg
# ==================================================
# Xuất dữ liệu bảng chi tiết kết quả tra cứu
# ==================================================
def export_excel(df):
try:
df.to_excel(
OUTPUT_EXCEL,
index=False,
engine="openpyxl"
)
print(
f"Đã ghi {len(df)} dòng."
)
except PermissionError:
print(
"Vui lòng đóng file Excel."
)
# ==================================================
# Ghi ngược trạng thái vào file danh sách gốc
# ==================================================
def update_input_file_status(df):
try:
df.to_excel(INPUT_EXCEL, sheet_name="Sheet1", index=False, engine="openpyxl")
print(f"-> Đã ghi nhận thành công cột Status ngược lại file danh sách gốc: {INPUT_EXCEL}")
except PermissionError:
print("LỖI: Vui lòng đóng file ListTaxCodeCN.xlsx để script có quyền ghi trạng thái Status!")
# ==================================================
# Main (Duyệt theo dòng DataFrame để gán chính xác trạng thái)
# ==================================================
def main():
if not os.path.exists(INPUT_EXCEL):
print(f"Lỗi: Không tìm thấy file {INPUT_EXCEL}!")
return
try:
# Đọc dữ liệu đầu vào và ép cột TaxCode thành chuỗi (String)
input_df = pd.read_excel(INPUT_EXCEL, sheet_name="Sheet1", dtype={"TaxCode": str})
if "TaxCode" not in input_df.columns:
print("Lỗi: Không tìm thấy cột có tên 'TaxCode'!")
return
# Tự động tạo cột Status nếu trong file gốc chưa có sẵn
if "Status" not in input_df.columns:
input_df["Status"] = ""
print(f"Tìm thấy tổng cộng {len(input_df)} dòng dữ liệu trong file cần kiểm tra.")
except Exception as e:
print(f"Lỗi khi đọc file Excel đầu vào: {e}")
return
session = create_session()
all_results = []
# Duyệt tuần tự qua từng Index của DataFrame để gán Status tương ứng
for idx, row in input_df.iterrows():
mst = str(row["TaxCode"]).strip()
# Bỏ qua dòng trống không chứa mã số thuế
if not mst or mst == "nan":
continue
print(f"\n==================================================")
print(f"TIẾN HÀNH TRA CỨU [{idx+1}/{len(input_df)}]: MST {mst}")
print(f"==================================================")
# Lấy đồng thời dữ liệu chi tiết (nếu thành công) và thông báo trạng thái tương ứng
rows, status_message = process_single_mst(session, mst, max_attempts=20)
# Cập nhật thông điệp trạng thái vào đúng dòng hiện hành trên RAM
input_df.at[idx, "Status"] = status_message
if rows:
all_results.extend(rows)
print(f" -> Kết quả: {status_message}.")
else:
print(f" -> Kết quả: {status_message}")
# 1. Đóng gói xuất file chi tiết các mã tìm thành công (TaxLookupResult.xlsx)
if all_results:
print(f"\n--- Đang đóng gói dữ liệu chi tiết kết quả tra cứu ---")
final_df = pd.DataFrame(all_results)
export_excel(final_df)
else:
print("\nKhông bóc tách được dữ liệu chi tiết nào.")
# 2. Ghi đè ngược lại toàn bộ DataFrame (đã cập nhật cột Status) về lại file gốc
print(f"\n--- Đang cập nhật trạng thái về lại file danh sách gốc ---")
update_input_file_status(input_df)
print("\n Hoàn thành toàn bộ quy trình tra cứu phối hợp!")
if __name__ == "__main__":
main()
Công đức vô lượng!!! Xin chân thành cảm ơn bác doanlong49 rất nhiều ! Chúc bác doanlong49 thật nhiều sức khỏe, nhiều niềm vui, luôn luôn gặp may mắn, thành công, hạnh phúc, bình an !
Tôi mới test sơ sơ 100 cái hình captcha thì thấy cái thư viện ddddocr này (đọc 1 lần) nó giải đúng 99% (sai cái hình số 89) đó bạn (có thể cái captcha của trang tracuunnt nó ít phức tạp). Test số lượng nhiều thì chưa có điều kiện để thử xem tỷ lệ giải đúng bao nhiêu % .
Mà cái ddddocr này với thư viện onnx runtime chỉ hỗ trợ được tới Python 13 (Mac os) thôi. Bản cao hơn không cài được nhe.
1/ hai file .py ở bài số 18 thì viết trên Ngôn ngữ nào trả được ... có lẽ tôi sẽ viết C++ Builder
2/ còn xem nguồn tesseract.exe trên Github nó khung khiếp quá có chăng chỉ xem một vài cái xem là cái gì thôi
3/ link sau nó không xuất một Exe duy nhất là có lý do của nó ... suy đoán thôi nó dựa vào nguồn đó tuỳ chỉnh lại chút ít gì đó + kèm theo nhiều DLL khác nữa xong nó đóng gói vào một file cài đặt vì vậy có xin tesseract.exe của ai đó copy sang máy sạch sử dụng chắc chắn cũng tịt
1/ hai file .py ở bài số 18 thì viết trên Ngôn ngữ nào trả được ... có lẽ tôi sẽ viết C++ Builder
2/ còn xem nguồn tesseract.exe trên Github nó khung khiếp quá có chăng chỉ xem một vài cái xem là cái gì thôi
3/ link sau nó không xuất một Exe duy nhất là có lý do của nó ... suy đoán thôi nó dựa vào nguồn đó tuỳ chỉnh lại chút ít gì đó + kèm theo nhiều DLL khác nữa xong nó đóng gói vào một file cài đặt vì vậy có xin tesseract.exe của ai đó copy sang máy sạch sử dụng chắc chắn cũng tịt
Thấy có hai dạng, một là dùng dạng .exe (không cần phải cài đặt thêm mấy cái DLL phụ thuộc), hai là dạng DLL truyền thống. Nói chung là dùng loại nào thì tùy người sử dụng.
Thấy có hai dạng, một là dùng dạng .exe (không cần phải cài đặt thêm mấy cái DLL phụ thuộc), hai là dạng DLL truyền thống. Nói chung là dùng loại nào thì tùy người sử dụng.
chi tiết là gì và ông có file Samples đó không úp lên đây cho mọi người thử xem
rảnh tôi viết hai file .py bài 18 trên Delphi/C++ Builder trong tầm tay rồi ... còn cái vụ cài Tools đó giải mã captra thì đang nghe ngóng xem sao xong tính
viết xong trên Delphi có thể xem xét viết trên Go vì Go cho Network quá tốt
chi tiết là gì và ông có file Samples đó không úp lên đây cho mọi người thử xem
rảnh tôi viết hai file .py bài 18 trên Delphi/C++ Builder trong tầm tay rồi ... còn cái vụ cài Tools đó giải mã captra thì đang nghe ngóng xem sao xong tính
viết xong trên Delphi có thể xem xét viết trên Go vì Go cho Network quá tốt
Máy bạn đang bị thiếu thư viện openpyxl (thư viện giúp Python đọc/ghi file Excel .xlsx).
Cách khắc phục:
Bạn mở cửa sổ Command Prompt (CMD) lên và gõ lệnh sau để cài bổ sung:
py -m pip install openpyxl
Lưu ý: Nếu máy báo lỗi hoặc cài nhiều bản Python, bạn gõ đầy đủ đường dẫn tới file python.exe trên máy bạn nhé:
"C:\Users\Ten_User\AppData\Local\Programs\Python\Pythonxxx\python.exe" -m pip install openpyxl
Sau khi cài xong bạn chạy lại code là sẽ đọc được file Excel bình thường!
Tôi mới test sơ sơ 100 cái hình captcha thì thấy cái thư viện ddddocr này (đọc 1 lần) nó giải đúng 99% (sai cái hình số 89) đó bạn (có thể cái captcha của trang tracuunnt nó ít phức tạp). Test số lượng nhiều thì chưa có điều kiện để thử xem tỷ lệ giải đúng bao nhiêu % .
Mà cái ddddocr này với thư viện onnx runtime chỉ hỗ trợ được tới Python 13 (Mac os) thôi. Bản cao hơn không cài được nhe.
Em nghĩ mình chuyển sang dùng thư viện ddddocr luôn đi anh.
Dùng thư viện này cực kỳ tiện vì nó là thư viện Python thuần (pip install ddddocr), giải captcha dạng số/chữ rất tốt mà mọi người lại không cần phải tải và cài đặt thêm phần mềm Tesseract OCR phức tạp nữa. Bước cấu hình đường dẫn tesseract_cmd cũng bỏ được luôn, giúp người mới dễ sử dụng hơn rất nhiều!
Bài đã được tự động gộp:
Do quyền của tài khoản hiện tại không sửa lại được bài viết hướng dẫn ở trên, mình xin bổ sung thêm một lưu ý nhỏ cho các bạn mới cài Python lần đầu:
Trong quá trình chạy code đọc file Excel .xlsx, nếu máy báo thiếu thư viện openpyxl, các bạn mở Command Prompt (CMD) và gõ câu lệnh sau để cài bổ sung nhé:
Tôi khuyên ông nên dùng vcpkg tích hợp vào Visual Studio, cài đặt tự động hết chỉ cần lo viết mã thôi, chứ tôi thấy nếu dùng C++ Builder thì phức tạp lắm, căn bản việc dựng từ nguồn Tesseract chỉ hỗ trợ những trình biên dịch dưới đây:
Thông thường đối với kế toán hiện nay, ngoài việc tra cứu thông tin thì còn có 1 nhu cầu khác là chụp ảnh màn hình lại, nhằm mục đích lưu lại bằng chứng tại thời điểm tra cứu doanh nghiệp đó vẫn còn hoạt động. Sau này khi đơn vị bên kia không may bị dính vào black list của cơ quan thuế thì công ty còn có bằng chứng để giải trình.
Dựa vào code tác giả em đã dựng 1 file .exe để tra cứu thông tin, mọi thứ chạy mượt mà, duy chỉ có muốn chụp màn hình thì phải thực hiện nhiều request tới thuế hơn, mỗi mã ngoài việc tra cứu, còn thêm cả chục request để tải logo/ảnh/CSS về hiển thị, làm cho lưu lượng tăng gấp nhiều lần. Việc này làm cho thuế block ngắn hạn địa chỉ IP, chỉ tra và chụp khoảng 2-3 mã thì ổn, chứ nhiều quá thì bị block ngay.
Có bác nào có hướng giải quyết không ạ, em mò mò từ qua tới nay mà vẫn đang bó tay.
... muốn chụp màn hình thì phải thực hiện nhiều request tới thuế hơn, mỗi mã ngoài việc tra cứu, còn thêm cả chục request để tải logo/ảnh/CSS về hiển thị, làm cho lưu lượng tăng gấp nhiều lần. ...
Bạn có hình mẫu không, tôi ngoài ngành nên chưa hình dung được cái hình cần chụp phải có logo, ảnh ...như thế nào, không phải là chụp thuần túy screenshot?
Bạn có hình mẫu không, tôi ngoài ngành nên chưa hình dung được cái hình cần chụp phải có logo, ảnh ...như thế nào, không phải là chụp thuần túy screenshot?
Anh vào link sau nha: https://tracuunnt.gdt.gov.vn/tcnnt/mstdn.jsp
Như thường lệ sau khi tra cứu 1 mã số thuế nào đó, kế toán một vài cty sẽ chụp màn hình lại, có ngày giờ máy tính để làm căn cứ xác minh tại thời điểm tra cứu doanh nghiệp đó vẫn còn hoạt động.
Nếu chỉ đơn giản là tra cứu thông tin, code hiện tại sẽ chỉ request để nhận mã capcha, giải và gửi câu trả lời, rồi chờ nhận thông tin phản hồi.
Còn nếu mở trình duyệt lên, thì ngoài request thông tin sẽ còn có các yêu cầu khác như lấy logo, file trang trí CSS/JS, lấy hình nền, nút bấm, icon... số lượng request sẽ nhiều lên và mỗi lần tải 1 mã số thuế là 1 lần request lại từ đầu tất cả, điều này làm cho sever thuế block tạm thời địa chỉ IP, theo em hiểu là vậy.
Thông thường đối với kế toán hiện nay, ngoài việc tra cứu thông tin thì còn có 1 nhu cầu khác là chụp ảnh màn hình lại, nhằm mục đích lưu lại bằng chứng tại thời điểm tra cứu doanh nghiệp đó vẫn còn hoạt động. Sau này khi đơn vị bên kia không may bị dính vào black list của cơ quan thuế thì công ty còn có bằng chứng để giải trình.
Dựa vào code tác giả em đã dựng 1 file .exe để tra cứu thông tin, mọi thứ chạy mượt mà, duy chỉ có muốn chụp màn hình thì phải thực hiện nhiều request tới thuế hơn, mỗi mã ngoài việc tra cứu, còn thêm cả chục request để tải logo/ảnh/CSS về hiển thị, làm cho lưu lượng tăng gấp nhiều lần. Việc này làm cho thuế block ngắn hạn địa chỉ IP, chỉ tra và chụp khoảng 2-3 mã thì ổn, chứ nhiều quá thì bị block ngay.
Có bác nào có hướng giải quyết không ạ, em mò mò từ qua tới nay mà vẫn đang bó tay. View attachment 312140
Làm giao diện nhìn chuyên nghiệp không thua gì phần mềm bán tiền luôn bạn ơi! Cảm ơn đóng góp rất tuyệt vời của bạn cho diễn đàn. Anh em cứ dùng thoải mái và cùng giữ đúng tinh thần phi thương mại nhé!
Nếu nhu cầu chính của bạn là chụp màn hình làm bằng chứng lưu trữ, mình khuyên bạn nên kết hợp hoặc chuyển sang dùng Power Automate Desktop (PAD) cho tác vụ này.
Lý do PAD giải quyết được bài toán này:
Tránh bị block IP: PAD điều khiển trực tiếp trình duyệt thật (Edge/Chrome) thông qua giao diện người dùng (UI), tải trang và giữ session giống hệt thao tác của kế toán thật nên không tạo ra dồn dập các request bất thường như khi chạy bằng Python/Crawler.
Tự động chụp & lưu file dễ dàng: PAD có sẵn các hành động (Actions) rất mạnh về Take Screenshot hoặc Print to PDF, tự động đặt tên file theo MST + Ngày tháng và lưu thẳng vào thư mục máy tính/OneDrive rất mượt mà.
Miễn phí & Có sẵn: PAD tích hợp sẵn trên Windows 11 (hoặc tải miễn phí trên Windows 10), cực kỳ phù hợp cho dân kế toán sử dụng mà không cần cấu hình phức tạp.
Còn giải pháp Python hiện tại, bạn nên để nó tập trung cho nhiệm vụ tra cứu dữ liệu hàng loạt ra file Excel (nhẹ, nhanh, quét được số lượng lớn). Khi cần lưu bằng chứng cho danh sách MST lọc ra, cho PAD chạy quét lại và chụp ảnh là tối ưu nhất!
Còn nếu bạn thích dùng python luôn vẫn có cách làm có thể dùng Playwright / Selenium. Bạn nghiên cứu tiếp xem thế nào.
Thông thường đối với kế toán hiện nay, ngoài việc tra cứu thông tin thì còn có 1 nhu cầu khác là chụp ảnh màn hình lại, nhằm mục đích lưu lại bằng chứng tại thời điểm tra cứu doanh nghiệp đó vẫn còn hoạt động. Sau này khi đơn vị bên kia không may bị dính vào black list của cơ quan thuế thì công ty còn có bằng chứng để giải trình.
Dựa vào code tác giả em đã dựng 1 file .exe để tra cứu thông tin, mọi thứ chạy mượt mà, duy chỉ có muốn chụp màn hình thì phải thực hiện nhiều request tới thuế hơn, mỗi mã ngoài việc tra cứu, còn thêm cả chục request để tải logo/ảnh/CSS về hiển thị, làm cho lưu lượng tăng gấp nhiều lần. Việc này làm cho thuế block ngắn hạn địa chỉ IP, chỉ tra và chụp khoảng 2-3 mã thì ổn, chứ nhiều quá thì bị block ngay.
Có bác nào có hướng giải quyết không ạ, em mò mò từ qua tới nay mà vẫn đang bó tay. View attachment 312140
Anh vào link sau nha: https://tracuunnt.gdt.gov.vn/tcnnt/mstdn.jsp
Như thường lệ sau khi tra cứu 1 mã số thuế nào đó, kế toán một vài cty sẽ chụp màn hình lại, có ngày giờ máy tính để làm căn cứ xác minh tại thời điểm tra cứu doanh nghiệp đó vẫn còn hoạt động.
Nếu chỉ đơn giản là tra cứu thông tin, code hiện tại sẽ chỉ request để nhận mã capcha, giải và gửi câu trả lời, rồi chờ nhận thông tin phản hồi.
Còn nếu mở trình duyệt lên, thì ngoài request thông tin sẽ còn có các yêu cầu khác như lấy logo, file trang trí CSS/JS, lấy hình nền, nút bấm, icon... số lượng request sẽ nhiều lên và mỗi lần tải 1 mã số thuế là 1 lần request lại từ đầu tất cả, điều này làm cho sever thuế block tạm thời địa chỉ IP, theo em hiểu là vậy.
Bạn cho hỏi, do trang tra cứu không có ngày giờ, như vậy phải chụp toàn bộ màn hình Windows, cả ngày giờ ở góc dưới bên phải? Có hợp lệ không nhỉ, vì ngày giờ Windows thay đổi được.
Giờ thấy bà con train model AI này nọ nên mình cũng phải tìm hiểu thêm chút cho đỡ mù AI.
Muốn train nhận diện cái captcha của trang tracuunnt mà ngồi label 1.000 cái file ảnh mới tải về để train thì cũng chua thật (cũng dùng ddddocr nhận dạng xử lý trước rồi). Tôi chỉ dùng code có người ta viết sẳn để train thôi, chứ mà tìm hiểu chi tiết vô nữa thì bó tay.
Tất cả các bước này bạn list toàn bộ các thư viện cầng cài thành 1 file requirement.txt rồi cài đặt bằng
python -m pip install -r requirements.txt
Mã:
# Thu vien cho cong cu tra cuu MST ca nhan/doanh nghiep GPE.
# Ho tro Python 3.11 tro len. Cai bang: python -m pip install -r requirements.txt
requests>=2.31,<3
Pillow>=10,<13
pytesseract>=0.3.10,<0.4
beautifulsoup4>=4.12,<5
pandas>=2.2,<3
openpyxl>=3.1,<4
Code này cũng hay, sử dụng toàn bộ Playwright nên an toàn, ít bị chặn hơn request, không phải giả lập nhiều thứ. Vấn đề tốn thời gian vẫn là OCR, captcha Cục thuế tuy đơn giản nhưng là font vẽ mập ốm khác nhau, thường các ký tự dính liền nhau, nên ddddocr hay Tesseract cũng như nhau, có lúc giải phát một, có lúc thử đi thử lại vài lần mới được.
Code này cũng hay, sử dụng toàn bộ Playwright nên an toàn, ít bị chặn hơn request, không phải giả lập nhiều thứ. Vấn đề tốn thời gian vẫn là OCR, captcha Cục thuế tuy đơn giản nhưng là font vẽ mập ốm khác nhau, thường các ký tự dính liền nhau, nên ddddocr hay Tesseract cũng như nhau, có lúc giải phát một, có lúc thử đi thử lại vài lần mới được.
Theo tôi thấy là tessertact và ddddocr giải đúng ngay 1 lần đó chứ ( không phải làm 100%), chẳng qua là trang web cho không cho kết quả ngay thôi. Tôi nhập trực tiếp bằng tay mà còn có khi đến 5 lần mới có kết quả đó thôi.
Bạn vô trang làm thử là biết ngay thôi.
Theo tôi thấy là tessertact và ddddocr giải đúng ngay 1 lần đó chứ ( không phải làm 100%), chẳng qua là trang web cho không cho kết quả ngay thôi. Tôi nhập trực tiếp bằng tay mà còn có khi đến 5 lần mới có kết quả đó thôi.
Bạn vô trang làm thử là biết ngay thôi.
Cám ơn tác giả rất nhiều để không cần cài thêm Tesseract-OCR\tesseract.exe mình đã text thử python314; và các thư viện ddddocr; pandas; openpyxl sau đó biên dịch ra file .exe là chạy tốt.
Lần nữa cám ơn tác giả đã đầu tư công, sức vật lộn với một loạt code để chia sẽ anh em nghiên cứu.
Theo tôi thấy là tessertact và ddddocr giải đúng ngay 1 lần đó chứ ( không phải làm 100%), chẳng qua là trang web cho không cho kết quả ngay thôi. Tôi nhập trực tiếp bằng tay mà còn có khi đến 5 lần mới có kết quả đó thôi.
Bạn vô trang làm thử là biết ngay thôi.
Trang của Cục thuế sử dụng Load Balancer để phân luồng truy cập, nhưng có vẻ đang bị lỗi. Khi tạo session captcha, sau đó nhận lại kết quả thì có lúc lại gán vào session khác, nên nhập captcha đúng thì vẫn bị lỗi nhập sai captcha. Hậu quả, người dùng web mất thời gian tra cứu, lúc được lúc không, còn ae viết code tra cứu hàng loạt đổ lỗi oan cho OCR như ddddocr, Tessertact, mất thời gian chỉnh sửa.
Trang của Cục thuế sử dụng Load Balancer để phân luồng truy cập, nhưng có vẻ đang bị lỗi. Khi tạo session captcha, sau đó nhận lại kết quả thì có lúc lại gán vào session khác, nên nhập captcha đúng thì vẫn bị lỗi nhập sai captcha. Hậu quả, người dùng web mất thời gian tra cứu, lúc được lúc không, còn ae viết code tra cứu hàng loạt đổ lỗi oan cho OCR như ddddocr, Tessertact, mất thời gian chỉnh sửa.
Mình cũng kiểm tra nhưng chưa tìm ra qui tắc. Cho ddddocr giải mã captcha từ web Cục thuế thì đúng 100%. Nhưng khi ráp vào tra cứu, vẫn bị báo sai kha khá. Tạm thời thì thử nhiều lần vẫn được trả lời thành công. Nếu Cục thuế fix lỗi này thì tốc độ tra cứu sẽ nhanh hơn.