アプリ版:「スタンプのみでお礼する」機能のリリースについて

https://www.oddsportal.com/football/albania/supe …
と云うサイトの表(サッカーの試合のオッズを表示)のデータをキチンと取り込めません。

 以前は
Driver.FindElementByXPath("//*[@id=""tournamentTable""]/tbody").AsTable().ToExcel (Worksheets("table").Cells(1, 1))
でExcelのシートに正常に取り込めていたのですが、最近、HTMLの記述が変わった為かこの方法が使えません(そもそも、"tournamentTable"というIDが見当たらなくなりました)。

 また、別の手段として
Driver.SendKeys ks.Control, "A" '全選択
Driver.SendKeys ks.Control, "C" 'コピー
ActiveSheet.PasteSpecial Format:="HTML", link:=False, DisplayAsIcon:=False, NoHTMLFormatting:=True
でも動作していたのですが、現在、このコードでは表のデータの下半分の大部分が取り込めません(どういう訳か、最下部の数行は正常に取得)。
 この場合、マウス操作で表だけを範囲選択すればデータを正常に取得できます。
 しかし、これでは自動化にならないので、上記サイトに対しての何か適切なスクレイピング手段があれば教えて下さい。
 宜しくお願いします。

A 回答 (4件)

No3です。



>戴いた2つのプロシージャを一つのプロシージャとして~~
二番目の方は再帰利用する仕組みにしているので、簡単にはまとめられないと思いますけれど??
・・どうやら回答の意味も内容も伝わっていないように感じます。

>この中に macro_Scrapeの名前がありません
引数をとるプロシージャの形式のままなら、そのマクロから処理を始められるはずはありません。
「疑似的に」と書きましたように、No3のコードはHTMLDocumentが取得できているものとして、解析部分だけを普通のVBAで記述したものです。
通常のVBAからでもHTMLソースのDocumentは取得可能ですが、ご質問のサイトではデータ部分は後から作成している関係で、ソースからだけでは情報が得られません。
ブラウザ経由にしないと取得したい情報は得られない状態になっています。
(No3では表示後のDOMをHTML化したものを作成し、それを疑似的に読み込んでパースしたものを解析しています)


記載がないので推測になりますが、多分、質問者様はSeleniumを利用して取得なさっているものと思いますので、そのDocumentに対して「同様」の処理を行えばできるだろうという意味での例示がNo3です。
とは言え、Seleniumと通常のVBAではオブジェクトが異なるので、そのままをコピペしても動作しないはずです。
(No3に「このまま使うことはないでしょう」と記したのはそういう意味です)

そのままでは動きませんので、Seleniumのオブジェクトに合わせて変換する必要があります。
とは言え、Basic言語が元なので変換は比較的簡単であろうと推測した次第です。
(No2で示したjavascriptでの処理の方が配列処理やループを記述しやすいので、遥かに簡単な記述ですみますが(=配列化までなら 十行程)、それを提示したところでそちらでVBA化するのは大変だろうと考え、親和性の高い通常のVBAでの回答にしたものがNo3です。)
当方には環境もなく記述法も知りませんので、書き換えまではできないため「これ以上は無理」と書きました。

一方で、DOM操作に関してはSeleniumの方が便利そうで、FindElementByCssのようなメソッドもあるようなので、二重ループも一重にすることができると思います。
また、VBAには配列を扱うメソッドがほとんど無いのに対して、(内容は知りませんが)ToArrayやSort、ToExcelといったメソッドもあるようですので、全体的にコードを簡略化できるのではないかと想像します。
    • good
    • 0
この回答へのお礼

詳細なコメント、有り難うございました。

・・どうやら回答の意味も内容も伝わっていないように感じます。
→ 情け無いのですが御指摘のとおりです。

 ところが先程ネット検索をしていた所、「Selenium サポート終了」の記事を発見。
 従って、これからは別の手段(Pythonとか HTTPなど)を使う必要がありそうなので、これらに挑戦してみたいと思っています。

 fujillinさんにはお忙しい中、今回も多くの丁重なアドバイスを頂戴し心より感謝しております。
 宜しければ、これに懲りず今後も宜しくお願いします。

お礼日時:2023/05/16 21:08

No2です。



