PowerQuery 中使用 M 語言創(chuàng)建自定義函數(shù)爬取天氣后報(bào)網(wǎng)頁

209190509更新
更新原因:實(shí)際使用下來有 bug,Power Query 對(duì)數(shù)據(jù)格式非常嚴(yán)格,月份輸入不是文本格式會(huì)報(bào)錯(cuò)。
更新內(nèi)容:使用 Text.From 函數(shù)將月份先轉(zhuǎn)換為文本然后做判斷長度和補(bǔ)足兩位的操作,修正了 bug;新加步驟轉(zhuǎn)換氣溫為數(shù)字格式;
因?yàn)?0190508中央氣象臺(tái)出現(xiàn)了0512最高溫39度的異常預(yù)報(bào),為了證明是錯(cuò)誤值,就簡(jiǎn)單做了一個(gè)上海月極值天氣的透視表,可以忽略。
某盤文件下載2 4dif

關(guān)于月份補(bǔ)足到兩位其實(shí) M 語言中有現(xiàn)成的函數(shù)可以用,可以參考 Text.PadStart/End 函數(shù)簡(jiǎn)介,但是我懶得改代碼了,就依然是原有的 If Else 條件判斷。

原文


說明:
excel 2016 最早期的 Power Query 有部分函數(shù)不支持,可能無法運(yùn)行。建議升級(jí)到最新。
2010 和 2013 版本的 Power Query 沒有測(cè)試。
作者有一點(diǎn)兒 Power Query 使用經(jīng)驗(yàn),M 語言只學(xué)了一天,做了這個(gè)模板,所以用這個(gè)代碼的注意檢查數(shù)據(jù)條數(shù)。
懶得寫的特別詳細(xì),所以不針對(duì)一點(diǎn)兒不懂 Power Query 的人。
某盤文件下載 f665

前期思路和準(zhǔn)備

要爬的網(wǎng)頁信息:天氣后報(bào)

URL 舉例:http://www.tianqihoubao.com/lishi/beijing/month/201904.html

拼音 beijing 用來控制是什么城市,201904 是年月。

這樣要爬的 URL 要設(shè)置三個(gè)變量,城市、年份、月份。理論上年份和月份可以是固定的 2011-2019 ,月份也可以是固定的 01-12 月。 考慮到每次都爬 9 年 12 個(gè)月的數(shù)據(jù)(多城市數(shù)據(jù)就更多)比較費(fèi)時(shí)間,大部分使用場(chǎng)景可能也不需要看這么久時(shí)間的天氣數(shù)據(jù),就設(shè)置可以自定義查詢。自定義輸入是通過引用 excel 的表實(shí)現(xiàn)的。

測(cè)試 Excel 表中直接輸入年份月份會(huì)默認(rèn)為數(shù)字、月份即使輸入01,表中也是默認(rèn)為 1。所以需要通過月份的位數(shù)判斷是不是要在前面加一位 0 。然后轉(zhuǎn)換年和月為文本格式。

測(cè)試Excel 表中直接刪除數(shù)據(jù)會(huì)形成空行,要求使用者右鍵刪除表行又多了好幾步操作,所以需要刪選三個(gè)表中的空行。

城市列表、年份、月份表格樣式

上述三個(gè)表名稱分別為:城市清單、年份、月份。


全部代碼

上面三個(gè)自定義參數(shù)的表設(shè)置后,直接復(fù)制以下代碼到 PowerQuery 的高級(jí)編輯器中可以直接保存運(yùn)行。

let    
    tianqi = (city,year,month)=>Table.PromoteHeaders(Web.Page(Web.Contents("http://www.tianqihoubao.com/lishi/"&city&"/month/"&year&month&".html")){0}[Data], [PromoteAllScalars=true]),
    source = Excel.CurrentWorkbook(){[Name="城市清單"]}[Content],
    添加年份 = Table.AddColumn(source, "yea", each Excel.CurrentWorkbook(){[Name="年份"]}[Content]),
    添加月份 = Table.AddColumn(添加年份, "mont", each Excel.CurrentWorkbook(){[Name="月份"]}[Content]),
    展開年份 = Table.ExpandTableColumn(添加月份, "yea", {"年份"}, {"年份"}),
    展開月份 = Table.ExpandTableColumn(展開年份, "mont", {"月份"}, {"月份"}),
    篩選拼音城市年月列不為空 = Table.SelectRows(展開月份, each [cit] <> null and [年份] <> null and [月份] <> null),
    判斷月份是否要加0 = Table.AddColumn(篩選拼音城市年月列不為空, "月份2", each if Text.Length([月份]) = 1 then "0"&[月份] else [月份]),
    更改年月列為文本 = Table.TransformColumnTypes(判斷月份是否要加0,{{"年份", type text}, {"月份2", type text}}),
    添加網(wǎng)頁表格 = Table.AddColumn(更改年月列為文本, "data", each tianqi([cit],[年份],[月份2])),
    展開網(wǎng)頁表格 = Table.ExpandTableColumn(添加網(wǎng)頁表格, "data", {"日期", "天氣狀況", "氣溫", "風(fēng)力風(fēng)向"}, {"日期", "天氣狀況", "氣溫", "風(fēng)力風(fēng)向"}),
    拆分天氣列 = Table.SplitColumn(展開網(wǎng)頁表格, "天氣狀況", Splitter.SplitTextByDelimiter("/", QuoteStyle.Csv), {"白天天氣", "夜間天氣"}),
    拆分氣溫列 = Table.SplitColumn(拆分天氣列, "氣溫", Splitter.SplitTextByDelimiter("/", QuoteStyle.Csv), {"最高氣溫", "最低氣溫"}),
    拆分風(fēng)向列 = Table.SplitColumn(拆分氣溫列, "風(fēng)力風(fēng)向", Splitter.SplitTextByDelimiter("/", QuoteStyle.Csv), {"白天風(fēng)向", "夜晚風(fēng)向"}),
    刪除攝氏度單位 = Table.ReplaceValue(拆分風(fēng)向列,"℃"," ",Replacer.ReplaceText,{"最高氣溫", "最低氣溫"}),
    日期列變更為日期格式 = Table.TransformColumnTypes(刪除攝氏度單位,{{"日期", type date}}),
    篩選日期為小于等于當(dāng)前日期 = Table.SelectRows(日期列變更為日期格式, each [日期] <= DateTime.Date(DateTime.LocalNow())),
    刪除的列 = Table.RemoveColumns(篩選日期為小于等于當(dāng)前日期,{"cit", "年份", "月份", "月份2"})
in
    刪除的列

代碼分步驟解釋

  1.    tianqi = (city,year,month)=>Table.PromoteHeaders(Web.Page(Web.Contents("http://www.tianqihoubao.com/lishi/"&city&"/month/"&year&month&".html")){0}[Data], [PromoteAllScalars=true]),
    

這里在開頭創(chuàng)建了一個(gè)自定義函數(shù) tianqi,參數(shù)有三個(gè) city,year, month 。使用 Web.Page 來獲得網(wǎng)頁表格,Table.PromoteHeaders 用來將第一行用作標(biāo)題。
網(wǎng)頁表格,抓起來一般是沒有標(biāo)題的,column1、column2 的類似標(biāo)題,如果爬出來之后再提升標(biāo)題,前面已經(jīng)設(shè)置好標(biāo)題的列會(huì)因?yàn)闃?biāo)題提升失去固定的標(biāo)題。

  1.  source = Excel.CurrentWorkbook(){[Name="城市清單"]}[Content],
     添加年份 = Table.AddColumn(source, "yea", each Excel.CurrentWorkbook(){[Name="年份"]}[Content]),
     添加月份 = Table.AddColumn(添加年份, "mont", each Excel.CurrentWorkbook(){[Name="月份"]}[Content]),
     展開年份 = Table.ExpandTableColumn(添加月份, "yea", {"年份"}, {"年份"}),
     展開月份 = Table.ExpandTableColumn(展開年份, "mont", {"月份"}, {"月份"}),
    

按照三個(gè) excel 表名稱添加展開三個(gè)自定義參數(shù)的表。這幾個(gè)步驟運(yùn)行完的結(jié)果:


