質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.93%

PyQueryでのスクレイピングで特定のページだけ文字化け

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,235

cloudspider

score 90

練習でPyQueryで大学のシラバスのデータを取得しようとしているのですが、

http://www0.osakafu-u.ac.jp/syllabus/list02.aspx?CD1=3&CD2=601
ここのページのデータだと文字化けせずに取得できるのですが、
上のページから飛んだリンクの先のページ、無数にありますが例えば
http://www0.osakafu-u.ac.jp/syllabus/detail.aspx?CD=103
ここのページをスクレイピングしようとすると日本語が文字化けします。

どちらのページもutf-8が使われていることは確認済みなのですが、
以下のコードのどこに問題があるのでしょうか。

ご存じの方がいれば回答よろしくお願いします。

import chardet
from pyquery import PyQuery as pq

if __name__ == '__main__' :
    # Htmlファイル取得
    opener = urllib.request.build_opener()
    request = urllib.request.Request('http://www0.osakafu-u.ac.jp/syllabus/list02.aspx?CD1=3&CD2=601')
    html = opener.open(request).read()

    for li_node in pq(html).find('li'):
        # liノード取得
        li_nodes = pq(li_node)('li')
        # url
        url = li_nodes.find('a').eq(0).attr('href')
        URL = 'http://www0.osakafu-u.ac.jp/syllabus/'+ str(url)
        # title
        title = li_nodes.eq(0).text()

        opener2 = urllib.request.build_opener()
        request2 = urllib.request.Request(URL)
        html2 = opener.open(request2).read()

        for li_node2 in pq(html2).find('div#div_Yu'):

            li_node2 = pq(li_node2)('div#div_Yu').find('tr').eq(1)

            code     = li_node2.find('span#lbl_ZYUGYO_CD').text()
            week     = li_node2.find('span#lbl_WJ_NM').text()
            room     = li_node2.find('span#lbl_ROOM_NM').text()
            teacher  = li_node2.find('span#lbl_KYOIN_NM').text()

            # 表示
            if url and title and code and week and room and teacher:
                print('[\"'+code+'\",\"'+week+'\",\"'+title+'\",\"'+teacher+'\",\"'+room+'\",\"'+URL+'\"],')

コード

それと、PyQueryの使い方の載った本とかを探してみても見当たらなかったんですが、どこか使い方がわかりやすくまとまっているページとかご存知でないでしょうか。
良ければそちらもお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

文字化けしないページでは、 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> と明確にソース上で UTF-8 であることが示されているのに対し、文字化けするページではそれが無いため、 PyQuery (正確には、その中で使われている lxml) が文字コードの検出に失敗しているせいだと思われます。コードの問題というわけではないでしょう。

UTF-8 であることがわかっているのであれば、 pq(html2.decode('utf-8')) としてデコードしてから渡してやる (とか、無理やりですが pq(b'<meta charset="utf-8" />' + html2) のように文字コードの宣言を追加してやるとかする) と動作するのではないでしょうか。

あるいは、もう少しお行儀よくやるのであれば、ご提示のコードで import している chardet に検出させてから decode() するとか、 Web サーバが返してきたContent-Type ヘッダの値を参考にするとか、そういった実装を検討するとよいように思います。いずれにせよ、先に decode() して渡した方が無難ということになります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/04/09 01:49

    できました!!!!
    pq(html2.decode('utf-8')) の方では、
    「'PyQuery' object has no attribute 'decode'」とエラーを吐かれたのですが、
    pq(b'<meta charset="utf-8" />' + html2)の方ではみごと、文字化けせずに実行されました!!
    確かに言われてみれば無理矢理な方法ですが、こういうアイディアもあるんですねww
    ありがとうございました。2日間くらいずっと解決できずに大変だったので感謝感激です。

    キャンセル

  • 2017/04/09 03:40

    動いたようで良かったです。
    `'PyQuery' object has no attribute 'decode'` エラーの件ですが、もしかすると、 `pq(html2.decode('utf-8'))` ではなく `pq(html2).decode('utf-8')` になっておりませんでしたか?
    一応は `decode()` を使う方が、よりちゃんとした書き方ではあるので、余裕があれば確認してみてください。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.93%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る