Requests ライブラリでの日本語テキスト表示と文字化け対策

テキストの取得と文字化けの原因

Python の requests ライブラリは、HTTP リクエストの実行とレスポンス処理を簡潔に記述できることから広く利用されています。しかし、日本語や中国語などの複数バイト文字を含むウェブページを取得する際、r.text で正しく表示されず、文字化けが発生するケースがよくあります。

requests は内部で以下のことを自動的に処理します:

  • r.content:生のバイト列(bytes型)。
  • r.text:自動的文字エンコーディング推測に基づいたデコード済み文字列(str型)。

ただし、推測されたエンコーディングが適切でない場合、特に日本語/中国語コンテンツで nổiトラブルが起きます。典型的には、r.encoding'ISO-8859-1' に設定されることがあります。

安定したテキスト取得の方法

方法1:バイト列を直接デコード

».content» で取得したバイト列を明示的に指定エンコーディングでデコードする方法です。

import requests

url = 'http://music.baidu.com'
response = requests.get(url)
html_bytes = response.content
html_str = html_bytes.decode('utf-8', errors='ignore')  # エラー無視でデコード
print(html_str)

方法2:計算エンコーディングの明示設定

requests が推測したエンコーディングを強制的に utf-8 に上書きします。

import requests

url = 'http://music.baidu.com'
response = requests.get(url)
response.encoding = 'utf-8'
print(response.text)

※ レスポンスヘッダーやHTMLのに従って最適なエンコーディングを参照する方式(後述の「最終解法」)に比べて低精度な場合があります。

ファイルへの保存処理

Direct bytes save(バイナリモード)

import requests

res = requests.get("http://www.baidu.com")
with open("download.html", "wb") as f:
    f.write(res.content)

decode → text save

import requests

res = requests.get("http://www.baidu.com")
text = res.content.decode('utf-8', errors='ignore')
with open("decode.html", "w", encoding="utf-8") as f:
    f.write(text)

text 属性使用 + 明示エンコード

import requests

res = requests.get("http://www.baidu.com")
res.encoding = "utf-8"
with open("r-text.html", "w", encoding="utf-8") as f:
    f.write(res.text)

解析処理ケース:Requests + lxml 連携

import requests
from lxml import etree

url = "http://music.baidu.com"
response = requests.get(url)
response.encoding = "utf-8"
html = response.text

tree = etree.HTML(html)
title_nodes = tree.xpath('//title/text()')
if title_nodes:
    print(title_nodes[0])

※ 出力例:»百度音乐-听到极致«

最適な文字エンコーディング推定方式

より高確率で正しいエンコーディングを推定するには、レスポンスコンテンツ本体からcharset情報を抽出・利用します。

import requests
import re

url = "http://news.sina.com.cn/"
res = requests.get(url)

# レスポンス本体内にcharset宣言があれば採択
content_type = res.headers.get('Content-Type', '')
encoding = None
# meta charsetの検出
match = re.search(r'charset=([^\s;]+)', res.text[:1024], re.IGNORECASE)
if match:
    encoding = match.group(1)
else:
    # ヘッダーのcharsetがある場合
    match = re.search(r'charset=([^\s;]+)', content_type, re.IGNORECASE)
    if match:
        encoding = match.group(1)

# 推定に失敗すれば apparentEncoding を参照
if not encoding:
    encoding = res.apparent_encoding

decoded_text = res.content.decode(encoding or 'utf-8', errors='replace')

with open('sina_news.html', 'w', encoding='utf-8') as f:
    f.write(decoded_text)

※ この方式では、HTML内に明示されたcharset宣言やContent-Typeのcharsetパラメータ、およびBody内容から charset を推定し、解析・デコードするため、より正確なテキスト再現が期待できます。

タグ: requests encoding web-scraping UTF-8 Python

7月27日 23:24 投稿