背景 記賬強迫症患者,苦於賬本上的信用卡額度總跟實際的對不上,python小白的我決定寫個小demo輔助對賬。 涉及 python BeautifulSoup SQLite 準備 信用卡賬單eml(這裡用的J行) 錢跡賬單csv 關鍵步驟 解析並處理信用卡賬單 使用BeautifulSoup組件,解 ...
背景
記賬強迫症患者,苦於賬本上的信用卡額度總跟實際的對不上,python小白的我決定寫個小demo輔助對賬。
涉及
- python BeautifulSoup
- SQLite
準備
- 信用卡賬單eml(這裡用的J行)
- 錢跡賬單csv
關鍵步驟
解析並處理信用卡賬單
使用BeautifulSoup組件,解析賬單eml
# 讀取賬單eml
eml = open(source_path).read()
# 使用Parser解析eml
content = Parser().parsestr(eml)
bill = ""
# 深度優先遍歷
for par in content.walk():
# 消息的有效內容是一個子EmailMessage對象的列表,則返回True,否則返回False
if not par.is_multipart():
content = par.get_payload(decode=True)
if len(content.strip()) != 0:
# 這裡,會得到唯一的一個包含賬單的html字元串
bill = content.decode(encoding='gbk')
# 這裡需要重點註意
# 使用BeautifulSoup轉化前,需要事先將換行符去掉
# 否則,帶有換行符節點的標簽對象會解析不出來,直接變成None
data = BeautifulSoup(bill.replace('<br>', '').replace('<br/>', ''), "html.parser")
搜索賬單列表。通過分析賬單,還款明細的開頭如下所示是一個id為takeList的tbody
<tbody id=takeList>
然後,這個tbody還會包含一個唯一的tbody,這個tbody下麵就是一條一條的還款明細了
# 得到還款明細列表
repayList = data.find("tbody", id="repayList").find("tbody")
同理,也能得到消費列表
takeList = data.find("tbody", id="takeList").find("tbody")
逐條解析消費明細,得到交易列表
bills = []
repayAmount = Decimal(0.00)
for repay in repayList.children:
# NavigableString類型,就是沒有子節點的字元串
# BeautifulSoup會將註釋也解析進去,主要就是為了排除註釋
if not isinstance(repay, NavigableString):
bill = {}
bill["type"] = "repay"
for item in repay.children:
# 匹配日期 MM/dd
if re.match("\d\d/\d\d", str(item.string), flags=0):
bill["time"] = year + "-" + str(item.string).replace("/", "-")
# 匹配金額
elif re.match("CNY\d*\.\d*", str(item.string), flags=0):
amount = str(item.string)[3:]
bill["amount"] = amount
repayAmount += Decimal(float(amount))
bills.append(bill)
為了之後方面對賬,就將上面的數據處理成瞭如下所示格式
{
"month": "2023-02",
"name": "信用卡08月",
"bills": {
"2022-02-22": [{
"type": "repay",
"channel": "BCM",
"time": "2022-02-22",
"amount": "2.00"
}]
},
"size": 1,
"start": "17"
}
處理錢跡賬單
導入SQLite
為了方便搜索數據,我用錢跡賬單csv生成了SQLite資料庫文件
搜索錢跡數據
current = current + relativedelta(days=1)
endtime = current.strftime("%Y-%m-%d") + " 00:00:00"
conn = sqlite3.connect(db_dir)
cur = conn.cursor()
cur.execute("select *from qian_ji qj where 時間 >= '" + starttime + "' and 時間 < '" + endtime + "' and 賬戶1 ='" + 賬戶名稱 + "'")
比對數據
因為信用卡的賬單是一個月一個,所以我這邊也是一次比對一個月。
從賬單日第一天開始,一直到下個月的賬單日前一天,逐日分別計算當天信用卡和錢跡的交易凈值並比對
感想
算是花了不少時間,實際最後也沒做出啥像樣的東西,但至少整個過程下來,也是學到了不少東西。
至少,目的達成了————成功阻止了我自己手動去改賬本上的賬戶金額。
後續再繼續研究下其他渠道的賬單。