第7章 ScrapyとMySQL

はじめに

ここではScrapyでスクレイピングした値をMySQLのテーブルに格納する方法についてまとめていきます。MongoDBのようなNoSQLデータベースが使われることも多いようですが、ここではMySQLを扱います。

プロジェクトの作成

対象にするサイトは、チュートリアル2で使った架空のオンライン書店のサイトです。チュートリアル2のコードをベースにMySQLに連携できるように書き換えます。ではプロジェクトを作成します。

$ scrapy startproject sample_books_mysql
$ cd sample_books_mysql
$ scrapy genspider books_spider_mysql books.toscrape.com

アイテムの定義(MySQL)

Items.pyを定義していきます。これまで使ってきませんでしたが、Itemを定義する必要があります。Itemはスクレイピングしたデータを格納しておくためのオブジェクトで、ここに格納して、MySQLに保存するためのパイプラインにデータを流します。

また、Itemを定義することで、タイポによるカラム名の間違いがあればエラーを返すようになります。指定の仕方は「名前=scrapy.Field()」で、タイトル、価格、URLを保存するようにします。

import scrapy

class BooksMysqlItem(scrapy.Item):
        title = scrapy.Field()
        price = scrapy.Field()
        detail_page_url = scrapy.Field()

クローラーの設計

Itemにあわせて、books_spider_mysql.pyの中の項目を削除しています。スクレイピングした情報はItemクラスのコンストラクタBooksMysqlItemで初期化した後に、キーを指定して渡します。その他はこれまでと変わりません。

パイプラインの設計

Itemに保存されているデータをMySQLにインサートするためのパイプラインをpipelines.pyに記述していきます。ここでは、pymysqlを使用します。書き方はいろんな方法があると思いますが、ここでは「データベースにコネクションを作る関数」「データベースに重複チェックをしてインサートする関数」「データベースのコネクションをクローズする関数」に分けています。

「データベースに重複チェックをしてインサートする関数」では、Itemのタイトルを取り出して、データベースを検索をかけ、データベースにない値であれば、インサートするという仕組みにしています。定期的にクローラーを動かす際に、この重複チェックがないと、同じレコードがインサートされていき、いざ分析となった際に非常に面倒です。

ちなみに、このサイトでは書籍のタイトルが重複しているため、この関数のチェックに引っかかり、999件しかインサートされません。本当は、ユニークなIDなどで確認するべきです。でなければ、今回のようにタイトルは同じでも、金額やURLが異なるものが取得できません。

The Star-Touched Queenが重複している書籍のタイトルです。

Title duplication

環境設定

パイプラインを有効にするために、settings.pyを有効にします。番号はデータベースへの格納なので、順序としては遅くても大丈夫なので、800としています。

テーブルの定義(MySQL)

ここまで準備ができたら、MySQLにデータベース、テーブルを作成します。itemの内容に沿ってテーブルを作成します。

クローラーを実行する

すべての準備が整ったので、クローラーを実行します。

MySQLの中身を確認しておきます。想定通り999件のデータが格納されていることがわかります。

MySQLとお寿司問題

ここでは、おまけ程度にMySQLの消える🍣問題をまとめておきます。例えば、スクレイピングした際に、🍣という絵文字が含まれている場合、MySQLのテーブル定義次第では🍣が消えてなくなります。いわゆる、「MySQL寿司消える問題」です。

🍣をインサートしてみます。MySQL8.0では、そもそも入らなくなってますね…MySQL5.6だと🍣絵文字以降が消えてしまいます。

この原因はCharsetのutf8です。MySQLのutf8は4バイト以上のU+1000以降の文字がありません。なので、不正な文字以降は認識されません。これに対応するためには、Charsetのutf8mb4に変更します。4バイトになるのは絵文字だけではなく、一部の漢字も含まれるので、日本語を扱うのであれば、utf8mb4が無難な選択肢といえるかと思います。

その他にも、🍣と🍺が区別できない、「MySQL寿司ビール問題」があります。照合順序がutf8_general_ciの場合、区別ができなくなります。これはU+1000以降の4バイト文字が「�」として扱われるためです。これを避けるためには、utf8mb4_binを使う必要があります。

最終更新

役に立ちましたか?