WEB_DATA_COLLECTOR_Extraction_JSON_入門ガイド

← 一覧へ戻る WEB_DATA_COLLECTOR_Extraction_JSON_入門ガイド

WEB DATA COLLECTOR

Extraction definition JSON 入門ガイド

対象読者: HTMLとCSSの基本を少し知っている方/Webデータ収集を初めて設定する方 対象実装: swpfsitedatacollectorprototype(2026-08-07 添付ソース) 目的: Webページの「どこから」「どの値を」「どのように」取り出すかを、Extraction definition JSONで設定できるようになること


1. はじめに — このJSONは何をしているのか

WEB DATA COLLECTORでは、WebページのHTMLをプログラムに直接書き込んで解析するのではなく、「どの場所から何を取得するか」をJSONで指定します。

たとえば、次のHTMLがあるとします。

<a class="Product__titleLink"
   data-auction-id="e1239762031"
   data-auction-title="OLYMPUS OM-D E-M5"
   href="https://auctions.yahoo.co.jp/jp/auction/e1239762031">
  OLYMPUS OM-D E-M5
</a>

この1つのタグから、次のように複数の情報を取得できます。

欲しい情報取得場所主に使うTYPE
画面に見える商品名タグの中の文字TEXT
オークションIDdata-auction-id属性ATTRIBUTE
商品詳細URLhref属性ATTRIBUTE
data属性の商品名data-auction-title属性ATTRIBUTE

つまりExtraction definition JSONは、簡単に言えば、

「HTMLのこの部分を探して、その中のこの値を取ってください」

という指示書です。


2. HTMLとCSSのおさらい

2.1 HTMLは「タグの入れ子」でできている

HTMLは、次のようなタグで構成されます。

<div class="product">
  <h2 class="title">カメラ</h2>
  <span class="price">10,500円</span>
  <a href="/item/123">詳細を見る</a>
</div>

この場合、構造は次のようになります。

div.product
├─ h2.title       → カメラ
├─ span.price     → 10,500円
└─ a              → 詳細を見る
   └─ href        → /item/123

WEB DATA COLLECTORでは、この「どのタグを選ぶか」を主にCSSセレクターで指定します。


2.2 タグ名

<h1>商品タイトル</h1>

h1を指定すると、<h1>タグを探します。

h1

2.3 class(クラス)

<div class="Product">...</div>

classは先頭に . を付けます。

.Product

タグ名も付ける場合は、

div.Product

です。

添付のYahoo!オークション検索設定では、1商品を囲む要素として次を使用しています。

"item_container": {
  "type": "CSS_ALL",
  "selector": "li.Product"
}

これは、

<li class="Product">...</li> を商品1件としてすべて探す

という意味です。


2.4 id

<div id="description">商品説明...</div>

idは先頭に # を付けます。

#description

タグ名も含める場合は、

div#description

です。

添付HTMLには次のような構造があります。

<div id="itemTitle">...</div>
<div id="description">...</div>
<script id="__NEXT_DATA__" type="application/json">...</script>

そのため、例えば次の指定ができます。

#itemTitle
#description
script#__NEXT_DATA__

2.5 属性

HTMLタグには属性があります。

<a href="/item/123" data-auction-id="abc123">商品</a>

属性が存在するタグを探す場合は [属性名] を使います。

a[href]
a[data-auction-id]

特定の値だけを探すこともできます。

a[data-auction-id="abc123"]

3. タグの指定方法 — jQueryでよく使うCSSセレクターとほぼ同じ考え方

WEB DATA COLLECTORのCSS選択は、内部ではSymfony DomCrawler + CssSelectorを使用しています。

したがって、一般的なjQueryで使うCSSセレクターと同じ書き方を中心に考えると理解しやすいです。

ただし、厳密には「jQueryそのもの」ではありません。jQuery独自の拡張セレクター(例: :contains() など)は使えるとは限らないため、標準CSSセレクターを基本にしてください。


3.1 よく使うセレクター一覧

指定意味例
divdivタグdiv
.ProductProductクラス.Product
li.ProductliタグかつProductクラスli.Product
#descriptiondescriptionというid#description
a[href]href属性を持つaタグa[href]
a[href="/item/1"]hrefが完全一致a[href="/item/1"]
a[href*="bid_hist"]hrefに文字列を含むa[href*="bid_hist"]
a[href^="https://"]hrefが指定文字列で始まるa[href^="https://"]
a[href$=".html"]hrefが指定文字列で終わるa[href$=".html"]
.product .priceproductの内側にあるprice.product .price
.product > .priceproductの直下のprice.product > .price
.a.baとbの両方のclassを持つ.a.b
h1, h2h1またはh2h1, h2

3.2 「含む」の指定は非常によく使う

添付HTMLの入札履歴リンクは次のようになっています。

<a href="https://auctions.yahoo.co.jp/jp/show/bid_hist?aID=e1239762031">7件</a>

URL全体は商品ごとに変わります。

そこで、完全一致ではなく、

a[href*="/jp/show/bid_hist"]

と指定します。

これは、

href属性の中に /jp/show/bid_hist を含むaタグ

という意味です。

実際のExtraction definition JSONでもこの方法を使用しています。

{
  "type": "ATTRIBUTE",
  "selector": "a[href*=\"/jp/show/bid_hist\"]",
  "attribute": "href",
  "transform": ["ABSOLUTE_URL"]
}

4. Extraction definition JSONの全体構造

現在のv2形式は、大きく次の構造になっています。

{
  "version": 2,
  "pages": {
    "list": {},
    "detail": {},
    "history": {}
  },
  "merge": {},
  "system_mapping": {}
}

概念図にすると次のようになります。

検索結果ページ LIST
  │
  ├─ 商品1
  │   └─ 詳細ページ DETAIL
  │        └─ 入札履歴ページ RELATED
  │
  ├─ 商品2
  │   └─ 詳細ページ DETAIL
  │        └─ 入札履歴ページ RELATED
  │
  └─ 商品3 ...

5. pageの基本設定

5.1 role

主に次の役割で使用します。

LIST
DETAIL
RELATED

LIST

検索結果一覧など、「複数の商品を並べているページ」です。

"role": "LIST"

DETAIL

1商品の詳細ページです。

"role": "DETAIL"

RELATED

詳細ページからさらにたどる関連ページです。

例:

  • 入札履歴
  • レビュー一覧
  • 在庫情報
  • 店舗詳細
"role": "RELATED"

5.2 result_type

設定できる主な値は次の2つです。

OBJECT

1ページから1組のデータを取ります。

"result_type": "OBJECT"

詳細ページ向きです。

COLLECTION

同じ構造が複数繰り返されるページから、複数件を取ります。

"result_type": "COLLECTION"

検索結果一覧や入札履歴一覧向きです。


6. item_container — まず「1件分の箱」を決める

検索結果一覧では、ページ全体から直接titleやpriceを探すのではなく、最初に1商品を囲むHTMLを指定します。

"item_container": {
  "type": "CSS_ALL",
  "selector": "li.Product"
}

CSS_ALLは、この用途で使う「CSSセレクターに一致する要素をすべて商品コンテナとして扱う」という設定名です。

その後、各商品コンテナの内側でfieldsを探します。

ページ全体
 ├─ li.Product  ← 商品1
 │   ├─ title
 │   ├─ price
 │   └─ URL
 │
 ├─ li.Product  ← 商品2
 │   ├─ title
 │   ├─ price
 │   └─ URL
 │
 └─ ...

これが非常に重要です。


7. fields と strategies — 「何を取り出すか」

例:

"title": {
  "strategies": [
    {
      "type": "ATTRIBUTE",
      "selector": "a.Product__titleLink[data-auction-title]",
      "attribute": "data-auction-title"
    },
    {
      "type": "TEXT",
      "selector": "a.Product__titleLink"
    }
  ]
}

ここではtitleを取得する方法を2つ書いています。

  1. data-auction-title属性から取得
  2. 取れなければタグ内文字列から取得

WEB DATA COLLECTORはstrategiesを上から順に試し、最初に値が取れた方法を採用します。

この仕組みを「フォールバック」と考えると分かりやすいです。

第1候補 ATTRIBUTE
   ↓ 取れない
第2候補 TEXT
   ↓ 取れた
採用

サイトのHTMLが少し変わった場合にも壊れにくくできます。


8. 取得TYPE一覧 — 添付ソースで実装されているTYPEをすべて解説

以下は、添付ソース GenericExtractor.php を解析して確認した現在の実装です。


8.1 TEXT

用途

タグの画面に表示される文字列を取得します。

HTML

<span class="price">10,500円</span>

JSON

{
  "type": "TEXT",
  "selector": ".price"
}

結果

10,500円

添付HTMLでの例

<h1>■ ほぼ新品 ■ オリンパス OLYMPUS OM-D E-M5 ...</h1>

例えば、

{
  "type": "TEXT",
  "selector": "#itemTitle h1"
}

とすると商品タイトルを取得できます。

向いているもの

  • 商品名
  • 価格表示
  • 件数
  • 状態
  • 店舗名
  • 説明文

8.2 ATTRIBUTE

用途

タグの属性値を取得します。

HTML

<a href="/item/123" data-id="123">商品を見る</a>

JSON

{
  "type": "ATTRIBUTE",
  "selector": "a[data-id]",
  "attribute": "data-id"
}

結果

123

hrefを取るなら、

{
  "type": "ATTRIBUTE",
  "selector": "a[href]",
  "attribute": "href"
}

です。

Yahoo!オークションでの実例

{
  "type": "ATTRIBUTE",
  "selector": "a.Product__titleLink[data-auction-id]",
  "attribute": "data-auction-id"
}

向いているもの

  • href
  • src
  • data-*
  • alt
  • title
  • value

8.3 ATTRIBUTE_FIRST

用途

現在の実装ではATTRIBUTEと同じ動作です。

内部では、CSSセレクターに複数一致しても先頭の1要素を使います。

{
  "type": "ATTRIBUTE_FIRST",
  "selector": "img",
  "attribute": "src"
}

通常はATTRIBUTEで十分です。


8.4 ATTRIBUTE_REGEX

用途

属性値を取得した後、さらに正規表現で必要な部分だけ抜き出すTYPEです。

HTML

<a href="/jp/auction/e1239762031">商品</a>

JSON例

{
  "type": "ATTRIBUTE_REGEX",
  "selector": "a[href*=\"/jp/auction/\"]",
  "attribute": "href",
  "pattern": "#/jp/auction/(?<value>[a-z0-9]+)#i"
}

結果

e1239762031

正規表現の結果は、

  1. valueという名前付きグループ
  2. なければ第1キャプチャ (...)

の順で使用されます。

向いているもの

  • URLの一部分だけ欲しい
  • 属性に複数情報が混ざっている
  • IDだけ抜き出したい

