講義43:柔軟で使いやすいSpiderの使用法

前のレッスンでは、例を通してScrapyの基本的な使用方法を学びましたが、このプロセスでは、Spiderを使用してクローラーロジックを作成し、いくつかのセレクターを使用して結果を選択しました。

このレッスンでは、SpiderとSelectorの基本的な使い方をまとめます。

クモの使い方

Scrapyでは、Webサイトをクロールするためのリンク構成、クロールロジック、解析ロジックなどが実際にはSpiderで構成されています。前のレッスンの例では、クロールロジックもSpiderで実行されることがわかりました。このレッスンでは、Spiderの基本的な使い方を具体的に理解します。

スパイダー実行プロセス

Scrapyクローラープロジェクトを実装する場合、コアクラスはSpiderクラスで、Webサイトのクロール方法のプロセスと分析方法を定義します。簡単に言うと、Spiderは次の2つのことを行う必要があります。

  • ウェブサイトをクロールするアクションを定義します。
  • クロールされたWebページを分析します。

Spiderクラスの場合、クロールサイクル全体は次のようになります。

  • 初期URLでリクエストを初期化し、コールバック関数を設定します。リクエストが正常にリクエストして戻ると、レスポンスが生成され、パラメータとしてコールバック関数に渡されます。
  • コールバック関数で返されたWebページのコンテンツを分析します。返される結果には2つの形式があります。1つは、解析の有効な結果がディクショナリまたはItemオブジェクトに返されることです。次のステップは(または直接)保存して処理でき、もう1つは解析される次の(次のページ)リンクです。このリンクを使用してリクエストを作成し、新しいコールバック関数を設定してリクエストに戻ることができます。
  • 返されたものがディクショナリまたはアイテムオブジェクトの場合、フィードエクスポートなどを介してファイルに保存できます。パイプラインが設定されている場合は、処理(フィルタリング、修正など)してパイプラインを介して保存できます。
  • 応答がReqeustの場合、応答は正常に実行された後、要求で定義されたコールバック関数に要求が渡され、セレクターを再度使用して、新しく取得したWebコンテンツを分析し、分析されたデータに基づいてアイテムを生成できます。

上記の手順を繰り返すことにより、サイトのクロールが完了します。

スパイダークラス分析

前のレッスンの例では、私たちが定義したSpiderは、scrapy.spiders.Spiderから継承されています。このクラスは、最も単純で最も基本的なSpiderクラスです。他のすべてのSpiderは、このクラスと、後で説明する特別なクラスから継承する必要があります。 Spiderクラスもそれを継承しています。

このクラスは、start_requestsメソッドのデフォルト実装を提供し、start_urls属性を読み取って要求し、parseメソッドを呼び出して、返された結果に従って結果を解析します。さらに、以下に説明するいくつかの基本的なプロパティがあります。

  • name:クモの名前。クモの名前を定義する文字列。Spiderの名前は、ScrapyがSpiderを見つけて初期化する方法を定義するため、一意である必要があります。ただし、制限なく複数の同一のSpiderインスタンスを生成できます。名前はSpiderの最も重要な属性であり、必須です。スパイダーが単一のWebサイトをクロールする場合、一般的な方法は、Webサイトのドメイン名でスパイダーに名前を付けることです。たとえば、スパイダーがmywebsite.comをクロールする場合、スパイダーは通常、mywebsiteという名前になります。

  • allowed_domains:クロールを許可するドメイン名はオプションです。この範囲にないリンクはクロールされません。

  • start_urls:開始URLリスト。start_requestsメソッドを実装しない場合、デフォルトでこのリストからクロールを開始します。

  • custom_settings:これは、このSpiderの構成専用のディクショナリです。この設定はプロジェクトのグローバル設定をオーバーライドします。この設定は初期化前に更新する必要があるため、クラス変数として定義する必要があります。

  • crawler:この属性は、このSpiderクラスに対応するCrawlerオブジェクトを表すfrom_crawlerメソッドによって設定されます。Crawlerオブジェクトには多くのプロジェクトコンポーネントが含まれています。これを使用して、プロジェクトの構成情報を取得できます。たとえば、最も一般的なものは、プロジェクトを取得することです設定情報、つまり設定。

  • settings:プロジェクトのグローバル設定変数を直接取得できる設定オブジェクトです。

