動的に読み込まれたコンテンツの選択

一部のWebページは、あなたがそれらのページをWebブラウザーに読み込み後に目的のデータを表示します。 ただし、Scrapyを使用してそれらをダウンロードする場合、 selectors を使用して目的のデータに到達することはできません。

この場合、推奨されるアプローチは データ・ソースを探し、 そこからデータを抽出することです。

データ・ソースを探すのに失敗し、それでもWebブラウザから DOM を介して目的のデータにアクセスできる場合は、 JavaScriptの事前レンダリング を参照してください。

データ・ソースを探す

目的のデータを抽出するには、最初にソースの場所を見つける必要があります。

データが画像やPDFドキュメントなどの非テキストベースの形式である場合、あなたのWebブラウザの ネットワークツール を使用して、対応するリクエストを見つけ、 それを再現します。

Webブラウザーで目的のデータをテキストとして選択できる場合、データは埋め込みJavaScriptコードで定義されるか、テキストベースの形式で外部リソースからロードされます。

その場合、wgrep などのツールを使用して、そのリソースのURLを見つけることができます。

データが元のURL自体からのものであることが判明した場合、 Webページのソースコードを調べて、 データの場所を特定する必要があります。

データが別のURLからのものである場合は、 対応するリクエストを再現する必要があります。

Webページのソースコードの調査

しばしば、(DOM ではなく)Webページのソースコードを調べて、必要なデータがどこにあるかを判断する必要がある場合があります。

Scrapyが見れるWebページのコンテンツをダウンロードするためにScrapyの fetch コマンドを使用しください。:

scrapy fetch --nolog https://example.com > response.html

目的のデータが <script/> 要素内の埋め込みJavaScriptコードにある場合は、 JavaScriptコードのパース を参照してください。

目的のデータが見つからない場合は、まず、Scrapyだけで見つからないのではないことを確認します。curlwget などのHTTPクライアントでもWebページをダウンロードしてみて、取得したレスポンスで情報が見つかるかどうかを確認します。

他のHTTPクライアントで目的のデータがあるレスポンスを受け取った場合、そのHTTPクライアントと同じになるようにあなたのScrapyの Request を変更します。 たとえば、同じユーザーエージェント文字列(USER_AGENT) または同じ headers にします。

目的のデータなしのレスポンスが返される場合は、リクエストをウェブブラウザのリクエストにより近いものにするための手順を実行する必要があります。 リクエストの再現 を参照してください。

リクエストの再現

しばしば、Webブラウザが実行する方法でリクエストを再現する必要がある場合があります。

Webブラウザーの ネットワーク・ツール を使用して、Webブラウザーが目的のリクエストをどのように実行するかを確認し、Scrapyでそのリクエストを再現してください。

同じHTTPメソッドとURLで Request を生成すれば十分かもしれません。ただし、そのリクエストの本文、ヘッダー、フォームパラメーター(FormRequest 参照)を再現する必要がある場合もあります。

すべての主要なブラウザはリクエストを cURL 形式でエクスポートできるため、Scrapyは from_curl() メソッドを組み込んで、cURLコマンドから同等の Request を生成します。 詳細については、ネットワークツール節内の request from curl を参照してください。

あなたが期待するレスポンスを取得したら、あなたは 目的のデータをそこから抽出する事 ができます。

Scrapyでリクエストを再現できます。 けれども、必要なすべてのリクエストを再現することは、開発時には効率的でないように見える場合があります。 もしあなたがそのような場合に遭遇し、そして、クロール速度があなたにとって大きな関心事ではない場合は、代わりに JavaScriptの事前レンダリング を検討することもできます。

予想されるレスポンスが時々は得られるが、常にではない場合、問題はおそらくリクエストではなく、ターゲットサーバーにあります。 ターゲットサーバーはバグがあるか、過負荷であるか、または 禁止 リクエストの一部です。

cURLコマンドをScrapyリクエストに変換するには、 curl2scrapy を使用できます。

さまざまなレスポンス形式の処理