8.5 XPATH

用途

CSSセレクターでは表現しにくい、HTMLの位置関係や文字内容を条件にして探す場合に使います。

XPATHは取得した要素のテキストを返します。

Yahoo!オークションで使用している例:

{
  "type": "XPATH",
  "selector": "//h2[.//span[normalize-space(.)='商品説明']]/parent::header/following-sibling::div[1]"
}

これは概念的には、

  1. 「商品説明」という文字を持つspanを含むh2を探す
  2. その親headerへ移動
  3. headerの次にあるdivを取得

という意味です。

いつ使うか

CSSで簡単に書ける場合はCSSを優先してください。

XPathは、

  • 「商品説明」という文字を基準にしたい
  • 兄弟要素の前後関係をたどりたい
  • class名が毎回ランダムで使えない

といった場合に有効です。


8.6 XPATH_TEXT

現在の実装ではXPATHと同じく、選択した要素のテキストを取得します。

{
  "type": "XPATH_TEXT",
  "selector": "//h1"
}

または、旧形式としてxpathキーも利用できます。

{
  "type": "XPATH_TEXT",
  "xpath": "//h1"
}

8.7 XPATH_ATTRIBUTE

用途

XPathで要素を探し、その属性値を取得します。

JSON例

{
  "type": "XPATH_ATTRIBUTE",
  "selector": "//a[contains(@href,'bid_hist')]",
  "attribute": "href"
}

向いているもの

XPathで場所を特定した上で、

  • href
  • src
  • data属性

などを取得したい場合です。


8.8 SCRIPT_JSON

用途

HTML内の<script>タグに埋め込まれたJSONを読み取り、その中の値を取得します。

最近のWebサイトでは非常に重要なTYPEです。

添付Yahoo!オークションHTMLには、次のようなタグがあります。

<script id="__NEXT_DATA__" type="application/json">
{
  "props": {
    "pageProps": {
      "initialState": {
        "item": {
          "detail": {
            "item": {
              "descriptionHtml": "商品説明全文..."
            }
          }
        }
      }
    }
  }
}
</script>

設定は次のようになります。

{
  "type": "SCRIPT_JSON",
  "selector": "script#__NEXT_DATA__",
  "path": "props.pageProps.initialState.item.detail.item.descriptionHtml"
}

処理の流れ

script#__NEXT_DATA__ を探す
        ↓
scriptタグの中身を読む
        ↓
JSONとして解析
        ↓
props
 └ pageProps
    └ initialState
       └ item
          └ detail
             └ item
                └ descriptionHtml
        ↓
値を返す

pathの書き方

JSONの階層を.でつなぎます。

props.pageProps.initialState.item.detail.item.descriptionHtml

添付HTMLから取れる別の例

オークションID:

props.pageProps.initialState.item.detail.item.auctionId

現在価格:

props.pageProps.initialState.item.detail.item.price

入札数:

props.pageProps.initialState.item.detail.item.bids

終了日時:

props.pageProps.initialState.item.detail.item.endTime

SCRIPT_JSONの利点

画面表示HTMLよりも、埋め込みJSONの方が、

  • 値が明確
  • 数値が加工前
  • class名変更の影響を受けにくい
  • HTML全文が省略されていても元データが入っていることがある

という場合があります。

Yahoo!オークションの商品説明全文取得では特に有効です。


8.9 REGEX

用途

ページまたは現在のHTMLコンテキスト全体に対して正規表現を使い、値を抜き出します。

HTML

<script>
var pageData = {"productID":"e1239762031","price":"10500"};
</script>

JSON例

{
  "type": "REGEX",
  "pattern": "/\"productID\":\"(?<value>[^\"]+)\"/"
}

結果

e1239762031

注意

REGEXは強力ですが、HTML構造が変わると壊れやすくなります。

推奨順位は通常、

ATTRIBUTE / TEXT / SCRIPT_JSON
        ↓
XPATH
        ↓
REGEX

です。


8.10 LABEL_VALUE

用途

画面上のテキストから、「ラベル名 : 値」形式の値を探します。

例

メーカー:OLYMPUS | 状態:未使用に近い | 色:シルバー

JSON

{
  "type": "LABEL_VALUE",
  "label": "メーカー"
}

結果

OLYMPUS

内部では空白を整えた後、指定したラベルの後ろの値を探します。

区切りは主に | または | までです。

向いているもの

HTMLタグに特徴がなく、画面表示が、

型番:ABC-123 | 色:黒 | 状態:中古

のように並んでいる場合です。


8.11 CONSTANT

用途

HTMLから取得せず、固定値を設定します。

{
  "type": "CONSTANT",
  "value": "Yahoo!オークション"
}

結果

Yahoo!オークション

向いているもの

  • サイト名
  • データ種別
  • 固定カテゴリ
  • 固定フラグ

8.12 URL_TEMPLATE

用途

取得済みの値を使ってURLを組み立てます。

{
  "type": "URL_TEMPLATE",
  "url_template": "https://page.auctions.yahoo.co.jp/jp/auction/{external_id}"
}

external_idが、

e1239762031

なら、

https://page.auctions.yahoo.co.jp/jp/auction/e1239762031

になります。

変数

{フィールド名}で取得済み値を埋め込みます。

{external_id}
{title}

ドット区切りのパスも内部的に参照できます。

重要

URL_TEMPLATEは、通常のHTML値抽出TYPEではなく、次のページURLを作る処理や仮想URL列を作る処理で使用されます。