いくつかの基本的なプロパティに加えて、Spiderには一般的に使用されるいくつかのメソッドがあり、ここで紹介します。

  • start_requests:このメソッドは、初期リクエストを生成するために使用されます。反復可能なオブジェクトを返す必要があります。このメソッドは、start_urlsのURLを使用して、デフォルトでリクエストを作成します。リクエストはGETリクエストメソッドです。起動時にPOSTで特定のサイトにアクセスする場合は、このメソッドを直接オーバーライドして、POSTリクエストを送信するときにFormRequestを使用できます。

  • parse:Responseでコールバック関数が指定されていない場合、このメソッドはデフォルトで呼び出されます。このメソッドは、Responseの処理、返された結果の処理、必要なデータと次のリクエストの抽出、および返却を担当します。このメソッドは、RequestまたはItemを含む反復可能なオブジェクトを返す必要があります。

  • closed:Spiderが閉じられると、このメソッドが呼び出され、リソースを解放するためのいくつかの操作または他の閉じる操作が一般的に定義されます。

セレクターの使用法

以前、Beautiful Soup、PyQuery、および正規表現を使用してWebページデータを抽出する方法を紹介しましたが、これは非常に便利です。また、Scrapyは独自のデータ抽出メソッド、つまりセレクター(セレクター)も提供しています。

セレクターはlxmlに基づいて構築され、XPathセレクター、CSSセレクター、および正規表現をサポートし、包括的な機能、高解像度の速度と精度を備えています。

次にセレクタの使い方を紹介します。

直接使用

セレクターは独立して使用できるモジュールです。Selectorクラスを直接使用してセレクターオブジェクトを作成し、xpath、cssなどの関連メソッドを呼び出してデータを抽出できます。

たとえば、HTMLコードの場合、次の方法でSelectorオブジェクトを作成してデータを抽出できます。

from scrapy import Selector
​
body = '<html><head><title>Hello World</title></head><body></body></html>'
selector = Selector(text=body)
title = selector.xpath('//title/text()').extract_first()
print(title)

演算結果:

Hello World

ここでは、Scrapyフレームワークでは実行しませんでしたが、Scrapyで個別にセレクターを使用しました。構築時にテキストパラメーターを渡すと、セレクターセレクターオブジェクトが生成され、先ほど使用したScrapyのようなものになります。解析方法は同じで、xpath、css、その他のメソッドを呼び出して抽出します。

ここでは、ソースコードのタイトルのテキストを探しています。XPathセレクターの最後にtextメソッドを追加して、テキストを抽出します。

上記の内容はSelectorを直接使用したものです。Beautiful Soupなどのライブラリと同様に、Selectorは実際には強力なWeb解析ライブラリです。便利な場合は、セレクタを直接使用して他のプロジェクトのデータを抽出することもできます。

次に、例を使用してセレクターの使用法を詳細に説明します。

スクレイピーシェル

たとえば、セレクターは主にScrapyと組み合わせて使用​​されるため、Scrapyのコールバック関数のパラメーター応答はxpath()またはcss()メソッドを直接呼び出してデータを抽出するため、ここではScrapyシェルを使用してScrapyリクエストのプロセスをシミュレートし、関連する抽出方法。

公式ドキュメントのサンプルページを使用してデモを行います:http : //doc.scrapy.org/en/latest/_static/selectors-sample1.html

Scrapyシェルを開き、コマンドラインに次のコマンドを入力します。

scrapy shell http://doc.scrapy.org/en/latest/_static/selectors-sample1.html

これで、Scrapy Shellモードに入りました。このプロセスは、実際にはScrapyがリクエストを開始することです。リクエストされたURLは、コマンドラインで今入力されたURLであり、次に、図に示すように、リクエスト、レスポンスなどの操作可能な変数が渡されます。

ここに画像の説明を挿入
コマンドラインモードでコマンドを入力してオブジェクトのいくつかの操作メソッドを呼び出し、Enterキーを押して結果をリアルタイムで表示できます。これは、Pythonのコマンドラインインタラクティブモードに似ています。

次に、デモの例ではすべて、ページのソースコードを分析対象として使用しています。ページのソースコードは次のとおりです。

<html>
 <head>
  <base href='http://example.com/' />
  <title>Example website</title>
 </head>
 <body>
  <div id='images'>
   <a href='image1.html'>Name: My image 1 <br /><img src='image1_thumb.jpg' /></a>
   <a href='image2.html'>Name: My image 2 <br /><img src='image2_thumb.jpg' /></a>
   <a href='image3.html'>Name: My image 3 <br /><img src='image3_thumb.jpg' /></a>
   <a href='image4.html'>Name: My image 4 <br /><img src='image4_thumb.jpg' /></a>
   <a href='image5.html'>Name: My image 5 <br /><img src='image5_thumb.jpg' /></a>
  </div>
 </body>