No2のデータ取得はjavascriptで行ったものですが、VBAで同じことを疑似的にやってみました。
VBAの HTMLDocument はquerySelectorが使えないなどいろいろと面倒なのですが、(Seleniumは存じませんけれど)Seleniumにはもっと効率の良い方法があるのではと想像します。

HTNLDocumentが取得できているものとして、以下のOUTPUTプロシージャで、シートに添付図のような結果が得られます。
順にテキストを取得してゆく方式なので、タイトル行の「1 X 2 B's」の部分が左に寄っていますけれど、手抜きで全行同じ処理にしてしまったためです。
このまま使うことはないでしょうし、容易に調整できると思いますので、そのままにしてあります。

当方、Selenium環境がありませんので、残念ながらこれ以上は無理です。


以下、ご参考にでもなれば。
※ 引数Docは HTMLDocment です。
※ Selenium Basic ではメソッド等も変わるはずですが、似た様な感じではと想像します。

Sub OutPut(ByRef Doc)
Dim elms, elm, el
Dim rg As Range

Cells.ClearContents
Cells.Interior.Color = xlNone
Set rg = Cells(1, 1)
Set elms = Doc.body.getElementsByClassName("eventRow")

For Each elm In elms
For Each el In elm.Children
If el.Children.Length > 1 Then _
rg.Resize(, 10).Interior.Color = RGB(230, 230, 230)
Call getText(el, rg)
Set rg = Cells(rg.Row + 1, 1)
Next el
Next elm
End Sub


Sub getText(ByRef el, ByRef r As Range)
Dim s As String, e
If el.Children.Length Then
For Each e In el.Children
Call getText(e, r)
Next e
Else
s = Trim(Replace(Replace(el.textContent, Chr(13), ""), Chr(10), ""))
If Len(s) And InStr(el.className, "mr-3") = 0 Then
r.Value = s
Set r = r.Offset(, 1)
End If
End If
End Sub
「VBA : スクレイピングできない」の回答画像3
    • good
    • 0
この回答へのお礼

此処まで作業して頂くとは思いも寄りませんでした。
 本当に感謝の言葉もありません。

 ところで、戴いた2つのプロシージャを一つのプロシージャ(マクロ名 : macro_Scrape)としてVBAの実行ボタンを押してもマクロがスタートせずにマクロ名の一覧が出てきてしまいます。
 この中に macro_Scrapeの名前がありません(当然なのかもしれませんが)。
 そこで OutPutと getTextのプロシージャを切り分けて夫々を実行してみたのですが、いずれも上記と全く同じ反応でした。
 そこでもう少し試行錯誤してみたいので暫く時間を頂ければと思います。
 宜しくお願いします。

P.S.
 結果として添付図のようであれば完璧です。
 タイトル行が左に寄っているのは小生でも修正できるので御安心下さい。(笑)

お礼日時:2023/05/15 21:49

No1です。



時間ができたので、もう少し調べてみました。

div.eventRow の孫要素が概ね1行分のデータを保持しています。
(セレクタで言うなら、「div.eventRow > div > div」)
仮に、これを単位として、その子要素が持つテキストを一律で列挙させてみると、以下のようになります。

以下は最初の約10行分ですが、全試合分取得可能です。
(実際のテキストはもっと深い階層にありますが、直接の子要素でまとめて取得したものです)

['07 May 2023 ']
['1', 'X', '2', "B's"]
['22:00', 'Laci11–1Erzeni1', '', '2.10', '3.28', '3.22', '3']
['01:00', 'Partizani33–1Kukesi1', '', '1.64', '3.52', '5.22', '7']
['01:00', 'Teuta22–1KF Tirana1', '', '3.40', '3.31', '2.06', '12']
['06 May 2023 ']
['1', 'X', '2', "B's"]
['21:00', 'Egnatia22–0Bylis0', '', '1.85', '3.31', '4.19', '11']
['01 May 2023 ']
['1', 'X', '2', "B's"]
['01:00', 'Vllaznia00–4Egnatia4', '', '2.16', '3.22', '3.25', '12']
     ・・・・・・・
     ・・・・・・・

子要素数が1のものと4のものはセットになっていて、グレーバックで表示されているタイトル行になっています。
子要素数7の行が個々の試合結果のデータになっています。
チーム名と得点の部分は、上記ではまとまっていてわかりにくいですが、2番目の子要素だけ更にその子要素を調べる様にすれば、チーム名、得点等も分割して取得することが可能です。