8.13 PAGE_URL

用途

すでにページパイプラインで解決済みのURLをフィールド値として使います。

{
  "type": "PAGE_URL",
  "page": "detail"
}

例えばdetailページURLが、

https://auctions.yahoo.co.jp/jp/auction/e1239762031

なら、そのURLを値として返します。

実例

"detail_link": {
  "strategies": [
    {
      "type": "PAGE_URL",
      "page": "detail"
    },
    {
      "type": "URL_TEMPLATE",
      "url_template": "https://page.auctions.yahoo.co.jp/jp/auction/{external_id}"
    }
  ]
}

意味は、

まず実際に解決したdetail URLを使用
        ↓ なければ
external_idからURLを組み立てる

です。


9. TYPE早見表

TYPE対象何を返すか初心者向け使用頻度
TEXTHTML要素表示文字★★★★★
ATTRIBUTEHTML要素属性値★★★★★
ATTRIBUTE_FIRSTHTML要素先頭要素の属性値★★☆☆☆
ATTRIBUTE_REGEX属性値正規表現で抜いた部分★★☆☆☆
XPATHXPath要素テキスト★★★☆☆
XPATH_TEXTXPath要素テキスト★★☆☆☆
XPATH_ATTRIBUTEXPath要素属性値★★☆☆☆
SCRIPT_JSONscript内JSONJSON内の指定値★★★★☆
REGEXHTML全体正規表現で抜いた部分★★☆☆☆
LABEL_VALUEテキスト全体ラベル後ろの値★★☆☆☆
CONSTANTなし固定値★★★☆☆
URL_TEMPLATE取得済み値組み立てたURL★★★★☆
PAGE_URLページ処理結果解決済みページURL★★★★☆
CSS_ALLitem_container一致する複数コンテナ★★★★★

10. transform — 取得後の値を整える「フィルタ/変換」

TYPEで値を取ったあと、transformで値を加工できます。

{
  "type": "TEXT",
  "selector": ".price",
  "transform": [
    "TRIM",
    "INTEGER"
  ]
}

transformは上から順番に適用されます。


10.1 TRIM

前後の空白を削除します。

入力:

   OLYMPUS E-M5   

出力:

OLYMPUS E-M5
"transform": ["TRIM"]

10.2 NORMALIZE_WHITESPACE

連続する空白・改行・タブなどを1個の空白にまとめます。

入力:

OLYMPUS

   OM-D     E-M5

出力:

OLYMPUS OM-D E-M5
"transform": ["NORMALIZE_WHITESPACE"]

説明文やタイトルの整形に便利です。


10.3 INTEGER

数字とマイナス記号以外を除去して整数にします。

入力:

10,500円

出力:

10500
"transform": ["INTEGER"]

価格、入札件数、在庫数などに向いています。


10.4 FLOAT

数字・小数点・マイナス記号を残して小数値にします。

入力:

12.50 kg

出力:

12.5
"transform": ["FLOAT"]

10.5 BOOLEAN

値を真偽値に変換します。

"transform": ["BOOLEAN"]

主に、

true / false
1 / 0
yes / no

のような値をbooleanとして扱うときに使用します。


10.6 ABSOLUTE_URL

相対URLを絶対URLにします。

HTML:

<a href="/jp/auction/e1239762031">...</a>

取得値:

/jp/auction/e1239762031

Base URLが、

https://auctions.yahoo.co.jp

なら、変換後は、

https://auctions.yahoo.co.jp/jp/auction/e1239762031

になります。

"transform": ["ABSOLUTE_URL"]

リンクや画像URLでは非常によく使います。


10.7 LOWERCASE

英字を小文字にします。

OLYMPUS → olympus
"transform": ["LOWERCASE"]

10.8 UPPERCASE

英字を大文字にします。

Olympus → OLYMPUS
"transform": ["UPPERCASE"]

11. transformを複数組み合わせる

例えば、

  10,500円  

を整数10500にしたい場合、

"transform": [
  "TRIM",
  "INTEGER"
]

とできます。

URLなら、

"transform": [
  "TRIM",
  "ABSOLUTE_URL"
]

という組み合わせが便利です。


12. selectorそのものが「検索フィルタ」になる

HTMLから必要な要素だけ選ぶ一番基本的なフィルタはselectorです。

例えば次のHTMLがあるとします。

<a href="/help">ヘルプ</a>
<a href="/jp/show/bid_hist?aID=e123">入札履歴</a>
<a href="/seller/abc">出品者</a>

単に、

a[href]

では先頭の「ヘルプ」が取られる可能性があります。

そこで、

a[href*="bid_hist"]

と絞ります。

このように、

良いselectorを作ること自体が、もっとも重要なフィルタ処理

です。


13. CSS selectorによる代表的な絞り込み

13.1 classで絞る

.Product__titleLink

13.2 タグ + class

a.Product__titleLink

13.3 属性があるものだけ

a.Product__titleLink[data-auction-id]

13.4 属性値に文字列を含む

a[href*="bid_hist"]

13.5 親要素の中だけ探す

#itemTitle h1

13.6 さらに条件を重ねる

a.Product__titleLink[data-auction-id][href]

条件を重ねるほど誤取得しにくくなります。


14. REGEX / ATTRIBUTE_REGEXによる値フィルタ

selectorは「どのタグか」を絞るものです。

REGEXは「その値の中のどの部分か」を絞るものです。

