GMの抵抗ワショーイ

GMの抵抗ワショーイ

2022.10.12
XML
カテゴリ: サーバ
pythonでwebスクレイピングをするときに以下のようなコードを書くことがあるかと思います。
今回はこのコードを書いた時にハマった話を紹介。


response = requests.get(url, headers=headers_dic)
res_bs = response.content
soup = BeautifulSoup(res_bs, 'html.parser', from_encoding="shift-jis")

※headersには適切な仮のヘッダ内容が指定されているものとします。
※対象のwebページはshift-jisなので、文字コードも想定通りの挙動とします。
(他PCにてその他のプログラムは動作確認済み)
※windows10, 11環境で検証

beautifulsoup4==4.10.0
bs4==0.0.1
chardet==3.0.4
requests==2.26.0




ぱっとみ、普通にwebページからソース拾ってきて内容をbs4(BeautifulSoup)で拾ってきそうに見えるコードですが…

実際には上記実装をするとかなりの環境依存なコードになります


よくある解決法として、 apparent_encodingを指定する というものがあります。


response.encoding = response.apparent_encoding










import requests

url = 'https://example.com'
response = requests.get(url)
response.encoding = 'utf-8' # 強制的にUTF-8を使用



そこで、こう直してみるも文字化けする状況は改善せず。



response = requests.get(url, headers=headers_dic)
response.encoding = 'shift-jis' # 強制的に文字コードを指定
res_bs = response.content
soup = BeautifulSoup(res_bs, 'html.parser', from_encoding="shift-jis")



文字コードに何が指定されているんだこれ、と思って調べてみたところ、
response.encodingとresponse.apparent_encoding にGB18030が指定されていたのであった…。

初めて聞く文字コード、どこのだこれ?と思ってググってみた。


GB 18030は、中華人民共和国(中国)が制定した文字コード(文字セット)の国家規格である。

WikiPedia
・・・???




GB 18030は従来の文字コードと互換性を維持したUnicode伝送形式(Unicode Transformation Format、すなわちすべてのUnicode符号位置を符号化する文字符号化方式)であるとみなせる。言いかえると、GB 18030はUTF-8(ASCIIと互換性を維持している)の中国版である。


ボクノパソコン、ドコシュッシンナノ


とはいえapparent_encoding は@propertyよろしくset不可なフィールド(=こいつに変数を代入することはできない)
直接文字コードを指定しても文字化けするのはなぜ…?


















原因はここにありました。



response = requests.get(url, headers=headers_dic)
response.encoding = 'shift-jis' # 強制的に文字コードを指定
res_bs = response.content
soup = BeautifulSoup(res_bs, 'html.parser', from_encoding="shift-jis")



ここを res_bs = response.text にすると直ります。


どちらにせよBeaultifulSoupのfrom_encoding="shift-jis"でorigin_encodingをSJISに上書きするかのように見えますが、実際には全然異なった。

BeautifulSoupがcontentに対して最初に参照する文字コードはapparent_encodingであったのだ…
※4.10現在

つまり、
(1) requestsでsjisのエンコードでソース取得
(2)PC側のデフォルドエンコードをshift-jisで強制的に変更

(4)Bs4はcontentを指定されるとapparent_encodingを参照してコンテンツをデコードをし、その後にfrom_encodingを実施

というステップを踏んでいることがわかった。
bs4のorigin_encodingは変更不可っぽそうなので今回はcontentを取得していたところを諦めてtextで取得するように改修して無事文字コード問題を解消した。

なので、requests->bs4にデータを渡すときはcontentでなくtextで渡した方が文字コード問題を起こさずできますよ、って話でした。




ちなみに上記コードを実行するとbs4の部分でwarningが出ます。
文字コード指定しすぎだよ、と。
確かにこの方法だとfrom_encodingが不要になるのでコードから消して解決。

これでwarning出すことなく無事動きました。


response = requests.get(url, headers=headers_dic)
response.encoding = 'shift-jis' # 強制的に文字コードを指定
res_bs = response.text
soup = BeautifulSoup(res_bs, 'html.parser')



最近、chatGPTにきいても原因わからずな話はこうやって記事にしておくのアリかなと思ってる。
(chatGPT使ってトラブルシュートすると一定の会話量超えるとゲームのNPCみたいに会話がループし始めて永遠と問題解決しないことがあるので…。そういう場合は視点変えて聞き方変えるのが有効策だったりする。)





お気に入りの記事を「いいね!」で応援しよう

Last updated  2023.03.12 10:02:15
コメント(0) | コメントを書く


【毎日開催】
15記事にいいね!で1ポイント
10秒滞在
いいね! -- / --
おめでとうございます!
ミッションを達成しました。
※「ポイントを獲得する」ボタンを押すと広告が表示されます。
x

© Rakuten Group, Inc.
Design a Mobile Website
スマートフォン版を閲覧 | PC版を閲覧
Share by: