小鉢料理っていいですよね。 少量ではありますがいろんなものをたくさん食べられます。
私はお酒が好きで店に限らず家でもよく飲みます。
おつまみも自分で作るのですがどうしても焼き物、揚げ物メインであとは豆腐やチーズといった出来合いを出して終わってしまいます。
家での飲みライフを充実させるべく小鉢料理のレパートリーを増やしたいと思い、レシピをかき集めることにしました。
スクレイピングで収集
レシピといえば料理本を買うのもいいですが今の時代はインターネットに山ほど情報が転がっています。
そして私はITのエンジニア。
スクレイピングで収集しない手はありません。
※ウェブサイトをスクレイピングする前に、必ず利用規約を読みrobots.txtのガイドラインに従ってください。
scrapyライブラリ
私はpythonを使いますがスクレイピングといえばBeautifulSoupが有名です。
しかし今回はスクレイピングで検索すると何かと出てくるscrapyライブラリを使ってみたいと思います。
スクレイピングに限らずクローラーとしての機能もあるようです。
Scrapy | A Fast and Powerful Scraping and Web Crawling Framework
環境
(python3) masashi@PC-ubuntu:~$ uname -a ; lsb_release -a Linux PC-ubuntu 5.4.0-89-generic #100-Ubuntu SMP Fri Sep 24 14:50:10 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 20.04.3 LTS Release: 20.04 Codename: focal
(python3) masashi@PC-ubuntu:~$ python --version Python 3.8.10
インストール
いくつか方法はありますがpipでインストールします。
(python3) masashi@PC-ubuntu:~$ pip install scrapy
バージョン。
(python3) masashi@PC-ubuntu:~$ scrapy version Scrapy 2.5.1
収集するサイト
小鉢料理のレシピサイトも多数ありますが、今回はホットペッパーのメシ通というサイトを収集したいと思います。
このサイトのレシピは小鉢に限っていませんが、お手軽そうで記事の内容も面白かったので。
レシピ カテゴリーの記事一覧 - メシ通 | ホットペッパーグルメ
コード
取得対象
本サイトに限らずですが、レシピの一覧から詳細ページに飛ぶという作りになっています。
詳細は都度飛べばいいと思うのでとりあえずタイトルとリンクだけ収集したいと思います。
プロジェクト作成
最初にスクレイピング用のプロジェクトを作成します。
(python3) masashi@PC-ubuntu:~$ cd scrapy (python3) masashi@PC-ubuntu:~/scrapy$ scrapy startproject sidedish New Scrapy project 'sidedish', using template directory '/home/masashi/python3/lib/python3.8/site-packages/scrapy/templates/project', created in: /home/masashi/scrapy/sidedish You can start your first spider with: cd sidedish scrapy genspider example example.com
ツリー構成。
(python3) masashi@PC-ubuntu:~/scrapy$ cd sidedish (python3) masashi@PC-ubuntu:~/scrapy/sidedish$ tree . ├── scrapy.cfg └── sidedish ├── __init__.py ├── items.py ├── middlewares.py ├── pipelines.py ├── settings.py └── spiders └── __init__.py 2 directories, 7 files
スクレイピング用コード
チュートリアルに沿って作成。
スクレイピング用クラスを作成してコマンドラインから実行という流れです。
タグの抽出はcssを使うサンプルが多かったので私もそうしました。
BeautifulSoupではクラス名とかxpathをよく使っていたのですが、cssはツリーがわかりやすくていいですね。
import scrapy class SidedishSpider(scrapy.Spider): name = 'sidedish' start_urls = [ 'https://www.hotpepper.jp/mesitsu/archive/category/%E3%83%AC%E3%82%B7%E3%83%94' ] def parse(self, response): for dish in response.css('section div h1'): yield { 'text': dish.css('a::text').get(), 'link': dish.css('a').attrib['href'], }
実行
実行してみます。
(python3) masashi@PC-ubuntu:~/scrapy/sidedish$ scrapy crawl sidedish 2021-10-31 19:06:14 [scrapy.utils.log] INFO: Scrapy 2.5.1 started (bot: sidedish) (略) 2021-10-31 19:06:14 [scrapy.core.engine] INFO: Spider opened 2021-10-31 19:06:14 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min) 2021-10-31 19:06:14 [scrapy.extensions.telnet] INFO: Telnet console listening on 127.0.0.1:6023 2021-10-31 19:06:14 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.hotpepper.jp/robots.txt> (referer: None) 2021-10-31 19:06:14 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.hotpepper.jp/mesitsu/archive/category/%E3%83%AC%E3%82%B7%E3%83%94> (referer: None) 2021-10-31 19:06:15 [scrapy.core.scraper] DEBUG: Scraped from <200 https://www.hotpepper.jp/mesitsu/archive/category/%E3%83%AC%E3%82%B7%E3%83%94> {'text': '一皿でタンパク質約26g、糖質控えめオートミールのパラパラチャーハンの作り方。筋トレにハマった管理栄養士のボディメイクめし', 'link': 'https://www.hotpepper.jp/mesitsu/entry/kanakitajima/2021-00455'} 2021-10-31 19:06:15 [scrapy.core.scraper] DEBUG: Scraped from <200 https://www.hotpepper.jp/mesitsu/archive/category/%E3%83%AC%E3%82%B7%E3%83%94> {'text': '脂がのったサーモン刺身で「サーモングラタン」。骨なし、火の通りの心配なしで最高だった【魚屋三代目】', 'link': 'https://www.hotpepper.jp/mesitsu/entry/sakanaya-sandaime/2021-00454'} 2021-10-31 19:06:15 [scrapy.core.scraper] DEBUG: Scraped from <200 https://www.hotpepper.jp/mesitsu/archive/category/%E3%83%AC%E3%82%B7%E3%83%94> (略) 2021-11-01 23:29:49 [scrapy.core.engine] INFO: Spider closed (finished)
いいですね。
取得できてます。
クローリング用コード作成
所謂「次のページへ」があって一覧が分割されてますのでここのリンクを辿ってすべて取得できるようにします。
def perseに追加。下三行です。
「次のページへ」をどんどん辿って最後まで各ページの一覧を取得していきます。
def parse(self, response): for dish in response.css('section div h1'): yield { 'text': dish.css('a::text').get(), 'link': dish.css('a').attrib['href'], } next_page = response.css('.test-pager-next').attrib['href'] if next_page is not None: yield response.follow(next_page, callback=self.parse)
保存するため再実行
scrapyは保存のオプションがありますのでこれを使ってデータを保存します。
これもjsonでのサンプルが多かったのでそれに習います。
(python3) masashi@PC-ubuntu:~/scrapy/sidedish$ scrapy crawl sidedish -O sidedishes.json
出力は割愛しますが数十秒ほどで完了。
中身確認
中身を見てみます。
(python3) masashi@PC-ubuntu:~/scrapy/sidedish$ ls -l sidedishes.json -rw-rw-r-- 1 masashi masashi 645658 11月 1 23:29 sidedishes.json (python3) masashi@PC-ubuntu:~/scrapy/sidedish$ wc -l sidedishes.json 1963 sidedishes.json
1963レシピも収集できました。
(python3) masashi@PC-ubuntu:~/scrapy/sidedish$ head -n2 sidedishes.json [ {"text": "\u4e00\u76bf\u3067\u30bf\u30f3\u30d1\u30af\u8cea\u7d0426g\u3001\u7cd6\u8cea\u63a7\u3048\u3081\u30aa\u30fc\u30c8\u30df\u30fc\u30eb\u306e\u30d1\u30e9\u30d1\u30e9\u30c1\u30e3\u30fc\u30cf\u30f3\u306e\u4f5c\u308a\u65b9\u3002\u7b4b\u30c8\u30ec\u306b\u30cf\u30de\u3063\u305f\u7ba1\u7406\u6804\u990a\u58eb\u306e\u30dc\u30c7\u30a3\u30e1\u30a4\u30af\u3081\u3057", "link": "https://www.hotpepper.jp/mesitsu/entry/kanakitajima/2021-00455"},
しかし中身をみるとunicode形式で保存されちゃってます。
これを変換してもいいですが、どうせならそのまま保存したいです。
調べたらsetting.pyに文字コード編集できるところがありました。
encodingの設定を追加。
(python3) masashi@PC-ubuntu:~/scrapy/sidedish$ tail -n1 sidedish/settings.py FEED_EXPORT_ENCODING='utf-8'
再実行
何度も仕掛けて申し訳ないですが、再実行。
(python3) masashi@PC-ubuntu:~/scrapy/sidedish$ ls -l sidedishes.json -rw-rw-r-- 1 masashi masashi 406541 11月 1 23:37 sidedishes.json
ちょっとサイズが減りましたね。
(python3) masashi@PC-ubuntu:~/scrapy/sidedish$ head -n2 sidedishes.json [ {"text": "一皿でタンパク質約26g、糖質控えめオートミールのパラパラチャーハンの作り方。筋トレにハマった管理栄養士のボディメイクめし", "link": "https://www.hotpepper.jp/mesitsu/entry/kanakitajima/2021-00455"},
中身も日本語になってます。 OK!
ちょっとデータ検索
取得ができたということで運用方法はこれから考えますが、ちゃんと取れているか見てみます。
小鉢として私がよく食べる食材でgrepしてみます。
- 納豆
(python3) masashi@PC-ubuntu:~/scrapy/sidedish$ grep 豆腐 sidedishes.json | head -n3 {"text": " 青唐辛子で作る「白ラー油」は辛みが一味違う→四川料理のスゴい人のレシピを参考に白ラー油の麻婆豆腐も試してみた", "link": "https://www.hotpepper.jp/mesitsu/entry/blues_harp/2021-00608"}, {"text": "タンパク質もうま味も山盛りすぎてローテ入りさせたい「豆腐ステーキ」の作り方【筋肉料理人】", "link": "https://www.hotpepper.jp/mesitsu/entry/kinniku/2021-00453"}, {"text": "「豆腐を入れる」「なすは皮を先に焼く」など、定食屋さんの味に近づくなすみそ炒めレシピのコツ【筋肉料理人】", "link": "https://www.hotpepper.jp/mesitsu/entry/kinniku/2021-00449"},
- 豆腐
(python3) masashi@PC-ubuntu:~/scrapy/sidedish$ grep 納豆 sidedishes.json | head -n3 {"text": "納豆の練りからし使わない勢は「きゅうりのからし漬け」で消費を。15分くらいで食べごろです【筋肉料理人】", "link": "https://www.hotpepper.jp/mesitsu/entry/kinniku/2021-00357"}, {"text": "納豆を自作して「無限納豆」生活にチャレンジしてみた ", "link": "https://www.hotpepper.jp/mesitsu/entry/oishiisekai/2020-00335"}, {"text": "「納豆、豚ひき肉、春菊の手抜きフィデウア」という材料入れて火をかけるだけのお手軽パスタ料理【ツジメシの付箋レシピ】", "link": "https://www.hotpepper.jp/mesitsu/entry/tsujimeshi/2020-00011"},
- アボカド
(python3) masashi@PC-ubuntu:~/scrapy/sidedish$ grep アボカド sidedishes.json | head -n3 {"text": " 「アボカド」の食べ方って、もっと可能性があるんじゃないか【やけくそレシピ】", "link": "https://www.hotpepper.jp/mesitsu/entry/chimiwo/2021-00611"}, {"text": "サーモンもお肉もなしで、もれなくウマいアボカド丼ができるレシピ", "link": "https://www.hotpepper.jp/mesitsu/entry/yuuyuu/2021-00422"}, {"text": "電子レンジで完成「にんにく一味肉みそ」で豆腐とアボカドが画力あるつまみに化けた【筋肉料理人】", "link": "https://www.hotpepper.jp/mesitsu/entry/kinniku/2021-00416"},
すべてが小鉢料理ではないですが、レシピデータのサマリとしてはいい感じに収集できたと思います。
このデータをもう一加工すれば小鉢料理のレパートリーとして使えそうですね。
本来は作れてマスターですが、いきなり1000以上のレシピを収集できただけでも近づけているのではないでしょうか。
まとめ
scrapyを使ったクローリング&スクレイピングでした。
フレームワークという名がついているので小難しいお作法を覚えないといけないなと思っていたのですが、
思いの外構文も楽で意外と簡単に実行までできました。
クローリング&スクレイピング自体はBeautifulSoupでも出来るので、scrapyでは他にどんなことが出来るのかも試したいですね。
今回使っていないファイルもありますし、ソースも読めていないのでコマンドから作成クラスをどう呼び出しているかも調べようと思います。
また、スクレイピング単体ではなく過去のツールとかと合わせて使いこなしたいですね。
過去のこれからレシピを引っかけるとか。
HTMLスクレイピングで旬の食材を確認する - paloma blog
参考サイト
Scrapy 2.5 documentation — Scrapy 2.5.1 documentation
How to Crawl the Web with Scrapy | Babbling Fish
Scrapy for Beginners - A Complete How To Example Web Scraping Project - YouTube