HTMLタグを選ぶ
    ↓ selector
属性値を取得
    ↓ ATTRIBUTE
属性の中の一部を抜く
    ↓ ATTRIBUTE_REGEX

例えば、

https://auctions.yahoo.co.jp/jp/auction/e1239762031?foo=1

から、

e1239762031

だけを取り出す、といった用途です。


15. fieldsのfilterable / sortable

収集結果一覧の列は、フィールド定義により検索フィルタと並び替えを制御できます。

"detail_link": {
  "strategies": [...],
  "filterable": false,
  "sortable": false
}

filterable

"filterable": true

または省略時は、基本的にフィルタ対象になります。

現在の実装では、文字列と判定された動的列について部分一致フィルタが表示されます。

例:

OLYMPUS OM-D E-M5

に対して、

E-M5

と入力すると一致します。

数値・boolean列には同じテキストフィルタは表示されません。

リンクボタンのように検索対象にする必要がない列では、

"filterable": false

とするのが分かりやすいです。

sortable

"sortable": true

で列見出しから並び替えできます。

現在の実装では値を、

  • number
  • boolean
  • text

のいずれかとして判定し、それに合わせて並び替えます。

リンク列などは、

"sortable": false

にできます。


16. display TYPE — 取得値をどう表示するか

添付ソースで明示的に実装されているdisplayのTYPEは、現在 BUTTON です。

"display": {
  "type": "BUTTON",
  "label": "詳細",
  "icon": "EXTERNAL_LINK",
  "icon_position": "RIGHT",
  "target": "_blank",
  "empty": "-"
}

URL値を一覧画面で「詳細 ↗」のようなボタンとして表示できます。

icon

現在の実装では次が扱われています。

ARROW
EXTERNAL_LINK
OPEN

EXTERNAL_LINKとOPENは ↗ 表示です。

icon_position

LEFT
RIGHT

target

通常は、

"target": "_blank"

とすると別タブで開きます。


17. source — 次ページのURLをどこから得るか

DETAILやRELATEDページは、前のページからURLを取得してたどります。

Yahoo!オークションのdetail定義:

"source": {
  "from": "list",
  "scope": "ITEM_CONTAINER",
  "strategies": [
    {
      "type": "ATTRIBUTE",
      "selector": "a.Product__titleLink[href]",
      "attribute": "href",
      "transform": ["ABSOLUTE_URL"]
    }
  ]
}

意味:

listの商品1件分HTML
      ↓
a.Product__titleLink[href] を探す
      ↓
hrefを取得
      ↓
絶対URLに変換
      ↓
detailページへ通信

18. from — どのページから次へ進むか

"from": "list"

なら、listページからdetailへ進みます。

"from": "detail"

なら、detailページからhistoryへ進みます。

Yahoo!オークションの流れは、

list
 ↓
detail
 ↓
history

です。


19. required — ページが見つからないときにどうするか

"required": true

そのページが必須です。

URLが見つからない場合は処理失敗となります。

"required": false

なら任意ページです。

Yahoo!オークションの入札履歴は、入札がない商品などでは利用できない可能性があるため、

"required": false

になっています。


20. required_fields — LISTで最低限必要な値

"required_fields": [
  "external_id"
]

これは、商品を識別する上で必須にしたいフィールドを表します。

Yahoo!オークションではexternal_idがオークションIDです。

初心者向けには、

商品を一意に識別できるIDを必須にする

と覚えるとよいでしょう。


21. merge — LIST / DETAIL / RELATEDをどうまとめるか

例:

"merge": {
  "page_order": [
    "list",
    "detail",
    "history"
  ],
  "detail_overwrites_list": true,
  "ignore_null_values": true,
  "collections_keep_page_key": true
}

page_order

データをまとめる順番です。

list → detail → history

detailoverwriteslist

同じフィールドがlistとdetailの両方にある場合、detail側を優先する考え方です。

ignorenullvalues

空の値で既存値を上書きしにくくするための設定です。

collectionskeeppage_key

historyのような複数件データは、historyというページキーの下にまとめて保持する構成です。


22. system_mapping — Collector内部で特別に使う値

"system_mapping": {
  "external_id": "external_id",
  "source_url": "_page_urls.detail",
  "display_label": "title",
  "image_url": "image_url"
}

これは収集した任意フィールドのうち、Collectorがシステム上どの意味として扱うかを指定します。

system_mapping意味
external_id外部サイト上の一意ID
source_url元データのURL
display_label一覧で代表表示する文字列
image_url代表画像URL

23. Yahoo!オークション設定を1項目ずつ読む

external_id

"external_id": {
  "strategies": [
    {
      "type": "ATTRIBUTE",
      "selector": "a.Product__titleLink[data-auction-id]",
      "attribute": "data-auction-id"
    }
  ]
}

日本語にすると、

aタグのうちProduct__titleLinkクラスを持ち、さらにdata-auction-id属性を持つものを探し、そのdata-auction-idの値を取得する。

です。


title

"title": {
  "strategies": [
    {
      "type": "ATTRIBUTE",
      "selector": "a.Product__titleLink[data-auction-title]",
      "attribute": "data-auction-title"
    },
    {
      "type": "TEXT",
      "selector": "a.Product__titleLink"
    }
  ]
}

日本語にすると、

まずdata-auction-title属性からタイトルを取る。取れなければリンクに表示されている文字を取る。

です。

このように2候補を用意するのは非常に良い設定方法です。


current_price

