Power BIでWebスクレイピングを行う

# 本記事の目的

コロナ禍に対して、国や都道府県だけでなく各市区町村も、消費活動を活発化させるための様々なキャンペーンを実施しています。鎌倉市では地元の店舗で利用可能な商品券を市民に配布する「縁結びカード」という事業が実施されています。

>鎌倉応援買い物・飲食電子商品券「縁（えん）むすびカード」事業
https://www.enmusubi-card.com/
>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/7c10be52-09e9-e720-fed0-d4af41710fa2.png)

利用できるのは地元の飲食店くらいかと思いきや、業種一覧を見てみると「建設業」（！？）なども入っています。2020年12月9日時点での利用可能な店舗数は1,200程度とかなり多くあり、利用者としては有難い限りです。

ただ、実際に利用できる店舗を検索しようとすると、こんな感じの表になっていて正直見づらい...。「近所で利用できる店舗ないかな」と思うと、近くの町名を順番に入れていって検索するしかなさそうです。

>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/6413e0f1-570e-b2f9-9743-a3c3b1e76c8f.png)

1ページあたり最大18店舗分の情報があり、これ以降はページ送りしながら順番に見ていくことになります。
現時点で全部で67ページほどあるので、これを全部見ていくのは骨が折れます。
>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/6290db35-f4ee-adb7-d9e9-5116f94c444d.png)

そこで今回は、縁むすびカードの対象店舗の一覧をマップ上にプロットすることを考えたいと思います。

結論だけ先に書きます。

- Power BIを使って「縁むすびカード」の対象店舗の情報をウェブスクレイピングできる
- 住所情報を含むリストがあれば、Google Maps上に対象店舗の情報をプロットできる

できたマップはこんな感じです。
https://www.google.com/maps/d/edit?mid=1I7WiAAm5JBOzZM1dF--9hVRJDTDxyAZU&usp=sharing
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/86bab376-098a-6c0b-6755-93885c3f514f.png)


# アプローチ

以降、2段階でのアプローチを考えます。

1. ウェブサイトからの情報取得（ウェブスクレイピング）を行う
2. 取得した情報を地図上にプロットして整理する

## 1. ウェブスクレイピング（Power BI）
まずはWebサイトから必要なデータを集めてくる部分ですが、これは色々な方法があります。

- UiPathやautomation anywhereなどのRPA
- Python (requestsやbeautifulsoup)
- ExcelのVBA
- Power BIのWebコネクタ

別にどれを使っても良いのですが、「もしかしてPower BIであればそのまま可視化（マップ化）までできるのでは？」という淡い期待があり、今回はPower BIでのウェブスクレイピングを試してみました。

以下ではその具体的な方法について紹介します。

#### 1ページ分の情報を取得
まずは単純に「Webコネクタ」を使って今回の対象URLから情報を取得します。
対象URL: https://www.enmusubi-card.com/list/?n=&t=&c=&page=1
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/12a42778-7d03-cc88-880d-cbaf855f0df2.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/5dc52c25-3258-1f0d-a37d-a5b4b59b46fe.png)
この時点で既に、ページからちゃんとHTML構造を把握してデータを取得してきてくれていることが分かります。
「データの変換」を押して、次の処理に進みましょう。
（もし「読み込み」を押してしまったら、画面上の「データの変換」を押せばOKです）

#### 複数ページ分の情報が取得できるようにパラメータ化した関数をつくる
先ほどはURLを決め打ちで指定しましたが、実際には複数のページからデータを取得します。Webページを確認すると、単純なルールで複数ページのURLが取得できることが分かります。そこで<font color="Red">末尾の数字</font>の部分をパラメータ化しておきます。

- <font color="Red">1</font>ページ目: https://www.enmusubi-card.com/list/?n=&t=&c=&page=1
- <font color="Red">2</font>ページ目: https://www.enmusubi-card.com/list/?n=&t=&c=&page=2
- <font color="Red">3</font>ページ目: https://www.enmusubi-card.com/list/?n=&t=&c=&page=3

画面上部のリボンにある「詳細エディター」を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/948236cc-d39b-f9ad-3032-9971a35bfedc.png)

するとWeb.BrowserContentsを使って、指定のURLが開かれていることが分かります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/04c02dfe-62bc-f622-c10d-d0516212d52d.png)

ここに変数「page」が利用できるようにするために以下のように書き換えます。

