Skip to content

Commit 7b5858e

Browse files
post: pulling album info
Signed-off-by: Tsung-Ju Lii <[email protected]>
1 parent ec23e2f commit 7b5858e

File tree

2 files changed

+93
-1
lines changed

2 files changed

+93
-1
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
---
2+
title: Using Haskell to generate post template (or, using Haskell in inappropriate places)
3+
layout: post
4+
comments: false
5+
tags: about this blog, haskell
6+
---
7+
8+
我前陣子太閒,就想要自動地在我執行 Github workflow 的時候去觸發一個腳本,從一些網路上的資料庫抓我想要寫的專輯的相關資料。一般來說,做這種事情通常是直接用 Python 或是 Bash script + `jq` 之類的,又簡單又好懂,不過我天生喜歡瞎忙,想說既然都在用 Haskell 了,不如也來用 Haskell 寫這腳本。事實證明真的是比用 Python 麻煩很多...
9+
10+
### 從哪裡抓專輯資料?
11+
12+
我本來以為可以從 Bandcamp 去拉,但我寫信去要 API key 時,得到的回覆是沒有公開 API 可以拿到某張專輯的資訊。網上有一些專案好像可以不用爬蟲不用 API key 去拿到專輯資訊,不過看起來有點醜,就覺得算了。最後決定直接從 [Discogs](https://www.discogs.com/developers) 上面抓。做法是先用 `search` 找到某張專輯的 [master release](https://support.discogs.com/hc/en-us/articles/360005055493-Database-Guidelines-16-Master-Release),拿到 master release 的 id,再用這個 id 去查找專輯的發行日期、廠牌跟封面。如果這張專輯太冷門,根本沒有 master release 的話,那麼就直接挑第一個 release,用它的 id 去拿我要的資訊。
13+
14+
### 用 Haskell 解析 JSON 資料
15+
16+
處理 JSON 資料時,Haskell 通常用 [aeson](https://hackage.haskell.org/package/aeson)。使用方式是先定義一個資料結構:
17+
18+
```haskell
19+
data Release = Release {
20+
artists :: [String],
21+
title :: String,
22+
year :: Int,
23+
released :: String,
24+
imageUrl :: String,
25+
labels :: [String],
26+
uri :: String
27+
} deriving (Show, Eq, Generic)
28+
```
29+
30+
接著實作 `ToJSON` 和 `FromJSON``ToJSON` 很簡單直接把資料結構轉成 JSON 格式`FromJSON` 就麻煩多了得詳細定義如何把 JSON 轉成資料結構
31+
32+
```haskell
33+
instance ToJSON Release -- 無腦轉
34+
35+
instance FromJSON Release where
36+
parseJSON (Object v) = do
37+
artists <- v .: "artists" >>= traverse (.: "name") -- "artists" 是個陣列,這裡的意思是取出陣列中每個元素的 "name"
38+
title <- v .: "title" -- .: 表示從 v 中取出 "title" 的值
39+
year <- v .: "year"
40+
released <- v .: "released"
41+
images <- v .: "images"
42+
imageUrl <- case images of
43+
(img:_) -> img .: "resource_url"
44+
[] -> fail "No images found"
45+
labels <- v .: "labels" >>= traverse (.: "name")
46+
uri <- v .: "uri"
47+
return Release {
48+
artists = artists,
49+
title = title,
50+
year = year,
51+
released = released,
52+
imageUrl = imageUrl,
53+
labels = labels,
54+
uri = uri
55+
}
56+
```
57+
58+
可以看出來,如果資料結構很複雜,解析起來就得定義一堆結構來對應。用 Python 的話,這有點像寫一堆 Pydantic class,但每個 class 都得自己實作 `decode`
59+
60+
另一個麻煩點是,當你只想取一個深層的值時,Haskell 就有點麻煩了。對比下面兩行程式碼:
61+
62+
```haskell
63+
body ^? key "results" . nth 0 . key (fromString queryKey) . _Integer
64+
```
65+
66+
67+
68+
```python
69+
int(body["results"][0][queryKey])
70+
```
71+
72+
可能我很淺?但我覺得 Python 的好讀多了。另外如果要在 Haskell 做這種操作,需要引入 [lens-aeson](https://hackage.haskell.org/package/lens-aeson) 套件,原生是不支援的。
73+
74+
### 套件管理
75+
76+
Haskell 的套件管理工具有 cabal 和 stack,感覺有點像 pip 和 poetry,但功能重疊得更多。
77+
78+
### 在 Github workflow 裡執行 Haskell 程式
79+
80+
如果每次在 workflow 裡面跑的時候都要重新 `stack build` (可以想像就是 `make build`)的話會花很多時間,實測下來大概需要二十分鐘左右。真的是太久了... 幸好有個專案就是在做 [stack action](https://github.com/freckle/stack-action/tree/v5/),快取做得蠻好的,所以其實只有第一次提交有改到 Haskell code 的改動時會需要去把整包編好,其他時候就直接用快取的執行檔,那這大概一分鐘之內就能做完。
81+
82+
### 結論
83+
84+
還是用 Python 做這種事情會比較快:
85+
86+
- 比較多人用:Discogs 有提供 Python SDK,甚至不用自己解析 API response。
87+
- Github workflow 設定起來比較不麻煩,而且不會需要花老半天編譯
88+
- JSON support
89+
90+
不過如果你時間很多的話也是個不錯的體驗啦... 所有的程式碼都在 [這裡](https://github.com/usefulalgorithm/usefulalgorithm.github.io/tree/main/scripts/pull_album_info)

scripts/pull_album_info/app/templates/post.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
title: {{artists}} - {{title}}
33
layout: post
44
comments: false
5-
tags: {{year}}
5+
score:
6+
released: {{released}}
7+
tags: {{year}}, music
68
---
79

810
![]({{imageUrl}})

0 commit comments

Comments
 (0)