あなたが目的のデータを含むレスポンスを取得した後、そこから目的のデータを抽出する方法は、レスポンスのタイプによって異なります。

  • レスポンスがHTMLまたはXMLの場合、通常どおり セレクター を使用します。

  • 応答がJSONの場合、 json.loads() を使用して、response.text から目的のデータをロードします:

    data = json.loads(response.text)
    

    目的のデータがJSONデータに埋め込まれたHTMLまたはXMLコード内にある場合、そのHTMLまたはXMLコードを Selector にロードして、それから、いつものように セレクター を使います。

    selector = Selector(data['html'])
    
  • 応答がJavaScript、または目的のデータを含む <script/> 要素を持つHTMLの場合、 JavaScriptコードのパース を参照してください。

  • レスポンスがCSSの場合、 正規表現 を使用して response.text から目的のデータを抽出します。

  • レスポンスが画像または画像に基づく別の形式(PDFなど))の場合、レスポンスを レスポンス・ボディ からバイトとして読み取り、OCRソリューションを使用してテキストとして目的のデータを抽出します。

    たとえば、 pytesseract を使用できます。 PDFから表を読むには、 tabula-py がより良い選択かもしれません。

  • レスポンスがSVG、または目的のデータを含む埋め込みSVGを含むHTMLの場合、SVGはXMLに基づいているため、 セレクター を使用して目的のデータを抽出できます。

    そうでない場合は、SVGコードをラスターイメージに変換し、 ラスターイメージ処理 を行う必要があります。

JavaScriptコードのパース

目的のデータがJavaScriptでハードコーディングされている場合、最初にJavaScriptコードを取得する必要があります。:

  • JavaScriptコードがJavaScriptファイルにある場合は、単に response.text から読み取ります。

  • JavaScriptコードがHTMLページの <script/> 要素内にある場合、 セレクター を使用して、その <script/> 要素内のテキストを抽出します。

あなたがJavaScriptコードを含む文字列を取得したら、そこから目的のデータを抽出できます。:

  • 正規表現 を使用してJSON形式で目的のデータを抽出し、 json.loads() でパースできる場合があります。

    たとえば、JavaScriptコードに var data = {"field": "value"}; のような個別の行が含まれている場合、次のようにそのデータを抽出できます:

    >>> pattern = r'\bvar\s+data\s*=\s*(\{.*?\})\s*;\s*\n'
    >>> json_data = response.css('script::text').re_first(pattern)
    >>> json.loads(json_data)
    {'field': 'value'}
    
  • chompjs は、JavaScriptオブジェクトをパースして dict に格納するAPIを提供します。

    たとえば、JavaScriptコードに var data = {field: "value", secondField: "second value"}; が含まれている場合、以下のようにしてそのデータを抽出できます:

    >>> import chompjs
    >>> javascript = response.css('script::text').get()
    >>> data = chompjs.parse_js_object(javascript)
    >>> data
    {'field': 'value', 'secondField': 'second value'}
    
  • それ以外の場合は、js2xml を使用してJavaScriptコードをXML文書に変換し、 セレクター を使用して解析できます。

    たとえば、JavaScriptコードに var data = {field: "value"}; が含まれている場合、以下のようにしてそのデータを抽出できます:

    >>> import js2xml
    >>> import lxml.etree
    >>> from parsel import Selector
    >>> javascript = response.css('script::text').get()
    >>> xml = lxml.etree.tostring(js2xml.parse(javascript), encoding='unicode')
    >>> selector = Selector(text=xml)
    >>> selector.css('var[name="data"]').get()
    '<var name="data"><object><property name="field"><string>value</string></property></object></var>'
    

JavaScriptの事前レンダリング

追加のリクエストからデータを取得するウェブページでは、目的のデータを含むリクエストを再現することをお勧めします。 多くの場合、その努力は、最小のパース時間とネットワーク転送で構造化された完全なデータという結果で報われます。

けれども、特定のリクエストを再現するのが非常に難しい場合があります。 または、Webブラウザーで表示されるWebページのスクリーンショットなど、リクエストで提供できないものが必要な場合があります。

これらの場合、シームレスな統合のために、 Splash JavaScriptレンダリングサービス(https://github.com/scrapinghub/splash)と scrapy-splash (https://github.com/scrapinghub/splash)を使用します。

スプラッシュはHTMLとしてWebページの DOM を返すため、 セレクター でパースできます。 configuration または scripting を介して大きな柔軟性を提供します。

以前に記述されたスクリプトを使用する代わりに、PythonコードからオンザフライでDOMと対話する、または複数のWebブラウザーウィンドウを処理するなど、Splashが提供する以上のものが必要な場合は、 代わりに ヘッドレス ブラウザ を使用する必要があります。

ヘッドレスブラウザ(headless browser)の使用

ヘッドレスブラウザー(headless browser)は、自動化のためのAPIを提供する特別なWebブラウザーです。

Scrapyでヘッドレスブラウザを使用する最も簡単な方法は、シームレスな統合のために scrapy-selenium (https://github.com/clemfromspace/scrapy-selenium)とともに Selenium (https://www.seleniumhq.org/)を使用することです。( scrapy-selenium 0.0.7 README邦訳 https://gist.github.com/kuma35/37d0c6c80af7d5d0e1d01edce30027f1#file-readme-jp-md )