``` PowerQuery:編集前
let
    ソース = Web.BrowserContents("https://www.enmusubi-card.com/list/?n=&t=&c=&page=1"),
    #"HTML から抽出されたテーブル" = Html.Table(ソース, {{"Column1", ".category"}, {"Column2", "H3"}, {"Column3", "P"}, {"Column4", ".address"}, {"Column5", ".tel"}, {"Column6", ".hours"}, {"Column7", ".closed"}, {"Column8", ".btDetail *"}, {"Column9", ".exLink"}}, [RowSelector=".listBox"]),
    変更された型 = Table.TransformColumnTypes(#"HTML から抽出されたテーブル",{{"Column1", type text}, {"Column2", type text}, {"Column3", type text}, {"Column4", type text}, {"Column5", type text}, {"Column6", type text}, {"Column7", type text}, {"Column8", type text}, {"Column9", type text}})
in
    変更された型
```

``` PowerQuery:編集後
(page as number) => 
let
    ソース = Web.BrowserContents("https://www.enmusubi-card.com/list/?n=&t=&c=&page="& Number.ToText(page)),
    #"HTML から抽出されたテーブル" = Html.Table(ソース, {{"Column1", ".category"}, {"Column2", "H3"}, {"Column3", "P"}, {"Column4", ".address"}, {"Column5", ".tel"}, {"Column6", ".hours"}, {"Column7", ".closed"}, {"Column8", ".btDetail *"}, {"Column9", ".exLink"}}, [RowSelector=".listBox"]),
    変更された型 = Table.TransformColumnTypes(#"HTML から抽出されたテーブル",{{"Column1", type text}, {"Column2", type text}, {"Column3", type text}, {"Column4", type text}, {"Column5", type text}, {"Column6", type text}, {"Column7", type text}, {"Column8", type text}, {"Column9", type text}})
in
    変更された型
```

変更点は2点

1. 冒頭に<font color="Red">(page as number) =></font>を追記
2. page=1の代わりに<font color="Red">page=& Number.ToText(page)</font>

これで与えられたパラメータ（page）に対する情報を取得できるようになりました。実際にパラメータとして例えば10（=10ページ目）を入力して「呼び出し」を押すと、正しく10ページ目の取得ができるようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/042c6e90-5a8a-d360-d6c9-d88242d625fa.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/3987f767-113a-e800-db62-40d9dd43e278.png)

#### パラメータを準備する
あとは利用するパラメータ（page）を準備するだけです。画面左の「クエリ」部分を右クリックして、空クエリから新しいクエリを作成しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/87e77594-fdc6-92f6-5fdc-17dcbae9f271.png)

新しいクエリで<font color="Red">={1..100}</font>などとすれば、1から100の連続値を持ったリストが作成できます。
今回は取得したいページ数は1~67ですが、今後ページ数が増えることも見越して100までの連続値を作りました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/c3920a3e-90d2-d5ac-615c-f634aa579167.png)

この後の処理のために、リストをテーブルに変換しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/bb542fcc-607d-80d4-35f0-4a753a7a84a3.png)

#### 作成したパラメータと関数を使ってデータを収集する

先ほど複数ページ分の情報が取得できるようにパラメータ化した関数をここで呼び出します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/1f6f8441-e14e-58a2-9ab2-91d9a37d30b6.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/f84d1a3d-3710-2bc9-465c-ec24ee940e71.png)

これでOKをおせば、1行目には1ページ目の情報、2行目には2ページ目の情報が入った形で表が作成されます。
この時点でデータを取得してきている（Webスクレイピングしている）ので、しばらく時間がかかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/c949cc57-3ebe-adff-aa8b-c6d15a81e62d.png)

あとは列の展開をすればOKです。
何も値が取得できなかったページ（今回で言えば68ページ以降）に対しては値がnullになっていますが、不要なのでnullの行は削除するように加工しておきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/d3e3ba7b-e80f-a845-5a77-e8d494aa15cb.png)


無事にPower BIに店舗一覧のデータが取り込めたので可視化してみたいのですが、今回のような自由記述の住所に対して、Power BIのビジュアルでうまくプロットすることはできなさそうでした。

そこで今回は整形したデータだけcsv形式で取得しておき、Google Mapsを用いて地図を作りたいと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/82275ecb-ee66-e071-18be-ef3eae5fcea2.png)



#### 2. 地図へのプロット（Google Maps）

ここからはおまけですが、Googleマップの機能で「マイマップ」を使っていきます。
>https://www.google.co.jp/intl/ja/maps/about/mymaps/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/57f9d82a-c53d-faf7-4377-c8fb6bda4090.png)

「新しい地図を作成」から「インポート」した後は指示通りに進んでいくだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/1a1b569c-c1ca-da6e-a87b-c07863c6c0cd.png)

業種ごとにグループ化してあげればこんな感じで見えます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/97bb0442-bad5-6e4b-f47f-0427ffb93204.png)