2023年7月1日 星期六

台中市環保地圖

最近想要清家裡, 把一堆舊衣服、 舊電器、 電腦週邊等等拿去回收。 於是搜尋到 iTaichung 環保地圖。 可是它把每個店家獨立顯示在一張地圖上, 感覺很難用。 於是製作了 台中環保商店地圖, 把所有的店家放在同一張地圖上, 並且在這裡記錄一下步驟, 順便當作網頁爬蟲/資料視覺化的教材。

  1. 點前幾頁, 再點到最後一頁, 看出簡單的網址規則。 先產生一個頁碼檔 pn.txt:
    perl -e 'for $i (1..207) { print("$i\n") }' > pn.txt 或是
    python3 -c 'for i in range(1,208): print(i)' > pn.txt
  2. [請省略這一步, 減輕官網負擔。 請改直接下載 我整理過的、 含有 207 個 html 檔以及後續中間步驟的綜合壓縮檔。]
    我當初這樣下載 207 個 html 檔:
    for i in $(cat pn.txt) ; do wget -O p$i.html "http://不要抄這句?pg=$i" ; sleep 5 ; done
  3. 頁面位數不同, 看起來不順眼。 先把一位數通通改成兩位數:
    ls p?.html | perl -pe 's/p(\d)\.html/mv p$1.html p0$1.html/'
    如果看起來沒問題, 再把上面的結果 pipe 給 bash。
  4. 同樣地, 再把兩位數通通改成三位數:
    ls p??.html | perl -pe 's/p(\d\d)\.html/mv p$1.html p0$1.html/'
  5. 把所有的 html 檔逐一轉成 csv、 全部串成一個大檔 summary.csv:
    for f in p*.html ; do html2csv.py $f ; done > summary.csv
  6. 有些欄位裡面有換列字元, 例如 「類別」 欄的每個標籤會被放在獨立的一列。 不過這種換列只有 \n 而每一筆紀錄尾巴的換列則是 \r\n。 可以用 head -n 5 summary.csv | xxd 查看, 注意看有些 0a (就是 \n) 單獨出現, 有些 0a 的前面還有一個 0d, 也就是 \r\n 連著出現。
  7. 把欄位內部的換列字元通通改成 /, 每一筆紀錄尾巴的換列則只留下 \n:
    perl -pe 's#\n#/# ; s#\r/#\n#' summary.csv > summary-2.csv
  8. 刪掉所有的雙引號、 刪掉所有 「非資料列」:
    perl -ne 's/"//g ; print unless m|^//###/類別|' summary-2.csv > summary-3.csv
  9. TGOS 要求每一列最前面要有一個序號。 為方便起見, 我們一律冠上四位數的序號: perl -pe 'print($.+999, ",")' summary-3.csv > summary-4.csv
  10. 哪一列 「沒有 "Map"」 嗎? 應該很少。
    grep -Pv Map summary-4.csv
    哪一列 「有兩個或更多 "Map"」 嗎? 應該是空的
    grep -P 'Map.*Map' summary-4.csv
  11. 進文字編輯器手動把所有的 "Map" 刪掉、 刪掉最後一列 "###"、 把第一列改成: "id,類別,行政區,name,地址,物品類別", 再把它搬到最後一列去, 方便等一下執行 join 指令。
  12. 截取 TGOS 所需要的資訊:
    cut -d, -f 1,5 summary-4.csv > tgos-utf8.csv
    並且手動把標題列搬回最前面。
  13. TGOS 只能吃 big5 編碼的版本, 所以進行 編碼轉檔
    iconv -f utf8 -t big5//TRANSLIT tgos-utf8.csv > tgos-big5.csv
  14. 把 tgos-big5.csv 餵給 地址轉經緯度的 TGOS 服務
  15. 經過幾十分鐘後得到 (utf8 編碼的) 成果檔, 存檔命名為 ans.csv 好了。
  16. 檢查第一列, 發現最前面有一個隱藏的 BOM 字元 「ef bb bf」:
    head -n 1 ans.csv | xxd
    刪掉它: perl -pe 's/^\xEF\xBB\xBF// if $.==1' ans.csv > ans-2.csv
  17. 查看有哪些列是失敗的:
    perl -ne 'print if m/找不到指定的門牌地址/' ans-2.csv
  18. 很多筆失敗的資料都是 「大慶夜市」 或 「旱溪夜市」。 手動查詢這兩處的經緯度, 然後批次修改:
    perl -pe 's/找不到指定的門牌地址。.*/,120.7007454272825,24.1336301361882/ if /大慶夜市/ ; s/找不到指定的門牌地址。.*/,120.70072396961065,24.13360565849847/ if /旱溪夜市/' ans-2.csv > ans-3.csv
  19. 其他的就算了 (喂)
    grep -Pv '找不到指定的門牌地址' ans-3.csv > ans-4.csv
  20. 手動編輯 ans-4.csv , 把第一列改成 "id,addr,address,longitude,latitude" 又把它搬到最後一列去, 方便等一下執行 join 指令。
  21. 水平合併原始資料與經緯度座標兩個檔案:
    join -t , -a 1 -a 2 summary-4.csv ans-4.csv > full.csv
  22. 每個類別各有多少個檔案?
    cut -d, -f 2 full.csv | sort | uniq -c | sort -n
    發現 「不塑商店(提供自備優惠)」 最大宗, 佔了 852 筆。
  23. 想要把這些 「單純只是 "不塑商店(提供自備優惠)"」 的資料拉出來放在一個獨立的圖層。 先確認一下正規表示式有沒有寫錯:
    grep -P ',不塑商店\(提供自備優惠\),' full.csv | wc
  24. 把 full.csv 拆成兩個檔案: grep -P ',不塑商店\(提供自備優惠\),' full.csv > non-plastic.csv
    ckhung@kepler22b:tc-recycle $ grep -Pv ',不塑商店\(提供自備優惠\),' full.csv > env-friendly.csv

    還要記得手動把 env-friendly.csv 的標題列拉回最前面, 又幫 non-plastic.csv 補回標題列 。 確認標題列裡要有一欄名為 「name」。 等一下 umap 顯示圖徵名稱時只認這一欄。
  25. 把兩個檔案上傳到 umap 各自一個圖層。
  26. 把資料分圖層的好處, 就是訪客可以從左側 「檢視資料圖層」 關閉某些沒興趣的圖層。
  27. 如果只對其中某幾類回收站有興趣, 也可以如上使用 grep 指令分次撈出少量資料、 重新製作地圖。

沒有留言:

張貼留言

因為垃圾留言太多,現在改為審核後才發佈,請耐心等候一兩天。