添加城市、年、月之后
  1.  篩選拼音城市年月列不為空 = Table.SelectRows(展開月份, each [cit] <> null and [年份] <> null and [月份] <> null),
     判斷月份是否要加0 = Table.AddColumn(篩選拼音城市年月列不為空, "月份2", each if Text.Length([月份]) = 1 then "0"&[月份] else [月份]),
     更改年月列為文本 = Table.TransformColumnTypes(判斷月份是否要加0,{{"年份", type text}, {"月份2", type text}}),
    

如步驟名稱,是開頭提過的要對(duì)三個(gè)參數(shù)源表的處理。運(yùn)行結(jié)果:


參數(shù)處理
  1.  添加網(wǎng)頁表格 = Table.AddColumn(更改年月列為文本, "data", each tianqi([cit],[年份],[月份2])),
     展開網(wǎng)頁表格 = Table.ExpandTableColumn(添加網(wǎng)頁表格, "data", {"日期", "天氣狀況", "氣溫", "風(fēng)力風(fēng)向"}, {"日期", "天氣狀況", "氣溫", "風(fēng)力風(fēng)向"}),
    

使用開頭聲明的自定義函數(shù)添加一列,然后展開列中表格包含的每一列。


初步數(shù)據(jù)抓取

到這一步,抓取數(shù)據(jù)的部分就做完了,接下來是正常使用 Power Query 處理數(shù)據(jù)的部分。

  1.  拆分天氣列 = Table.SplitColumn(展開網(wǎng)頁表格, "天氣狀況", Splitter.SplitTextByDelimiter("/", QuoteStyle.Csv), {"白天天氣", "夜間天氣"}),
     拆分氣溫列 = Table.SplitColumn(拆分天氣列, "氣溫", Splitter.SplitTextByDelimiter("/", QuoteStyle.Csv), {"最高氣溫", "最低氣溫"}),
     拆分風(fēng)向列 = Table.SplitColumn(拆分氣溫列, "風(fēng)力風(fēng)向", Splitter.SplitTextByDelimiter("/", QuoteStyle.Csv), {"白天風(fēng)向", "夜晚風(fēng)向"}),
    

這里是拆分原表的列,Power Query 的內(nèi)建函數(shù) Table.SplitColumn 似乎沒辦法一次拆分多個(gè)列,原默認(rèn)代碼中的拆分后列名為 天氣狀況.1 和 天氣狀況.2,這里就是一個(gè)重命名的設(shè)置,為了減少步驟,這里我自定義了拆分后列名。

拆分后
  1.  刪除攝氏度單位 = Table.ReplaceValue(拆分風(fēng)向列,"℃"," ",Replacer.ReplaceText,{"最高氣溫", "最低氣溫"}),
    

拆分后的氣溫包含攝氏度單位,這里使用替換為 null 的方式刪除符號(hào)。


溫度符號(hào)處理
  1.  日期列變更為日期格式 = Table.TransformColumnTypes(刪除攝氏度單位,{{"日期", type date}}),
     篩選日期為小于等于當(dāng)前日期 = Table.SelectRows(日期列變更為日期格式, each [日期] <= DateTime.Date(DateTime.LocalNow())),
    

原網(wǎng)頁中的數(shù)據(jù)如果是超過當(dāng)前日期也會(huì)抓出來空白的內(nèi)容,所以這列做了一個(gè)條件篩選,篩選日期小于等于查詢運(yùn)行當(dāng)天日期的行。原抓出來的日期列為文本,這里先轉(zhuǎn)換為日期格式,然后做篩選。

M 語言中沒有工作表函數(shù) now() 來表示當(dāng)前時(shí)間,表示當(dāng)前日期(不含時(shí)間)是 DateTime.Date(DateTime.LocalNow())

日期篩選前

  1.  刪除的列 = Table.RemoveColumns(篩選日期為小于等于當(dāng)前日期,{"cit", "年份", "月份", "月份2"})
    

刪除無用的列。

刪除前

刪除后

加載到 excel 工作表的數(shù)據(jù)

最后結(jié)果

參考:
1.excelhome 上網(wǎng)友分享教程
2.pqfans 自定義函數(shù)文章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容