🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
スクレイピング

スクレイピングとは、公開されているWebサイトからページ内の情報を抽出する技術です。

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

Access

Accessはマイクロソフトによるリレーショナルデータベース管理システムです。オブジェクト指向のアプリケーション作成に対応しており、テーブルや編集をはじめ、クエリ生成、入力フォーム作成、レポート作成など一通りの機能を備えています。

Q&A

解決済

1回答

3648閲覧

AccessVBAのWebスクレイピングにおけるWeb上のテーブルデータをAccessテーブルに書き込みたい

GoogleWindows

総合スコア23

スクレイピング

スクレイピングとは、公開されているWebサイトからページ内の情報を抽出する技術です。

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

Access

Accessはマイクロソフトによるリレーショナルデータベース管理システムです。オブジェクト指向のアプリケーション作成に対応しており、テーブルや編集をはじめ、クエリ生成、入力フォーム作成、レポート作成など一通りの機能を備えています。

0グッド

1クリップ

投稿2019/10/21 00:48

前提・実現したいこと

AccessVBAのWebスクレイピングにおけるWeb上のテーブルデータをAccessテーブルに書き込みたいと思っています。
ExcelVBAにおいては、以下のコードで実現しておりますが、AccessでのRecordsetへの組み込み方法が分かりません。

ソースコード

ExcelVBA

1'Web上のテーブルデータをExcelシートに書き出す関数 2Public Sub WriteTableData(ByVal table As HTMLTable, ByVal cell As Range) 3 4 Dim i As Long: i = 0 5 Dim tr As HTMLTableRow 6 For Each tr In table.getElementsByTagName("tr") 7 8 Dim j As Long: j = 0 9 Dim th As HTMLTableCell 10 For Each th In tr.getElementsByTagName("th") 11 cell.Offset(i, j).Value = th.innerText 12 j = j + 1 13 Next th 14 15 Dim td As HTMLTableCell 16 For Each td In tr.getElementsByTagName("td") 17 cell.Offset(i, j).Value = td.innerText 18 j = j + 1 19 Next td 20 i = i + 1 21 Next tr 22End Sub

ExcelVBA

1'Webスクレイピング処理 2Private Sub table_build() 3 4 Dim objIE As InternetExplorer 5 Dim hUrl As String 6 Dim objShell As Object 7 Dim objExec As Object 8 9 hUrl = "https://" '該当ページのURL ※伏せています 10 11 Set objIE = CreateObject("Internetexplorer.Application") 12 13 objIE.Visible = True 14 Call ieView(objIE, hUrl) 15 Call Wait(objIE) 16 17 Dim htmlDoc As HTMLDocument 18 Set htmlDoc = objIE.Document 19 20  'テーブル書き出し関数呼び出し 21 WriteTableData htmlDoc.getElementsByClassName("Table01")(0), Worksheets("書き出し").Range("A1") 22 23 Call Wait(objIE) 24 25 'IEを閉じる 26 Set objShell = CreateObject("WScript.Shell") 27 Set objExec = objShell.Exec("taskkill.exe /F /IM iexplore.exe") 28 29End Sub

試したこと

WriteTableData関数と呼び出しコードからRangeの引数を省いて、
cell.Offset(i, j).ValueをAccess用に改変すれば実現可能かと思い、

ExcelVBA

1 Dim RsData As Recordset 2 Dim mydb As Database 3 Set mydb = CurrentDb 4 Set RsData = mydb.OpenRecordset("Accessテーブル") 5--------省略-------- 6 For Each td In tr.getElementsByTagName("td") 7 RsData.Fields(i) = td.innerText 8 j = j + 1 9 Next td

という具合にしてみたのですが、
フィールドを指定していないせいで、思うように追加してくれませんでした。
そもそもinnerTextで、フィールド別に分けることなどできるのでしょうか?

同じ事例がウェブ上から探せず困り果てております。
参考となるページがあればご紹介いただけると幸いです。

どうか宜しくお願いいたします。

補足情報(FW/ツールのバージョンなど)

Windows10 x64
Office2013 x86

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

Excelのセルは何でも代入できますが、Accessの場合はフィールドにはデータ型があり、データ型が異なるデータは基本的には代入できません。
Accessのテーブルのフィールド数・フィールドのデータ型は、HTMLのテーブルのデータと合うように設計されているか確認してください。

次にVBAでAccessのテーブルのデータ操作をするには、DAOを使用する場合と、ADOを使用する場合があります。それをどちらにするか決めてください。AccessのVBAならば、DAOの方が扱いやすいと思います。
(追記: 質問のコードではDAOを使っているようですのでそれでOKですね。)