1行コードではすみませんけれど、上記の規則性を利用すれば、単純なループ処理と分岐でそれぞれの値を取得することが可能と思います。
    • good
    • 0
この回答へのお礼

fujillinさんからの回答に気づくのが遅れてしまい申し訳ありませんでした。
 ご多用中、わざわざ解析して具体的なデータを提示頂き恐縮です。
 小生としてはこれを多少なりとも理解するのに大分時間がかかりそうなので、正式や返信は暫くお待ち頂ければ幸いです。

お礼日時:2023/05/14 21:11

こんばんは



ざっと見ただけですが・・・

>表のデータをキチンと取り込めません。
>そもそも、"tournamentTable"というIDが見当たらなくなりました
無いものを探しても取得できるはずはありませんよね。
多分、構成が変わったのではないでしょうか?
(見た目にさほど違いはなくても、HTMLの構成が変わっているのでは?)


「表」というのは、「Football / Albania / Superliga」が一番上の行になっている表のことでしょうか?
その部分は、
 <div class="flex flex-col px-3 text-sm max-mm:px-0">
 <div data-v-238d5b4d="">
以下の子要素(div)で構成されています。
細かなレイアウトまでは調べていませんが、flexレイアウトで表に見える様にレイアウトしているようです。
直接の子要素のDIVは表示上では1~3行分のになっているようなので、1行分はさらにその子要素になっているのでしょう。

ただし、上記はご提示のサイトの現状の表示を調べてみただけなので、日付や内容が変わったりした際に、常に同じ識別子なのかどうかまではわかりません。
(例えば、「data-v-238d5b4d」のようなdata名は、日付や節等に応じて変わりそうな感じがします)


>上記サイトに対しての何か適切なスクレイピング手段があれば教えて下さい。
表の親要素さえ特定できれば、後の構成は規則性のあるものだろうと想像できますので、その構成さえ理解できればさほど難しくはないと思われます。
Seleniumは存じませんので、.AsTable() メソッドでTable ではないものをまとめて解釈してくれるのかどうかは知りませんけれど、少なくとも1行分に該当する要素がわかれば、後はループ等で順に処理すれば良いのではないでしょうか?
ざっと見たところ
 <div class="eventRow">
という要素が、基本的な行の単位として構成されているようです。
とは言え、日付等のタイトルの1行分を含んでいる場合と、含んでいない場合(=同じ日の2行目以降)とがあるようですけれど・・
    • good
    • 0
この回答へのお礼

今回も迅速なる対応を有り難うございました(HTMLの中身まで検証頂き感謝です)。
 以下で、fujillinさんのご指摘に対する小生のコメントを → 以降に書いてみました。

多分、構成が変わったのではないでしょうか?
(見た目にさほど違いはなくても、HTMLの構成が変わっているのでは?)
→ その通りだと思います。
 見た目で変わったのはチーム名にチームのロゴ画像が追加された事です。
 また、"tournamentTable" が無くなってしまった事から HTMLが変わった事は間違いありませんが、以前のHTMLのデータが無いので詳細は不明です。
 このサイトでは、以前からHTML内にオッズ等のデータは直接には記述されていませんでした。
 小生にとってはこれが問題解決を難しくしている一因です。

「表」というのは、「Football / Albania / Superliga」が一番上の行になっている表のことでしょうか?
→ その通りです。舌っ足らずで済みません。

(例えば、「data-v-238d5b4d」のようなdata名は、日付や節等に応じて変わりそうな感じがします)
→ これが変数のようなので挫折の原因でしたが、先程、他のページでも同じdata名が使われている事を確認できたので定数と考えて良さそうです。

<div class="eventRow">
という要素が、基本的な行の単位として構成されているようです。
とは言え、日付等のタイトルの1行分を含んでいる場合と、含んでいない場合(=同じ日の2行目以降)とがあるようですけれど・・
→ そのようですね。

 fujillinさんには貴重な時間を割いて教えて頂いて恐縮なのですが、これ以上は手に余りそうなので HTMLでの処理は断念して コピペ方式で模索してみようと思います。
 この度も丁重なご指導、誠に有り難うございました。

お礼日時:2023/05/13 10:19

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!