"current_price": {
  "strategies": [
    {
      "type": "ATTRIBUTE",
      "selector": "a.Product__titleLink[data-auction-price]",
      "attribute": "data-auction-price"
    }
  ]
}

表示文字列の「10,500円」を解析するのではなく、data属性に入っている価格を直接取得しています。

このように、画面表示用の文字より構造化された属性値があるなら属性値を優先するのがおすすめです。


bid_count

"bid_count": {
  "strategies": [
    {
      "type": "TEXT",
      "selector": ".Product__bid"
    }
  ]
}

表示文字を直接取得します。

もし結果が、

7件

で数値7として保存したいなら、

"transform": ["INTEGER"]

を追加できます。


image_url

"image_url": {
  "strategies": [
    {
      "type": "ATTRIBUTE",
      "selector": "a.Product__titleLink[data-auction-img]",
      "attribute": "data-auction-img",
      "transform": ["ABSOLUTE_URL"]
    },
    {
      "type": "ATTRIBUTE",
      "selector": "img.Product__imageData",
      "attribute": "src",
      "transform": ["ABSOLUTE_URL"]
    }
  ]
}

考え方は、

第1候補 data-auction-img
      ↓ なければ
第2候補 imgのsrc

です。


24. 商品説明 — XPATHとSCRIPT_JSONの違い

現在の設定では2種類あります。

"description": {
  "strategies": [
    {
      "type": "XPATH",
      "selector": "//h2[.//span[normalize-space(.)='商品説明']]/parent::header/following-sibling::div[1]"
    }
  ]
}

と、

"description_html": {
  "strategies": [
    {
      "type": "SCRIPT_JSON",
      "selector": "script#__NEXT_DATA__",
      "path": "props.pageProps.initialState.item.detail.item.descriptionHtml"
    }
  ]
}

XPATH版

ブラウザに表示されるHTML部分からテキストを取ります。

長所:

  • 見えている内容に近い
  • JSON構造を知らなくても取得できる

短所:

  • 画面側で一部省略されている可能性
  • DOM構造変更の影響を受ける

SCRIPT_JSON版

ページ内部の構造化データから元のHTMLを取ります。

長所:

  • 商品説明全文を取りやすい
  • HTML表示側のclass変更に強いことがある

短所:

  • JSONの階層が変わると取れなくなる

Yahoo!オークションの添付HTMLでは、descriptionHtmlに詳細な商品説明HTMLが入っているため、全文保存にはSCRIPT_JSONが適しています。


25. 初心者がselectorを作る手順

手順1: 欲しい文字をブラウザで確認

例:

10,500円

手順2: 開発者ツールでその要素を調べる

Firefox / Chromeでは、対象を右クリックして、

調査

または、

検証

を選びます。

手順3: できるだけ安定した特徴を探す

優先順位の目安:

id
↓
意味のあるclass
↓
data-*属性
↓
親子関係
↓
XPath
↓
REGEX

手順4: 1商品だけでなく別の商品でも同じか確認

商品IDそのものをselectorに書いてしまうと別商品で動きません。

悪い例:

a[data-auction-id="e1239762031"]

良い例:

a[data-auction-id]

26. 「良いselector」と「壊れやすいselector」

良い例

#description
script#__NEXT_DATA__
a.Product__titleLink[data-auction-id]
a[href*="/jp/show/bid_hist"]

意味を持つid・class・属性を使っています。

注意が必要な例

添付Yahoo! HTMLには、

.sc-efdc6b77-4
.lkGDco
.iRjINw

のような自動生成に見えるclassが多数あります。

これらはサイト更新で変わる可能性があります。

可能なら、

#itemTitle h1
#description

のような、意味のある固定idを基準にする方が安全です。


27. strategiesは「保険」をかけられる

例えば画像URLは、サイトによって、

<img src="...">

の場合もあれば、遅延読み込みで、

<img data-src="...">

の場合もあります。

その場合、

"strategies": [
  {
    "type": "ATTRIBUTE",
    "selector": "img[data-src]",
    "attribute": "data-src",
    "transform": ["ABSOLUTE_URL"]
  },
  {
    "type": "ATTRIBUTE",
    "selector": "img[src]",
    "attribute": "src",
    "transform": ["ABSOLUTE_URL"]
  }
]

としておけば、どちらかで取得できます。


28. 実践例1 — 商品名と価格を取得

HTML:

<li class="Product">
  <a class="Product__titleLink"
     data-auction-title="OLYMPUS OM-D E-M5"
     data-auction-price="10500">
    OLYMPUS OM-D E-M5
  </a>
</li>

設定:

{
  "item_container": {
    "type": "CSS_ALL",
    "selector": "li.Product"
  },
  "fields": {
    "title": {
      "strategies": [
        {
          "type": "ATTRIBUTE",
          "selector": "a.Product__titleLink[data-auction-title]",
          "attribute": "data-auction-title"
        }
      ]
    },
    "price": {
      "strategies": [
        {
          "type": "ATTRIBUTE",
          "selector": "a.Product__titleLink[data-auction-price]",
          "attribute": "data-auction-price",
          "transform": ["INTEGER"]
        }
      ]
    }
  }
}

結果:

{
  "title": "OLYMPUS OM-D E-M5",
  "price": 10500
}

29. 実践例2 — 詳細ページURLを取得

HTML:

<a class="Product__titleLink" href="/jp/auction/e1239762031">...</a>

設定:

{
  "type": "ATTRIBUTE",
  "selector": "a.Product__titleLink[href]",
  "attribute": "href",
  "transform": ["ABSOLUTE_URL"]
}

結果:

https://auctions.yahoo.co.jp/jp/auction/e1239762031

30. 実践例3 — 入札履歴URLを取得

添付HTML:

<a href="https://auctions.yahoo.co.jp/jp/show/bid_hist?aID=e1239762031">7件</a>

設定:

{
  "type": "ATTRIBUTE",
  "selector": "a[href*=\"/jp/show/bid_hist\"]",
  "attribute": "href",
  "transform": ["ABSOLUTE_URL"]
}

31. 実践例4 — SCRIPT_JSONから現在価格を取得

{
  "type": "SCRIPT_JSON",
  "selector": "script#__NEXT_DATA__",
  "path": "props.pageProps.initialState.item.detail.item.price"
}

添付HTMLでは、この値は、

10500

です。

画面上の、

10,500円

をTEXTで取得してINTEGER変換する方法もありますが、JSONに数値がある場合はSCRIPT_JSONの方が明確なケースがあります。


32. 実践例5 — 商品状態を取得

添付HTMLには、

<span>未使用に近い</span>

という表示があります。

また、__NEXT_DATA__にも、

"conditionName": "未使用に近い"

があります。

SCRIPT_JSONなら、

{
  "type": "SCRIPT_JSON",
  "selector": "script#__NEXT_DATA__",
  "path": "props.pageProps.initialState.item.detail.item.conditionName"
}

とできます。


33. どのTYPEを選べばよいか — 判断フロー

欲しい値はタグの画面表示文字?
 ├─ YES → TEXT
 └─ NO
      ↓
タグのhref/src/data-*などの属性?
 ├─ YES → ATTRIBUTE
 └─ NO
      ↓
<script type="application/json">等のJSONにある?
 ├─ YES → SCRIPT_JSON
 └─ NO
      ↓
CSSでは場所を指定しにくい?
 ├─ YES → XPATH / XPATH_ATTRIBUTE
 └─ NO
      ↓
文字列の一部分だけ抜きたい?
 ├─ 属性 → ATTRIBUTE_REGEX
 └─ HTML → REGEX
      ↓
固定値でよい?
 └─ CONSTANT

URLを作る場合:

すでにアクセスしたページのURL
 └─ PAGE_URL

取得済みIDからURLを組み立てる
 └─ URL_TEMPLATE

34. 初心者向けおすすめ順

最初は次の5種類を覚えれば、多くのサイトに対応できます。

  1. CSS_ALL
  2. TEXT
  3. ATTRIBUTE
  4. ABSOLUTE_URL
  5. SCRIPT_JSON

次に必要になったら、

  1. INTEGER
  2. NORMALIZE_WHITESPACE
  3. XPATH
  4. URL_TEMPLATE
  5. PAGE_URL

を覚えるとよいでしょう。

REGEXは最後の手段として考える方が保守しやすくなります。


35. 現在のYahoo!オークション設定を日本語で要約

現在のExtraction definition JSONは、次の処理を行っています。

【LIST】Yahoo!オークション検索結果
  ↓
li.Product を商品1件として繰り返す
  ↓
external_id
  └ data-auction-id属性
  ↓
title
  ├ data-auction-title属性
  └ 失敗時はリンク表示文字
  ↓
current_price
  └ data-auction-price属性
  ↓
bid_count
  └ .Product__bid の表示文字
  ↓
image_url
  ├ data-auction-img
  └ 失敗時 imgのsrc
  ↓
detail URL
  └ 商品リンクhref

【DETAIL】商品詳細ページ
  ↓
description
  └ XPathで「商品説明」の次の領域からテキスト取得
  ↓
description_html
  └ __NEXT_DATA__ JSONのdescriptionHtmlを取得

【HISTORY】入札履歴
  ↓
detailページからbid_histリンクを探す
  ↓
見つからなければexternal_idからURLを生成
  ↓
入札履歴ページへアクセス

36. 設定作成時のチェックリスト

  • [ ] item_containerが本当に「商品1件」を囲んでいるか
  • [ ] selectorは複数商品でも同じ構造か
  • [ ] 自動生成classだけに依存していないか
  • [ ] IDやdata属性など、より安定した目印がないか
  • [ ] URLはABSOLUTE_URLを付ける必要がないか
  • [ ] 価格や件数はINTEGERにした方がよいか
  • [ ] 値が取れない場合の第2strategyを用意できないか
  • [ ] ページ内JSONにもっと安定した元データがないか
  • [ ] DETAILが必須ならrequired: trueになっているか
  • [ ] RELATEDがなくてもよいならrequired: falseか
  • [ ] systemmapping.externalidが一意IDを指しているか
  • [ ] URL列はfilterable: false、sortable: falseでよいか

37. よくある失敗と原因

何も取れない

主な原因:

  • selectorのclass名が間違っている
  • .や#を付け忘れている
  • LISTではitem_containerの外側を探そうとしている
  • ページがJavaScript描画で、取得したHTMLに目的データがない

対策:

  • 保存されたHTMLに目的の文字列が本当にあるか検索する
  • __NEXT_DATA__など埋め込みJSONを確認する

URLが /jp/auction/... のまま

ABSOLUTE_URLを付けます。

"transform": ["ABSOLUTE_URL"]

価格が「10,500円」の文字列になってしまう

数値にしたければ、

"transform": ["INTEGER"]

を付けます。


