はじめに
最近BeautifulSoupを使ったWebスクレイピングを学習中です。
特定の要素をCSSセレクタでselectしようとしても、空配列が返ってくることが結構多いので、
その場合の自分なりの対処法を、半分備忘録代わりに書こうと思います。
(今更感満載かもしれません)
もっと良いやり方知ってるよ!って方は優しく教えてください笑
書かないこと
- Pythonのインストール手順
- BeautifulSoup等ライブラリのインストール手順
環境
- OS: Windows11
- ブラウザ:Chrome
- BeautifulSoupのバージョン: 4.11.1
- Pythonのバージョン: 3.10.1
題材
カロリーSlimというサイトから、栄養素一覧を取ってくるというスクリプトを書いていたので、
こちらを題材にしたいと思います。
今回はあさりのみそ汁の栄養素を取ってきたいと思います。
selectを用いた要素取得
栄養素一覧を取ってくるとなると話が長くなるので、今回は食物繊維のグラム数を取得したいと思います
(題材をヨウ素取得にしましょうっていう洒落を利かせようと思いましたが、ややこしくなりそうなのでやめます)
まずはページ全体のパース
import requests
from bs4 import BeautifulSoup
url = "https://calorie.slism.jp/200293/"
res = requests.get(url)
soup = BeautifulSoup(res.content, "lxml")
取得したい要素(今回は0.68g
)にマウスカーソルを合わせ、右クリック⇒検証
Chromeの開発者ツールが開くので、<span class="etc content">0.68</span>
の箇所で、右クリック⇒Copy selector
コピーしたCSSセレクタを貼り付けて、中身を見てみます
result = soup.select('#etc > div > table > tbody > tr:nth-child(1) > td.valMeasure')
print(result)
> []
はい、空の配列が返ってきました。
デバッグしてみる
大体途中までは取得できているはずなので、結果が返ってくるまで子要素を消していきます。
result = sourp.select('#etc > div > table')
print(result)
> [<table style="width:100%;"> <tr> <td class="name">食物繊維 総量</td><td class="valMeasure"><span class="etc_content">0.68</span><span class="etc_unit">g</span></td> <td class="guide" style="text-align: right;">5.7g~</td> </tr> <tr><td colspan="3"><div class="graph"><img height="8px" id="dietary_fiber_total_amountGraph" src="//cdn.slism.jp/calorie/images/bar01.gif" width="11.93%"/></div></td></tr> <tr> <td class="name">食塩相当量</td><td class="valMeasure"><span class="etc_content">1.76</span><span class="etc_unit">g</span></td> <td class="guide" style="text-align: right;">~2.5g</td> </tr> <tr><td colspan="3"><div class="graph"><img height="8px" id="sodium_chloride_equivalentGraph" src="//cdn.slism.jp/calorie/images/bar01.gif" width="70.4%"/></div></td></tr> </table>]
table
タグまで遡ってようやく取得できました。
結果を見てみると、table
タグの次はtbody
タグではなくてtr
タグになってますね。
tbody
タグを取っ払ってみます。
result = soup.select('#etc > div > table > tr:nth-child(1) > td.valMeasure')
print(result)
> [<td class="valMeasure"><span class="etc_content">0.68</span><span class="etc_unit">g</span></td>]
無事取得できました。
あとは、0.68
という値を取得しにいきます。
span
タグのvalueになってるので、span
タグまで潜った方がよさそうですね。
result = soup.select('#etc > div > table > tr:nth-child(1) > td.valMeasure > span.etc_content')
print(result[0].contents[0])
> 0.68
欲しい要素を取得することができました。
おわりに
BeautifulSoupの細かい仕様を理解していないので、なんでtbodyタグが取得できなかったのかは分かりませんが、
ChromeのCSSセレクタとBeautifulSoupのパース結果が異なるから起きる事象なのかと思われます。
BeautifulSoupのパース結果を実際に見てみるってのが大事ですね。
Seleniumでもスクレイピングしたりしてるんで、Seleniumバージョンのデバッグ手順も今後書きたいと思います。