</html>

XPathセレクター

Scrapyシェルに入ったら、主に分析のために応答変数を操作します。HTMLコードを解析しているため、Selectorは自動的にHTML構文を使用して分析します。

Responseには属性セレクタがあります。response.selectorを呼び出すことによって返されるコンテンツは、応答のテキストを使用してSelectorオブジェクトを構築することと同じです。このSelectorオブジェクトを通じて、xpath、cssなどの解析メソッドを呼び出し、XPathまたはCSSセレクターパラメーターをメソッドに渡して情報を抽出できます。

以下に示すように、例を使って体験してみましょう。

>>> result = response.selector.xpath('//a')
>>> result
[<Selector xpath='//a' data='<a href="image1.html">Name: My image 1 <'>,
 <Selector xpath='//a' data='<a href="image2.html">Name: My image 2 <'>,
 <Selector xpath='//a' data='<a href="image3.html">Name: My image 3 <'>,
 <Selector xpath='//a' data='<a href="image4.html">Name: My image 4 <'>,
 <Selector xpath='//a' data='<a href="image5.html">Name: My image 5 <'>]
>>> type(result)
scrapy.selector.unified.SelectorList

印刷された結果の形式は、セレクターで構成されるリストです。実際には、タイプはセレクターリストです。セレクターリストとセレクターの両方は、xpathやcssなどのメソッドを引き続き呼び出して、さらにデータを抽出できます。

上記の例では、ノードを抽出しました。次に、以下に示すように、xpathメソッドの呼び出しを続けて、aノードに含まれるimgノードを抽出します。

>>> result.xpath('./img')
[<Selector xpath='./img' data='<img src="image1_thumb.jpg">'>,
 <Selector xpath='./img' data='<img src="image2_thumb.jpg">'>,
 <Selector xpath='./img' data='<img src="image3_thumb.jpg">'>,
 <Selector xpath='./img' data='<img src="image4_thumb.jpg">'>,
 <Selector xpath='./img' data='<img src="image5_thumb.jpg">'>]

ノードaのすべてのimgノードを取得し、結果は5です。

セレクターの前にドット(ドット)が追加されることは注目に値します。つまり、要素内のデータを抽出します。ドットが追加されない場合は、ルートノードから抽出することを意味します。ここでは、ノードからの抽出を意味する./imgの抽出方法を使用しました。ここで// imgを使用する場合でも、htmlノードから抽出します。

response.selector.xpathメソッドを使用してデータを抽出しました。Scrapyには、response.xpathとresponse.cssの2つの実用的なショートカットがあります。これらの機能は、response.selector.xpathとresponse.selector.cssと完全に同等です。便宜上、xpathおよびcssの応答メソッドを直接呼び出して選択を行います。

ここで得られるのは、SelectorオブジェクトのリストであるSelectorListタイプの変数です。以下に示すように、インデックスを使用してSelector要素の1つを個別に取得できます。

>>> result[0]
<Selector xpath='//a' data='<a href="image1.html">Name: My image 1 <'>

このSelectorListをリストのように操作できます。ただし、取得されるコンテンツは、実際のテキストコンテンツではなく、SelectorまたはSelectorListタイプです。それでは、特定のコンテンツを抽出する方法は?
たとえば、ノード要素を抽出する場合は、以下に示すように、extractメソッドを使用できます。

>>> result.extract()
['<a href="image1.html">Name: My image 1 <br><img src="image1_thumb.jpg"></a>', '<a href="image2.html">Name: My image 2 <br><img src="image2_thumb.jpg"></a>', '<a href="image3.html">Name: My image 3 <br><img src="image3_thumb.jpg"></a>', '<a href="image4.html">Name: My image 4 <br><img src="image4_thumb.jpg"></a>', '<a href="image5.html">Name: My image 5 <br><img src="image5_thumb.jpg"></a>']

ここで抽出メソッドを使用すると、本当に必要なものを取得できます。

以下に示すように、XPath式を書き換えてノードの内部テキストと属性を選択することもできます。

>>> response.xpath('//a/text()').extract()
['Name: My image 1 ', 'Name: My image 2 ', 'Name: My image 3 ', 'Name: My image 4 ', 'Name: My image 5 ']
>>> response.xpath('//a/@href').extract()
['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']

ノードの内部テキストを取得するには/ text()の別のレイヤーを追加するか、ノードのhref属性を取得するには/ @ hrefのレイヤーを追加するだけです。このうち、@記号の後の内容は、取得する属性の名前です。

これで、ルールを使用して要件を満たすすべてのノードを取得できます。返されるタイプはリストタイプです。

しかし、問題があります。要件を満たすノードが1つしかない場合、返される結果はどうなりますか?以下に示すように、別の例を使用して感じてみましょう。

>>> response.xpath('//a[@href="image1.html"]/text()').extract()
['Name: My image 1 ']

XPathが1つの要素のみと一致できるように、属性を使用して一致の範囲を制限します。次に、extractメソッドを使用して結果を抽出します。結果は依然としてリストの形式であり、そのテキストはリストの最初の要素です。しかし、多くの場合、実際に必要なデータは最初の要素のコンテンツです。ここでは、以下に示すように、インデックスを追加することで取得します。

'Name: My image 1 '

ただし、この執筆は明らかに危険です。XPathに問題があると、抽出の結果が空のリストになる場合があります。取得にインデックスを使用すると、配列が範囲外になるのではないですか?
したがって、単一の要素を具体的に抽出できる別のメソッドは、extract_firstと呼ばれます。上記の例を次のように書き換えます。

>>> response.xpath('//a[@href="image1.html"]/text()').extract_first()
'Name: My image 1 '

このようにして、extract_firstメソッドを直接使用して、一致の最初の結果を抽出します。境界外の配列の問題を心配する必要はありません。

さらに、extract_firstメソッドのデフォルト値パラメーターを設定して、XPathルールがコンテンツを抽出できない場合に、デフォルト値が直接使用されるようにすることもできます。たとえば、XPathを存在しないルールに変更し、以下に示すようにコードを再実行します。

>>> response.xpath('//a[@href="image1"]/text()').extract_first()>>> response.xpath('//a[@href="image1"]/text()').extract_first('Default Image')
'Default Image'

ここで、XPathがどの要素とも一致しない場合、extract_firstを呼び出すとnullが返され、エラーは報告されません。コードの2行目では、Default Imageなどのパラメーターをデフォルト値として渡します。このように、XPathが結果と一致しない場合、戻り値は代わりにこのパラメーターを使用し、出力がまったく同じであることがわかります。

これまでに、ネストされたクエリ、コンテンツの抽出、単一のコンテンツの抽出、テキストと属性の取得など、ScrapyでのXPathの関連する使用法を理解しました。

CSSセレクター

次に、CSSセレクターの使用方法を見てみましょう。ScrapyのセレクターもCSSセレクターとドッキングします。response.css()メソッドを使用して、CSSセレクターを使用して対応する要素を選択します。

たとえば、上記ではすべてのノードを選択しましたが、次に示すように、CSSセレクターもそれを実行できます。

>>> response.css('a')
[<Selector xpath='descendant-or-self::a' data='<a href="image1.html">Name: My image 1 <'>, 
<Selector xpath='descendant-or-self::a' data='<a href="image2.html">Name: My image 2 <'>, 
<Selector xpath='descendant-or-self::a' data='<a href="image3.html">Name: My image 3 <'>, 
<Selector xpath='descendant-or-self::a' data='<a href="image4.html">Name: My image 4 <'>, 
<Selector xpath='descendant-or-self::a' data='<a href="image5.html">Name: My image 5 <'>]

同様に、ノードは、以下に示すように、extractメソッドを呼び出すことによって抽出できます。

['<a href="image1.html">Name: My image 1 <br><img src="image1_thumb.jpg"></a>', '<a href="image2.html">Name: My image 2 <br><img src="image2_thumb.jpg"></a>', '<a href="image3.html">Name: My image 3 <br><img src="image3_thumb.jpg"></a>', '<a href="image4.html">Name: My image 4 <br><img src="image4_thumb.jpg"></a>', '<a href="image5.html">Name: My image 5 <br><img src="image5_thumb.jpg"></a>']

使用法とXPathの選択はまったく同じです。さらに、以下に示すように、属性選択とネストされた選択を実行することもできます。

