PythonでWebスクレイピングを行っている際に、取得した日本語が「ãこんにちは」のように文字化けしてしまい、困った経験はないでしょうか。
BeautifulSoupを利用してHTMLを解析しようとしたものの、出力結果が正しく表示されないという問題は多くのエンジニアが直面する課題です。
この記事では、文字化けが発生する根本的なメカニズムから、2026年現在でも通用する実用的な解消方法までを詳しく紹介します。
初心者の方でも迷わずに実装できるよう、具体的なコード例を交えてステップバイステップで説明していきます。
なぜBeautifulSoupで文字化けが発生するのか
文字化けが発生する最大の理由は、Webサイトが使用している文字コードと、プログラムが読み込む際の文字コードが一致していないことにあります。
Webページは通常、UTF-8やShift_JIS、EUC-JPといった特定のエンコード方式で作成されています。
BeautifulSoupは内部で「UnicodeDammit」というライブラリを使用して、自動的に文字コードを判定しようと試みます。
しかし、HTML内に適切なmetaタグが存在しなかったり、HTTPヘッダーの情報の精度が低かったりする場合、この自動判定に失敗することがあります。
特に日本語のサイトでは、古いシステムで構築されたページが混在しており、誤ったエンコードとして処理されてしまうケースが少なくありません。
また、スクレイピングに併用されるrequestsライブラリの仕様が原因で文字化けが引き起こされることもあります。
requestsライブラリを利用する際の注意点
BeautifulSoupとセットで使われることが多いrequestsライブラリには、独自のエンコード判定ロジックが存在します。
requestsは、HTTPヘッダーの「Content-Type」を見てエンコードを推測しますが、これが常に正しいとは限りません。
例えば、ヘッダーに文字コード指定がない場合、requestsは慣習的に「ISO-8859-1」という欧米向けのエンコードを選択してしまいます。
この状態でresponse.textを参照すると、日本語が壊れた状態でBeautifulSoupに渡されることになります。
これを防ぐためには、バイナリデータであるresponse.contentをBeautifulSoupに渡すのが最も確実な方法です。
文字化けを解消する正しいエンコード指定方法
文字化けを防ぐための具体的な修正パターンをいくつか見ていきましょう。
1. response.contentをそのまま渡す方法
最も推奨されるのは、requestsで取得した生データをBeautifulSoupに直接解析させる方法です。
これにより、requestsによる誤ったテキスト変換を回避し、BeautifulSoup側の自動判定機能を最大限に活かすことができます。
import requests
from bs4 import BeautifulSoup
url = "https://example.com/japanese-page"
response = requests.get(url)
# response.textではなくresponse.contentを使用する
soup = BeautifulSoup(response.content, "html.parser")
print(soup.title.text)
2. response.encodingを明示的に指定する方法
対象のサイトがUTF-8ではない(例えばShift_JISなど)ことがあらかじめ分かっている場合は、明示的に指定を行います。
requestsのencoding属性を上書きすることで、textプロパティの中身を正しく変換させることができます。
import requests
from bs4 import BeautifulSoup
url = "https://example.com/sjis-page"
response = requests.get(url)
# サーバーの判定を無視してShift_JISを指定する
response.encoding = "shift_jis"
soup = BeautifulSoup(response.text, "html.parser")
print(soup.h1.text)
3. BeautifulSoupのfrom_encoding引数を利用する方法
BeautifulSoupのコンストラクタに対して、直接エンコードを教えることも可能です。
これは、取得したバイト列をどのような形式で解釈すべきか、ライブラリに対して強制的な指示を与える役割を果たします。
import requests
from bs4 import BeautifulSoup
response = requests.get("https://example.com/page")
# constructorで直接エンコードを指定する
soup = BeautifulSoup(response.content, "html.parser", from_encoding="utf-8")
print(soup.p.text)
自動判定の精度を高めるライブラリの活用
サイトによってエンコードがバラバラな場合、手動で一つずつ指定するのは効率的ではありません。
そのような場合は、charset-normalizerやcchardetといった高性能な文字コード判定ライブラリを導入しましょう。
2026年現在のモダンな環境では、requestsの内部でも利用されているcharset-normalizerが主流となっています。
以下のコードは、コンテンツから自動的にエンコードを推測し、適切に変換する例です。
import requests
from bs4 import BeautifulSoup
import charset_normalizer
url = "https://example.com/unknown-encoding"
response = requests.get(url)
# 取得したバイナリからエンコードを推測
results = charset_normalizer.from_bytes(response.content)
detected_encoding = results.best().encoding
# 判定されたエンコードでデコード
soup = BeautifulSoup(response.content, "html.parser", from_encoding=detected_encoding)
print(f"Detected: {detected_encoding}")
print(soup.body.text[:50])
Detected: shift_jis
サンプルサイトのトップページへようこそ。ここでは...
主要な文字コードの種類と特徴
日本のWebサイトで主に使用される文字コードの特徴を理解しておくと、トラブルシューティングがスムーズになります。
| 文字コード | 特徴 | 主な利用シーン |
|---|---|---|
| UTF-8 | 世界標準の文字コード | 現在のWebサイトの主流。迷ったらこれ。 |
| Shift_JIS (cp932) | Windows環境で普及 | 日本の古いWebサイトや行政機関のページ。 |
| EUC-JP | UNIX系で普及 | 非常に古い日本の学術系サイトなど。 |
特にShift_JISの場合、Windows固有の拡張文字が含まれることがあるため、プログラム上ではcp932と指定するとエラーを防ぎやすくなります。
特定のタグで文字化けが発生する場合の対処
ページ全体ではなく、特定の箇所だけが文字化けして見える場合、それは数値文字参照や実体参照である可能性があります。
例えば「こん」のように表示されるケースです。
BeautifulSoupは標準でこれらの参照をデコードしてくれますが、パーサーの種類(html.parser, lxml, html5lib)によって挙動が微妙に異なることがあります。
最新の環境では、より堅牢な解析が可能なlxmlパーサーの使用を検討してください。
# lxmlをインストールしている場合
soup = BeautifulSoup(response.content, "lxml")
lxmlは高速であり、多少壊れたHTML構造であっても適切に補完して解析してくれる強力なツールです。
まとめ
PythonのBeautifulSoupで発生する文字化けは、データの入り口であるrequestsでの扱いと、BeautifulSoupへの渡し方を整理することで確実に解消できます。
まずはresponse.textではなくresponse.contentをBeautifulSoupに渡すという基本を徹底しましょう。
それでも解決しない場合は、サイトのmetaタグを確認し、from_encoding引数で明示的に文字コードを指定してください。
複数のサイトを横断的にスクレイピングする際は、自動判定ライブラリを組み合わせることで、メンテナンスコストを大幅に下げることが可能です。
適切なエンコード設定をマスターして、ストレスのないデータ収集を実現しましょう。
