質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
スクレイピング

スクレイピングとは、公開されているWebサイトからページ内の情報を抽出する技術です。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Scrapy

Scrapyは、Pythonで開発されたオープンソースソフトウェアです。スクレイピングという、Webサービスから必要な情報を取り出したり自動操作をしたりする技術を使うものです。

Q&A

0回答

1249閲覧

scrapyで期待通りのJSONを出力したい

yosukemurakami

総合スコア12

スクレイピング

スクレイピングとは、公開されているWebサイトからページ内の情報を抽出する技術です。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Scrapy

Scrapyは、Pythonで開発されたオープンソースソフトウェアです。スクレイピングという、Webサービスから必要な情報を取り出したり自動操作をしたりする技術を使うものです。

0グッド

0クリップ

投稿2021/03/22 12:19

編集2021/03/23 00:08

スクレイピングフレームワークscrapyを使用して、あるコンビニ商品のデータをJSON出力したいです。
ファイル構成を以下に示します。

const

1# ドメイン 2DOMAIN = "xxx.co.jp" 3# URL 4URL = "https://www.xxx.co.jp/" 5# 商品グループのlink_prefix 6GROUP_LINK_PREFIX = "/products/a/cat/" 7# 商品のカテゴリーのlink_prefix 8PRODUCT_LIST_CLASS_PREFIX = "list_inner -item-code-" 9# 商品名のdiv要素のclass名 10PRODUCT_TTL_CLASS = "item_ttl" 11# 商品名のdiv要素のclass名 12PRODUCT_PRICE_CLASS = "item_price" 13# 商品名のdiv要素のclass名 14PRODUCT_REGION_CLASS = "item_region" 15# 商品名のdiv要素のclass名 16PRODUCT_LAUNCH_CLASS = "item_launch" 17# ページャーのdiv要素のclass名 18PRODUCT_PAGER_CLASS = "pager" 19# 商品一覧の"次へ"のリンクテキスト 20PRODUCT_NEXT_PAGE = "次へ" 21# 販売地域が文字列でしゅとくされるので、配列にするため区切り文字を定義 22SALES_AREA_DELIMITER = "、" 23# 税込価格のみを取得するための正規表現 24INCLUDED_TAX_PRICE_REGEXP = "(?<=税込)[0-9]+" 25 26# "おにぎり"リンクのテキスト 27ONIGIRI = "おにぎり"

spider

1import scrapy 2import app.const as const 3from app.items import ConvenienceStoreProduct 4 5 6class OnigiriSpider(scrapy.Spider): 7 name = 'onigiri' 8 allowed_domains = [const.DOMAIN] 9 start_urls = [const.URL] 10 11 def parse(self, response): 12 """'おにぎり'リンクをたどるために初めに呼ばれる 13 """ 14 onigiri_category_relative_path = response.xpath('//a[contains(text(), "' + const.ONIGIRI + '")]/@href').get() 15 yield scrapy.Request( 16 response.urljoin(onigiri_category_relative_path), 17 callback = self.onigiri_group_parse 18 ) 19 20 def onigiri_group_parse(self, response): 21 """'おにぎり'のグループリンクをたどるために呼ばれる 22 """ 23 onigiri_group_relative_paths = response.xpath('//a[starts-with(@href, "' + const.GROUP_LINK_PREFIX + '")]/@href').getall() 24 for path in onigiri_group_relative_paths: 25 yield scrapy.Request( 26 response.urljoin(path), callback = self.product_parse) 27 28 def product_parse(self, response): 29 """商品データを抽出 30 """ 31 for sel in response.xpath('//div[starts-with(@class, "' + const.PRODUCT_LIST_CLASS_PREFIX + '")]'): 32 yield ConvenienceStoreProduct( 33 name = sel.css('.' + const.PRODUCT_TTL_CLASS).xpath('./p/a/text()').get().replace(" ", " "), 34 included_tax_price = sel.css('.' + const.PRODUCT_PRICE_CLASS).xpath('./p/text()').re_first(r'' + const.INCLUDED_TAX_PRICE_REGEXP + ''), 35 sales_area = sel.css('.' + const.PRODUCT_REGION_CLASS).xpath('./p/text()').get().split(const.SALES_AREA_DELIMITER), 36 product_detail_link = sel.css('.' + const.PRODUCT_TTL_CLASS).xpath('./p/a/@href').get(), 37 img_link = sel.css('figure').xpath('./a/img/@data-original').get(), 38 ) 39 # "次へ"のリンクがある場合はそのリンクをたどって、このメソッドを再帰的に呼び出す 40 next_page = response.css('.' + const.PRODUCT_PAGER_CLASS).xpath('//a[contains(text(), "' + const.PRODUCT_NEXT_PAGE + '")]/@href').get() 41 if next_page: 42 yield scrapy.Request(response.urljoin(next_page), self.product_parse)

item

1# Define here the models for your scraped items 2# 3# See documentation in: 4# https://docs.scrapy.org/en/latest/topics/items.html 5 6from dataclasses import dataclass 7 8@dataclass 9class ConvenienceStoreProduct: 10 name: str 11 included_tax_price: int 12 sales_area: list 13 product_detail_link: str 14 img_link: str

json

