Fippiyのプログラム学習内容アウトプットBlog

日々の学習内容をアウトプットして振り返りを実施する。

VBAプログラム開発、スクレイピング・データ取得をレコード単位に修正する【1】カラム毎取得からレコード取得へ変更する

書籍情報一覧情報をスクレイピングしてExcelに表として出力することをしました。

しかし、カラム毎に試行錯誤して作成していたので、いろいろと課題が残っています。

この課題を解決していきます。

今回の目的

データ取得方法を見直し、レコード単位で処理できるプログラムに変更する

なぜやるか

レコードを意識したコードとなっておらず、処理がばらばらになっている。今後の拡張や修正を意識したプログラムにしておくため。

やりたいこと

レコード単位で情報を取得する

取得情報から必要な情報をレコード単位として出力させる

やったこと

現状の課題を整理する

レコード単位でデータを取得する

実施内容

課題を整理する

現状の問題点とはなにか
  • カラム毎に取得しているのでレコード単位の処理にしたい
  • ForEachを多用しすぎている

大別するとこのあたりです。

初めは、とにかくコードを書いてデータを取得して表示できることを考えていました。

しかし、今後の拡張対応等を考えるまえにまずこの問題点を解決しておくべきと考えました。

現状のコードがどうなっているのかをもう少し詳しくみます。

 

現状のコードを確認する

カラム毎にデータが取得されているとはどういうことか?

# スクレイピング

' タイトル名

i = 1
For Each Str In htmlDoc.getElementsByClassName("list-book-title")
 Worksheets("スクレイピング").Cells(i + 1, 2).Value = Str.innerHTML
 i = i + 1
Next Str

 

'書籍詳細
i = 1
For Each Str In htmlDoc.getElementsByClassName("list-book-detail")
 Worksheets("スクレイピング").Cells(i + 1, 3).Value = Str.innerHTML
 i = i + 1
Next Str

 HTML情報を変数に読み込んだ直後のコードです。前半がタイトル名、後半が詳細テキストをそれぞれ取得して書き出しています。

 

この記述でもExcelに一覧表示はできています。しかし、データの取り方が行単位ではなく列単位になっています。

f:id:Fippiy:20190609142236p:plain

カラム単位でデータを取得している

レコード単位で取得していないため、どこかでデータがズレるようなことがあれば、そこから全部おかしなことになりそうです。

コードを見ても、同じ様な記述が繰り返し出現しており、良いコードとはとてもいえません。

 

1つの本の内容としてデータを取得して表示させるので、データとして取得して、そこからカラム毎に反映させる方がいいでしょう。

f:id:Fippiy:20190609142333p:plain

レコード単位で取得

1行にIDやタイトル名などを表示するので、この行単位でデータを取得して、中身を分割すると後でコードを見たときにも1レコードに対しての処理を実施していることがわかります。

あとは、この処理をWebで表示されているデータ分だけ繰り返してあげればいいのです。

カラム毎に繰り返しをしていましたが、データ単位だけの繰り返しになるので、繰り返しの回数も少なくなります。これでForEach多用問題も解決できそうです。

  

レコード単位でデータ取得する

HTMLを確認する

今までは全HTMLから取得したいカラムを直接指定していました。

しかし、今回はレコード単位を意識してデータ取得します。

 

今回スクレイピングしているページはPHP+Laravelを使用し自分で作成した書籍管理サイトの情報です。

書籍一覧はどのように表示していたかというと、コンポーネントを利用してDBからレコード単位でデータを取得し出力していました。

つまり、この出力結果部分のHTMLをそのまま取得してやればレコード単位でデータが扱えそうです。

 

ブラウザのデベロッパーツールでHTML出力を確認します。

f:id:Fippiy:20190609144647j:plain

デベロッパーツール

HTMLはこのようになっています…が見にくいので次をご覧下さい。

 

# HTML

<div class="book-table__list">
 <div class="book-table__list--checkbox">
  <input name="select_books" type="checkbox" value="8">
 </div>
 <div class="book-table__list--picture">
  <a href="/book/8">
   <img src="https://cover.openbd.jp/9784797398892.jpg">
  </a>
 </div>
 <div class="book-table__list--detail">
  <a href="/book/8">

   <h3 class="list-book-title">

    1冊ですべて身につくHTML &amp; CSSとWebデザイン入門講座

   </h3>

  </a>
  <p class="list-book-detail">

   日本で2年間...

  </p>
 </div>
</div>

 

<div class="book-table__list">…</div>

<div class="book-table__list">…</div>

<div class="book-table__list">…</div>

 …

<div class="book-table__list">…</div>

青色で記載しているのが1レコード目のHTMLです。

"book-table__list"というクラス名のdivタグ内に書籍情報が入っています。そして、これが連続して20件分記載されています。

つまり、"book-table__list"クラスでオブジェクト取得してやれば、レコード単位でデータが取得できそうです。

 

レコード単位でオブジェクトを取得する

"book-table__list"クラスを指定すれば、レコード単位として欲しいデータがそろいそうでしたので、このクラスを指定してデータを取得してみます。 

確認の為、取得したデータをそのままExcel上にテキスト表示させてみました。

 

# スクレイピング

'レコード単位出力をためす
 For Each Str In htmlDoc.getElementsByClassName("book-table__list")
  Worksheets("テスト").Cells(i + 1, 2).Value = Str.innerHTML
  i = i + 1
 Next Str

取得HTMLからgetElementsByClassName("book-table__list")として、データを取得し、ForEachによって取得データ毎に結果を表示させる記述としました。表示確認用のため、ワークシートを変更しています。

f:id:Fippiy:20190609150118j:plain

レコード取得結果

"book-table__list"を指定することで、その中のHTMLが全て取得できました。先程のHTMLを確認してみると…。

# HTML

<div class="book-table__list">
 <div class="book-table__list--checkbox">
  <input name="select_books" type="checkbox" value="8">
 </div>
 <div class="book-table__list--picture">
  <a href="/book/8">
   <img src="https://cover.openbd.jp/9784797398892.jpg">
  </a>
 </div>
 <div class="book-table__list--detail">
  <a href="/book/8">

   <h3 class="list-book-title">

    1冊ですべて身につくHTML &amp; CSSとWebデザイン入門講座

   </h3>

  </a>
  <p class="list-book-detail">

   日本で2年間...

  </p>
 </div>
</div>

 

<div class="book-table__list"></div>

<div class="book-table__list"></div>

<div class="book-table__list"></div>

 …

<div class="book-table__list"></div>

赤色の部分が取得できExcel上にテキスト出力されています。

ここから、必要な情報を各セル単位で出力できればレコード単位処理となり、なおかつForEachの使用回数も少なくできそうです。

 

レコード単位でのデータ取得ができたので、この中から個別に必要な要素をとりだす形に変更することで、レコード単位処理を実現します。詳細は次回記事へ記載します。