次に、DAO(またはADO)でデータを追加するコードを調べてください。WEB検索すればサンプルコードはいくらでも見つかります。例えば、下記ではDAOとADOのコード例があります。

DAO・ADOでのデータの追加~マイクロソフトアクセス(Access)活用法(メルマガ)

上記のヒントを参考に、ExcelのコードをAccess用に書き換えてください。不明な点がでたら、どこまでできて、どこが不明なのか質問に追記してください。


Web上の目的のTableデータはすべてString型なので、
Access側ではIDのオートナンバーを省いて
Stringフィールドのみにしております。

これの仕様でのコード例
IDフィールドは無しとする

vb

1'Web上のテーブルデータをAccessテーブルに書き出す関数 2Public Sub WriteTableData(ByVal table As HTMLTable, ByVal tblName As String) 3 Dim mydb As Database 4 Set mydb = CurrentDb 5 Dim RsData As Recordset 6 Set RsData = mydb.OpenRecordset(tblName) 7 8 Dim tr As HTMLTableRow 9 For Each tr In table.getElementsByTagName("tr") 10 If tr.getElementsByTagName("td").length > 0 Then 11 Dim i As Long: i = 0 'テーブルの1列目から代入 12 Dim td As HTMLTableCell 13 RsData.AddNew 14 On Error Resume Next 15 For Each td In tr.getElementsByTagName("td") 16 RsData.Fields(i) = td.innerText 17 i = i + 1 18 Next td 19 If Err.Number <> 0 Then 20 'エラー内容をイミディエイトウィンドウへ出力 21 Debug.Print Err.Number & ": " & Err.Description 22 Err.Clear 23 End If 24 On Error GoTo 0 'エラー処理を解除 25 RsData.Update 26 End If 27 Next tr 28 29 RsData.Close 30End Sub

直書きなのでミスがあるかも。ロジックを参考にしてください。

投稿2019/10/21 01:14

編集2019/10/21 04:41
hatena19

総合スコア34073

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

GoogleWindows

2019/10/21 01:30

hatena19様 早急なご回答、感謝いたします。 Web上の目的のTableデータはすべてString型なので、 Access側ではIDのオートナンバーを省いて Stringフィールドのみにしております。 私が不明なのは、web上のテーブルデータをフィールド別に小分けにする方法です(単一データの取得や書き込みは普段DAOではなくSQL文のinsertを使用しておりますので特に問題ありません)。 innerTextだと、一行ずつ読みますが、テーブルデータ一行分がすべて代入されており、フィールド別に分けることができません。 Webスクレイピングする段階で、どのようにフィールド別に小分けにするのか知りたいと思っております。 どうかよろしくお願いいたします。
hatena19

2019/10/21 02:06

> innerTextだと、一行ずつ読みますが、テーブルデータ一行分がすべて代入されており、フィールド別に分けることができません。 質問のコードでは、 For Each td In tr.getElementsByTagName("td") となってますので、フィールド(セル)毎にデータが取れているはずですが、1行分になりますか。 "tr"なら1行、"td"はセルになるはずです。 現状のコードを省略せずに正確に提示してもらえますか。
GoogleWindows

2019/10/21 02:33

ご回答ありがとうございます。 Public Sub WriteTableData(ByVal table As HTMLTable) Dim RsData As Recordset Dim mydb As Database Set mydb = CurrentDb Set RsData = mydb.OpenRecordset("Accessテーブル") RsData.AddNew Dim i As Long: i = 0 Dim tr As HTMLTableRow For Each tr In table.getElementsByTagName("tr") Dim j As Long: j = 0 Dim th As HTMLTableCell For Each th In tr.getElementsByTagName("th") RsData.Fields(i) = th.innerText j = j + 1 Next th Dim td As HTMLTableCell For Each td In tr.getElementsByTagName("td") RsData.Fields(i) = td.innerText j = j + 1 Next td i = i + 1 Next tr End Sub このようになっております。 それと、先ほどブレークポイントで止めて一行ずつ実行してみましたら ひと値ずつtd.innerTextに格納されておりました。 失礼いたしました。 最終的に、「テーブルに書き込めません」というエラーが出て終わっています。 また、forのループ回数ですが、 どうやらAccessで設定したフィールドの回数分ループしているようでした。まだまだwebテーブルの行数はたくさんあるはずなのに、 Accessで指定したフィールド数(列数)分しかforが回っていないようです。 RsData.Fields(i)に問題があるのでしょうか?
hatena19

2019/10/21 02:37

WEBのテーブルの1行毎に AddNew して代入後に UpDate する必要があります。 コード例を回答に追加しておきますのでしばらくお待ちください。
GoogleWindows

2019/10/21 03:53