1// 期待するJSON 2{ 3 "onigiri": [ 4 { 5 "name": "具たっぷり手巻 海老マヨネーズ", 6 "included_tax_price": 135, 7 "sales_area": ["北海道", "福島県", "関東", "新潟県", "北陸"], 8 "product_detail_link": "/products/a/item/045578/", 9 "img_link": "https://img.7api-01.dp1.sej.co.jp/item-image/045578/F04CBC825AEC3EE2A91BF51AC50AF7A7.jpg" 10 }, 11 { 12 "name": "鯛だし飯手巻おにぎり すだち香る鯛塩焼き", 13 "included_tax_price": 151, 14 "sales_area": ["鳥取県", "島根県", "岡山県", "広島県", "四国"], 15 "product_detail_link": "/products/a/item/045419/", 16 "img_link": "https://img.7api-01.dp1.sej.co.jp/item-image/045419/D832C7C08CFCD5FD8CB443AC64CC1EB7.jpg" 17 } 18... 19 ] 20}

上記のpythonコードでは期待通りのJSONが出力できず、onigiri_group_parseメソッドとproduct_parseメソッドの間でうまくループして、"onigiri"をキーとした商品配列のJSONを出力したいです。(onigiri_group_parse で処理しているおにぎりのグループリンクが3つほどあり、それらの商品リンクをすべて辿って"onigiri"をキーとした商品配列の中に全部入れたいです。)ざっくりとした質問で大変申し訳ないのですが、何か実現できる良い実装案はございませんでしょうか? ご回答頂けると大変助かります、、よろしくお願いいたします。

バージョン
python:3.9.1
Scrapy:2.4.1

以下詳細情報追加

==================================================================

具体的なHTMLとしましては、以下のリンクから得られるHTMLページです。
https://www.sej.co.jp/products/a/onigiri/

このページに今
・「手巻きおにぎり」のラインナップを見る
・「直巻きおにぎり」のラインナップを見る
・「その他のおむすび」のラインナップを見る
の3つのリンクがあるのですが、それらのリンクを追うため、
onigiri_group_parse でリクエストを送っています。

なんとかうまくループして、上記の期待通りのJSONにしたいのですが、
現在

{ "onigiri": [ { "name": "具たっぷり手巻 海老マヨネーズ", "included_tax_price": 135, "sales_area": ["北海道", "福島県", "関東", "新潟県", "北陸"], "product_detail_link": "/products/a/item/045578/", "img_link": "https://img.7api-01.dp1.sej.co.jp/item-image/045578/F04CBC825AEC3EE2A91BF51AC50AF7A7.jpg" }, { "name": "鯛だし飯手巻おにぎり すだち香る鯛塩焼き", "included_tax_price": 151, "sales_area": ["鳥取県", "島根県", "岡山県", "広島県", "四国"], "product_detail_link": "/products/a/item/045419/", "img_link": "https://img.7api-01.dp1.sej.co.jp/item-image/045419/D832C7C08CFCD5FD8CB443AC64CC1EB7.jpg" } ], "onigiri": [ { "name": "具たっぷり手巻 海老マヨネーズ", "included_tax_price": 135, "sales_area": ["北海道", "福島県", "関東", "新潟県", "北陸"], "product_detail_link": "/products/a/item/045578/", "img_link": "https://img.7api-01.dp1.sej.co.jp/item-image/045578/F04CBC825AEC3EE2A91BF51AC50AF7A7.jpg" }, { "name": "鯛だし飯手巻おにぎり すだち香る鯛塩焼き", "included_tax_price": 151, "sales_area": ["鳥取県", "島根県", "岡山県", "広島県", "四国"], "product_detail_link": "/products/a/item/045419/", "img_link": "https://img.7api-01.dp1.sej.co.jp/item-image/045419/D832C7C08CFCD5FD8CB443AC64CC1EB7.jpg" } ], ... }

このように"onigiri"のキーをいくつか(今回は3つ分)持った形になるか、

{ { "name": "具たっぷり手巻 海老マヨネーズ", "included_tax_price": 135, "sales_area": ["北海道", "福島県", "関東", "新潟県", "北陸"], "product_detail_link": "/products/a/item/045578/", "img_link": "https://img.7api-01.dp1.sej.co.jp/item-image/045578/F04CBC825AEC3EE2A91BF51AC50AF7A7.jpg" }, { "name": "鯛だし飯手巻おにぎり すだち香る鯛塩焼き", "included_tax_price": 151, "sales_area": ["鳥取県", "島根県", "岡山県", "広島県", "四国"], "product_detail_link": "/products/a/item/045419/", "img_link": "https://img.7api-01.dp1.sej.co.jp/item-image/045419/D832C7C08CFCD5FD8CB443AC64CC1EB7.jpg" } }

このように仕方なく"onigiri"というキーを持たせず出力するようにしかできない状況です。
最初に載せた期待通りのJSONの通り"onigiri"のキーを一つにして、その値の配列にすべてのおにぎり商品データを入れたいです。

情報が不足しており、すみません。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

CHERRY

2021/03/22 21:59

どのような HTML に対して処理して、どのような結果になっているのでしょうか? その結果のどの部分が期待通りではないのでしょうか?
yosukemurakami

2021/03/23 00:08 編集

ご指摘頂き、ありがとうございます。 質問内容を更新しましたので、ご確認頂けますと幸いです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問