とある職場にて
これは一部実話に基づいたほとんどフィクションです。
新人「extractメソッドってなんですか?」
おれ「新人くん、業務でスクレイピングをするために事前にScrapyの予習をお願いしてたと思うけどちゃんとやってきたかな?」
新人「はい、やってきましたよ」
おれ「じゃあ、軽く復習するからね」
新人「おかのした」
...
...
...
おれ「というように、Scrapyを動かすとアクセスしたWebページからのレスポンスとしてHtmlResponseオブジェクトを取得できるので、それに対してCSSパスなどを使って欲しい値のある場所を指定してあげるんだよ。」
response.css("p.sample::text")
こうして取得できるのがSelectorListオブジェクト。ほしい値を指定することができたら、ここから値に
response.css("p.sample::text").extract()
おれ「…と書けば値をすべて取得できるんだよ。」
新人「せんぱーい、extractメソッドってなんですか?」
おれ「え?ちゃんと予習してこなかったの?extractっていうのはSelectorListオブジェクトで指定されている要素の中にある値をリスト型ですべて取得するメソッドだよ」
新人「いや、ぼくはそれをgetallメソッドとして学んだのですが…」
おれ「…へ?」
新人「正しくは…」
response.css("p.sample::text").getall()
新人「…こう書くのが正解じゃないんですか?」
おれ「(あ、ふーん。extractメソッドのエイリアスメソッドかな?知ったフリしておこ)」
おれ「あ…まぁ、そうだね、そういう書き方もあるよねー」
...
...
おれ「ちなみに、SelectorListオブジェクトは状況によってfor文でSelectorオブジェクトとしてひとつひとつ処理していくことが求められる場合もある。その場合には…」
selector_list = response.css("p.sample::text")
for selector in selector_list:
print(selector.extract()) # 出力結果 "Sample"
おれ「という書き方をすることになる。SelectorListと違って、Selectorオブジェクトに対してextractメソッドを使うと、取得対象がある場合は文字列を取得することができるよ」
新人「待ってください、getallメソッドを使った場合だと、出力結果がリスト型になってますよ」
おれ「…え?」
selector_list = response.css("p.sample::text")
for selector in selector_list:
print(selector.getall()) # 出力結果 ['Sample']
新人「getallってextractのエイリアスメソッドじゃなかったんですか?てか、そもそも先輩ほんとにScrapyわかってます?」
おれ「」
おれ「getallメソッドってなんやねん」
おれ「getallメソッドってなんやねん、ちょっと調べてみるか」
…
…
…
おれ「なるほど、Scrapy 1.6.0のバージョン以降から公式から推奨され始めたみたいやな。
んで、extractにはSelectorListから取得できる値のうち最初の値のみを取得するextract_firstメソッドがあるように、
getallメソッドにも似たような位置づけでgetメソッドがあるみたいやな」
big documentation improvements, including a switch from .extract_first() + .extract() API to .get() + .getall() API;
【DeepL翻訳】extract_first() + .extract() API を .get() + .getall() API に変更するなど、ドキュメントの大きな改善が行われました。
おれ「ちなみに、**extractメソッドはまだ公式にもサポートされていて、廃止する予定もない**みたいやな。」
extractとgetallの違いってなんやねん
おれ「んじゃ、これらのメソッドって具体的に何が違うんやろうか…」
参考:extract() and extract_first()
違い①簡潔で読みやすいコード
おれ「extract_first…よりもgetのほうが読みやすい…そらそうだわな」
We feel that these new methods result in a more concise and readable code.
【DeepL翻訳】これらの新しいメソッドは、より簡潔で読みやすいコードになると感じています。
違い②getallメソッドは必ずリスト型データを返してくれる
So, the main difference is that output of .get() and .getall() methods is more predictable: .get() always returns a single result, .getall() always returns a list of all extracted results. With .extract() method it was not always obvious if a result is a list or not; to get a single result either .extract() or .extract_first() should be called.
【DeepL翻訳】.get() は常に単一の結果を返し、.getall() は常に抽出されたすべての結果のリストを返します。.extract() メソッドでは、結果がリストであるかどうかは必ずしも明らかではありませんでした。
おれ「なるほど…」
おれ「たしかに、extractメソッドを使った場合は取得できた値の件数によってリスト型が返ってきたり、文字列型が返ってきたりしてたよな。」
※対象となるオブジェクトがSelectorListの場合はリスト型、Selectorの場合は文字列が返る?
おれ「getallメソッドの場合は、いかなる場合においてもリスト型で返してくれるから関数の結果が予測しやすくコードが書きやすいのか」
# 公式より引用
response.css('a::attr(href)')[0].getall()
['image1.html']
おれ「これならget、getall使ったほうがいいかもしれんなー」
なぜget、getallを知らなかったのか
新人「先輩、なんでgetやgetallを知らなかったんですか?」
おれ「言い訳すると、Scrapyの勉強をQiitaやネットの記事を中心に勉強していたからかな。
2019年以降でもextractなどで書かれたサンプルコードを載せた記事がたくさん出ていたからね。」
新人「ふーん、僕はPythonクローリング&スクレイピングで学べたのでラッキーでしたね。」
おれ「あとは、公式ページの更新情報をちゃんとキャッチできていなかったからかな。まさか基本的な機能で変更が行われるとも思っていなかったから。。。」
新人「なるほどっすね、今後はちゃんと公式ドキュメントやGitHubを追いかけるようにしないとダメっすよ、先輩」
おれ「」
おわりに
公式ドキュメントやGitHubはちゃんと読みましょう
Qiitaなどの技術記事、ブログはあくまでも導入部分でのみ活用して、慣れてきたら公式の情報に目を通すようにしましょう。じゃないと、エンジニアとして苦労するよ🤣(わたしも苦労してますw)