サンプルコードまでご提供くださり、大変感謝いたします。 If tr.getElementsByTagName("td").Count > 0 Then この行でエラーが出たのと、thタグのForが見当たらないのもあって そのまま使用せず、一部を引用させていただき、とりあえず取得できるか試してみました。 そうすると、おかげさまでテーブルに見事書き出されました。 ただ、階段のように一行ずつ下にズレていたため、Forでフィールド分ループさせようとしましたが、「RsData.Fields(i)でコレクションがない」という旨のエラーが出たので取り除きました。 また、webのテーブルデータは100行ほどしかないのに600行以上、空欄も含めて取得してしまいます。 Public Sub WriteTableData(ByVal table As HTMLTable, ByVal tblName As String) Dim RsData As Recordset Dim mydb As Database Set mydb = CurrentDb Set RsData = mydb.OpenRecordset(tblName) Dim p As Long RsData.AddNew Dim tr As HTMLTableRow For Each tr In table.getElementsByTagName("tr") Dim j As Long: j = 0 Dim th As HTMLTableCell For Each th In tr.getElementsByTagName("th") j = j + 1 Next th Dim td As HTMLTableCell Dim i As Long: i = 0 For Each td In tr.getElementsByTagName("td") RsData.AddNew RsData.Fields(i) = td.innerText i = i + 1 RsData.Update Next td j = j + 1 Next tr End Sub RsData.Updateをforの外に出すと、何も取得できませんでしたのでこういう形になりましたが、やはりhatena19様がご提示くださったコードを修正していく形の方が良いようです。 そうなると、 If tr.getElementsByTagName("td").Count > 0 Then この部分で、なぜ「オブジェクトは、このプロパティまたはメゾットをサポートしていません」とエラーが出るのかよく分からないです。 tr.getElementsByTagName("td").Countの部分をあらかじめLong型などに代入しても同じエラーが出ます。 せっかくサンプルコードまで示してくださったのに、ふがいない質問者で申し訳ございません。
GoogleWindows

2019/10/21 04:03

あれこれやっているうちにできました。 下記のように修正してみました。 'Web上のテーブルデータをAccessテーブルに書き出す関数 Public Sub WriteTableData(ByVal table As HTMLTable, ByVal tblName As String) Dim mydb As Database Set mydb = CurrentDb Dim RsData As Recordset Set RsData = mydb.OpenRecordset(tblName) Dim tr As HTMLTableRow For Each tr In table.getElementsByTagName("tr") If tr.getElementsByTagName("td").Item > 0 Then Dim i As Long: i = 0 'オートナンバーフィールド無しの為0から Dim td As HTMLTableCell RsData.AddNew For Each td In tr.getElementsByTagName("td") On Error Resume Next RsData.Fields(i) = td.innerText i = i + 1 Next td RsData.Update End If Next tr RsData.Close End Sub countがエラーを吐いていたようで、Itemにへんこうしたのと、 コレクションがないエラーは、不本意ですがOn Error Resume Nextを入れてスルーするようにしました。 このコードに何か問題がなければよいのですがいかがでしょうか?
hatena19

2019/10/21 04:26

> countがエラーを吐いていたようで、 あっすみません。length でした。 On Error Resume Next はなるべく狭い範囲にとどめたほうがいいでしょう。他でエラーが出た場合のデバッグが困難になりますので。 回答のコードを修正しますので、参考にしてください。
GoogleWindows

2019/10/21 04:37

ふがいない質問ばかり投げてしまって申し訳ございませんでした。 色々と丁寧に教えてくださり、大変感謝しております。 今回のことで多くのことが学べそうです。 まだまだ先ほどいただいたコードに関してはすべてを理解しておりませんので、これからじっくり見させていただこうと思います。 本当にありがとうございました。また機会がありましたら宜しくお願いいたします。
GoogleWindows

2019/10/21 05:07

わざわざコードを修正してくださってありがとうございます。 試しに走らせたら問題なく動きました。 こちらのコードの方が洗練されていて、より参考になりそうです。 何から何まで感謝の限りです。
hatena19

2019/10/21 05:18

エラーログをイミディエイトウィンドウに出力するようにしてますので、 実行した後、イミディエイトウィンドウを確認してエラーがあるか確認してください。 エラーがある場合は、そのエラーログから原因を特定して、 可能なら対策してエラーが出ないようにするといいでしょう。
GoogleWindows

2019/10/21 05:24

SQL文のエラーチェックを行う際、Debug.Printでイミディエイトウィンドウに出力されたSQL文をクエリに起こして確認したりしておりました。 hatena19様が書かれておられるような方法は今回初めて拝見いたしましたのでとても参考になります。 重ね重ねありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問