最初の商品だけ取れてしまう

一覧ページでは、fieldsではなく、まずitem_containerを正しく指定します。

"item_container": {
  "type": "CSS_ALL",
  "selector": "li.Product"
}

各fieldのstrategy自体は、各コンテナ内の先頭一致値を取ります。


38. 補足 — CSS selectorとXPathの使い分け

条件CSSXPath
classで探す◎○
idで探す◎○
属性で探す◎◎
親子関係◎◎
「表示文字が商品説明の要素」を探す△◎
兄弟要素の前後関係△◎
初心者の読みやすさ◎△

原則は、

CSSで書けるならCSS、CSSで難しい場所だけXPath

がおすすめです。


39. 補足 — 添付ソースから確認した実装上の重要点

このガイドは添付ソースコードを基準にしています。現在の実装では、次の動作になっています。

  1. CSS selectorはSymfony DomCrawlerのfilter()で処理されます。
  2. XPathはfilterXPath()で処理されます。
  3. 通常のfield strategyは一致要素の先頭1件を値として使います。
  4. LIST/COLLECTIONの複数件処理は、item_containerでコンテナを複数選択して繰り返すことで実現します。
  5. strategiesは上から順に試し、最初の非NULL・非空文字値を採用します。
  6. SCRIPTJSONはscriptのtextContentをjsondecode()し、.区切りpathを順にたどります。
  7. URL_TEMPLATEの {変数} はURLエンコードして展開されます。
  8. PAGE_URLはページパイプラインで解決済みのURLを仮想フィールドに設定するために使われます。
  9. transformは配列順に適用されます。
  10. 未知のtransform名は現在の実装では値をそのまま返します。
  11. 未対応strategy TYPEはUNSUPPORTEDSTRATEGYTYPEエラーになります。

40. まとめ

最初に覚えるべきことは、実はそれほど多くありません。

HTMLの「1件分の箱」を見つける
        ↓ CSS_ALL
箱の中から欲しいタグを選ぶ
        ↓ selector
表示文字なら
        ↓ TEXT
href / src / data-*なら
        ↓ ATTRIBUTE
JSONに入っているなら
        ↓ SCRIPT_JSON
取った値を整える
        ↓ transform
次ページへ進む
        ↓ source + ATTRIBUTE / URL_TEMPLATE

Yahoo!オークションの設定は一見複雑ですが、実際にはこの基本操作の組み合わせです。

まずは、

CSS_ALL
TEXT
ATTRIBUTE
SCRIPT_JSON
ABSOLUTE_URL
INTEGER

の6つから始めると理解しやすいでしょう。


付録A. 現在実装されている取得TYPE一覧

TEXT
ATTRIBUTE
ATTRIBUTE_FIRST
ATTRIBUTE_REGEX
XPATH
XPATH_TEXT
XPATH_ATTRIBUTE
SCRIPT_JSON
REGEX
LABEL_VALUE
CONSTANT
URL_TEMPLATE
PAGE_URL

コンテナ選択:

CSS_ALL

付録B. 現在実装されているtransform一覧

TRIM
NORMALIZE_WHITESPACE
INTEGER
FLOAT
BOOLEAN
ABSOLUTE_URL
LOWERCASE
UPPERCASE

付録C. 現在実装されている表示TYPE

BUTTON

BUTTONのアイコン指定:

ARROW
EXTERNAL_LINK
OPEN

付録D. Yahoo!オークション商品詳細HTMLで確認できるSCRIPT_JSON例

{
  "auction_id": {
    "type": "SCRIPT_JSON",
    "selector": "script#__NEXT_DATA__",
    "path": "props.pageProps.initialState.item.detail.item.auctionId"
  },
  "price": {
    "type": "SCRIPT_JSON",
    "selector": "script#__NEXT_DATA__",
    "path": "props.pageProps.initialState.item.detail.item.price"
  },
  "bid_count": {
    "type": "SCRIPT_JSON",
    "selector": "script#__NEXT_DATA__",
    "path": "props.pageProps.initialState.item.detail.item.bids"
  },
  "condition": {
    "type": "SCRIPT_JSON",
    "selector": "script#__NEXT_DATA__",
    "path": "props.pageProps.initialState.item.detail.item.conditionName"
  },
  "description_html": {
    "type": "SCRIPT_JSON",
    "selector": "script#__NEXT_DATA__",
    "path": "props.pageProps.initialState.item.detail.item.descriptionHtml"
  }
}

※上記は「strategyオブジェクトの例」を見やすく並べた説明用表現です。実際のExtraction definition JSONでは各fieldのstrategies配列内に配置します。


付録E. 用語集

用語意味
HTMLWebページの構造そのもの
CSS本来は見た目を指定する仕組み。CSS selectorはHTML要素選択にも使う
selectorどのHTML要素を探すかという指定
attributehref/src/class/data-*などタグに付いた情報
item_container一覧ページの「1件分」を囲む要素
field取得して保存したい1項目
strategyそのfieldをどう取得するかという候補
transform取得後の値を整形・変換する処理
XPathHTML/XMLを階層や条件で検索する式
REGEX文字列パターンで一部を抜き出す正規表現
SCRIPT_JSONscriptタグに埋め込まれたJSONを読むTYPE
LIST一覧ページ
DETAIL詳細ページ
RELATED詳細からさらにたどる関連ページ
OBJECT1ページから1組の結果
COLLECTION1ページから複数件の結果
fallback第1候補が失敗したら第2候補を試すこと