>>> response.css('a[href="image1.html"]').extract()
['<a href="image1.html">Name: My image 1 <br><img src="image1_thumb.jpg"></a>']
>>> response.css('a[href="image1.html"] img').extract()
['<img src="image1_thumb.jpg">']

ここでは、[href =” image.html”]を使用してhref属性を制限しています。一致する結果が1つだけあることがわかります。さらに、aノードでimgノードを検索する場合は、スペースとimgを追加するだけで済みます。セレクターは、標準のCSSセレクターとまったく同じ方法で記述されます。

以下に示すように、extract_first()メソッドを使用してリストの最初の要素を抽出することもできます。

>>> response.css('a[href="image1.html"] img').extract_first()
'<img src="image1_thumb.jpg">'

次の2つの使用法は同じではありません。ノードの内部テキストと属性は、以下に示すように、この方法で取得されます。

>>> response.css('a[href="image1.html"]::text').extract_first()
'Name: My image 1 '
>>> response.css('a[href="image1.html"] img::attr(src)').extract_first()
'image1_thumb.jpg'

テキストと属性を取得するには、:: textと:: attr()を使用する必要があります。Beautiful SoupやPyQueryなどの他のライブラリには、個別のメソッドがあります。

さらに、CSSセレクターは、XPathセレクターのように選択範囲をネストできます。最初にXPathセレクターを使用してすべてのaノードを選択し、次にCSSセレクターを使用してimgノードを選択してから、XPathセレクターを使用して属性を取得します。以下に示すように、例を使って体験してみましょう。

>>> response.xpath('//a').css('img').xpath('@src').extract()
['image1_thumb.jpg', 'image2_thumb.jpg', 'image3_thumb.jpg', 'image4_thumb.jpg', 'image5_thumb.jpg']

すべてのimgノードのsrc属性を正常に取得しました。
したがって、xpathメソッドとcssメソッドの両方を自由に使用して、ネストされたクエリを実現でき、2つは完全に互換性があります。

レギュラーマッチ

Scrapyのセレクターは、通常のマッチングもサポートしています。たとえば、例のaノードのテキストはName:My image 1.に似ています。ここでは、Name:の後のコンテンツのみを抽出します。現時点では、reメソッドを使用して次のことを実現できます。

>>> response.xpath('//a/text()').re('Name:\s(.*)')
['My image 1 ', 'My image 2 ', 'My image 3 ', 'My image 4 ', 'My image 5 ']

正規表現をreメソッドに渡します。ここで(。*)は照合するコンテンツであり、出力結果は正規表現によって照合されたグループであり、結果は順番に出力されます。

同時に2つのグループがある場合でも、以下に示すように、結果は順番に出力されます。

>>> response.xpath('//a/text()').re('(.*?):\s(.*)')
['Name', 'My image 1 ', 'Name', 'My image 2 ', 'Name', 'My image 3 ', 'Name', 'My image 4 ', 'Name', 'My image 5 ']

extract_firstメソッドと同様に、re_firstメソッドはリストの最初の要素を選択できます。

>>> response.xpath('//a/text()').re_first('(.*?):\s(.*)')
'Name'
>>> response.xpath('//a/text()').re_first('Name:\s(.*)')
'My image 1 '

正規表現で一致するグループの数に関係なく、結果はリストの最初の要素と等しくなります。

応答オブジェクトがreおよびre_firstメソッドを直接呼び出すことはできないことに注意してください。フルテキストで定期的な照合を実行する場合は、次に示すように、最初にxpathメソッドを呼び出してから、定期的な照合を実行できます。

>>> response.re('Name:\s(.*)')
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'HtmlResponse' object has no attribute 're'
>>> response.xpath('.').re('Name:\s(.*)<br>')
['My image 1 ', 'My image 2 ', 'My image 3 ', 'My image 4 ', 'My image 5 ']
>>> response.xpath('.').re_first('Name:\s(.*)<br>')
'My image 1 '

上記の例では、reメソッドを直接呼び出すと、re属性がないことを示すプロンプトが表示されることがわかります。ただし、ここでは最初にxpath( '。')を呼び出して全文を選択し、次にreおよびre_firstメソッドを呼び出して通常の照合を実行します。

上記の内容は、2つの一般的なセレクターと通常のマッチング関数を含むScrapyセレクターの使用方法です。XPath構文、CSSセレクター構文、および正規表現構文に精通している場合は、データ抽出効率を大幅に向上させることができます。

おすすめ

転載: blog.csdn.net/weixin_38819889/article/details/107974075