最悪や…orz
まる一日ハマってしまったので、ちょっとまとめておきます。
出だしから分かるように原因はしょうもない事でした。
あ、わかりやすく言うと、上図のような状況でした。
やろうとしたこと
Laravel環境で、特定のWebサイトをクロールしてスクレイピングする処理を作ってた。
スクレイピングの内容は単純で、URIと、title、descriptionなど特定の要素やプロパティを抜く程度。
で、集めたデータをMySQL DBに格納すると。
ある程度はすんなりDBに格納できたんだけど、いくつかのページでDBエラーが発生した。
DBエラーの内容
General error: 1366 Incorrect string value: '\xE3\x82' for column ...
環境について詳しく
対象の環境は以下の通り。Laradock使ってます。
- Windows 10
- Docker Desktop for Windows(Hyper-V)
- Laravel 5.7.28
- PHP 7.3.23
- MySQL 5.7.31(charset:utf8)
ここで問題です
見る人が見たらすぐ分かるかも。
Q. なぜ上記のようなエラーになってしまったんでしょうか?
ヒント(確認したこと試したこと)
1. MySQLの文字コードを変更
とりあえずエラーメッセージでググると、MySQLのcharsetいじれ的な記事が多く出てきたので確認してみた。
mysqlでstatusを見ると、Server、Dbのcharctersetはutf8で、Client、Conn.はlatin1。(Laradockのデフォルト)
my.cnfを修正して、すべてのcharsetをutf8mb4に合わせてみた。
→ 結果:解決せず
2. 照合順序を変更
エラーが発生した対象DBのmigrationファイルに以下を追記して照合順序を「utf8_general_ci」に変更してみた。
$table->collation = 'utf8_general_ci';
→ 結果:解決せず
3. そもそものデータをもう少し調べる
実行されるSQLをクライアントツールで動かしてみたら、なぜか登録は出来ていたのでサーバの設定の問題かと思ってたんだけど、よく見ると最後の1文字が文字化けしてた…。
答え合わせ
はい、しゅ~りょ~!
ほんとしょうもない理由でしたが、今回の原因は↓でした。
DB挿入時の処理
// スクレイピング処理後
$title = $page_info->title;
$uri = $page_info->uri;
$content = trim(strip_tags($page_info->content));
$description = substr($content, 0, 120);
// DB格納処理
$this->insertDB($title, $uri, $description, $content);
諸悪の根源はソースでした。どちくしょう。
問題の箇所はこちら。
substr($content, 0, 120);
マルチバイトデータに対してsubstrで部分抽出していた。
最後の文字の文字コード途中で切られていたので、おかしくなってただけだった。
mb_substr($content, 0, 120, 'utf-8');
mb_substrに変更したあげく、文字コードまでつけてやった。
くそう…自分の愚かさがうらめしい…