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 |
| オークションID | data-auction-id属性 | ATTRIBUTE |
| 商品詳細URL | href属性 | 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 よく使うセレクター一覧
| 指定 | 意味 | 例 |
|---|---|---|
div | divタグ | div |
.Product | Productクラス | .Product |
li.Product | liタグかつProductクラス | li.Product |
#description | descriptionという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 .price | productの内側にあるprice | .product .price |
.product > .price | productの直下のprice | .product > .price |
.a.b | aとbの両方のclassを持つ | .a.b |
h1, h2 | h1またはh2 | h1, 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つ書いています。
data-auction-title属性から取得- 取れなければタグ内文字列から取得
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"
}
向いているもの
hrefsrcdata-*alttitlevalue
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
正規表現の結果は、
valueという名前付きグループ- なければ第1キャプチャ
(...)
の順で使用されます。
向いているもの
- URLの一部分だけ欲しい
- 属性に複数情報が混ざっている
- IDだけ抜き出したい
8.5 XPATH
用途
CSSセレクターでは表現しにくい、HTMLの位置関係や文字内容を条件にして探す場合に使います。
XPATHは取得した要素のテキストを返します。
Yahoo!オークションで使用している例:
{
"type": "XPATH",
"selector": "//h2[.//span[normalize-space(.)='商品説明']]/parent::header/following-sibling::div[1]"
}
これは概念的には、
- 「商品説明」という文字を持つspanを含むh2を探す
- その親headerへ移動
- 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 | 対象 | 何を返すか | 初心者向け使用頻度 |
|---|---|---|---|
TEXT | HTML要素 | 表示文字 | ★★★★★ |
ATTRIBUTE | HTML要素 | 属性値 | ★★★★★ |
ATTRIBUTE_FIRST | HTML要素 | 先頭要素の属性値 | ★★☆☆☆ |
ATTRIBUTE_REGEX | 属性値 | 正規表現で抜いた部分 | ★★☆☆☆ |
XPATH | XPath要素 | テキスト | ★★★☆☆ |
XPATH_TEXT | XPath要素 | テキスト | ★★☆☆☆ |
XPATH_ATTRIBUTE | XPath要素 | 属性値 | ★★☆☆☆ |
SCRIPT_JSON | script内JSON | JSON内の指定値 | ★★★★☆ |
REGEX | HTML全体 | 正規表現で抜いた部分 | ★★☆☆☆ |
LABEL_VALUE | テキスト全体 | ラベル後ろの値 | ★★☆☆☆ |
CONSTANT | なし | 固定値 | ★★★☆☆ |
URL_TEMPLATE | 取得済み値 | 組み立てたURL | ★★★★☆ |
PAGE_URL | ページ処理結果 | 解決済みページURL | ★★★★☆ |
CSS_ALL | item_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種類を覚えれば、多くのサイトに対応できます。
CSS_ALLTEXTATTRIBUTEABSOLUTE_URLSCRIPT_JSON
次に必要になったら、
INTEGERNORMALIZE_WHITESPACEXPATHURL_TEMPLATEPAGE_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の使い分け
| 条件 | CSS | XPath |
|---|---|---|
| classで探す | ◎ | ○ |
| idで探す | ◎ | ○ |
| 属性で探す | ◎ | ◎ |
| 親子関係 | ◎ | ◎ |
| 「表示文字が商品説明の要素」を探す | △ | ◎ |
| 兄弟要素の前後関係 | △ | ◎ |
| 初心者の読みやすさ | ◎ | △ |
原則は、
CSSで書けるならCSS、CSSで難しい場所だけXPath
がおすすめです。
39. 補足 — 添付ソースから確認した実装上の重要点
このガイドは添付ソースコードを基準にしています。現在の実装では、次の動作になっています。
- CSS selectorはSymfony DomCrawlerの
filter()で処理されます。 - XPathは
filterXPath()で処理されます。 - 通常のfield strategyは一致要素の先頭1件を値として使います。
- LIST/COLLECTIONの複数件処理は、
item_containerでコンテナを複数選択して繰り返すことで実現します。 - strategiesは上から順に試し、最初の非NULL・非空文字値を採用します。
SCRIPTJSONはscriptのtextContentをjsondecode()し、.区切りpathを順にたどります。URL_TEMPLATEの{変数}はURLエンコードして展開されます。PAGE_URLはページパイプラインで解決済みのURLを仮想フィールドに設定するために使われます。- transformは配列順に適用されます。
- 未知のtransform名は現在の実装では値をそのまま返します。
- 未対応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. 用語集
| 用語 | 意味 |
|---|---|
| HTML | Webページの構造そのもの |
| CSS | 本来は見た目を指定する仕組み。CSS selectorはHTML要素選択にも使う |
| selector | どのHTML要素を探すかという指定 |
| attribute | href/src/class/data-*などタグに付いた情報 |
| item_container | 一覧ページの「1件分」を囲む要素 |
| field | 取得して保存したい1項目 |
| strategy | そのfieldをどう取得するかという候補 |
| transform | 取得後の値を整形・変換する処理 |
| XPath | HTML/XMLを階層や条件で検索する式 |
| REGEX | 文字列パターンで一部を抜き出す正規表現 |
| SCRIPT_JSON | scriptタグに埋め込まれたJSONを読むTYPE |
| LIST | 一覧ページ |
| DETAIL | 詳細ページ |
| RELATED | 詳細からさらにたどる関連ページ |
| OBJECT | 1ページから1組の結果 |
| COLLECTION | 1ページから複数件の結果 |
| fallback | 第1候補が失敗したら第2候補を試すこと |