2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

TechCommitAdvent Calendar 2021

Day 3

pythonで青空文庫にある夏目漱石の書籍をスクレイピングする

Posted at

はじめに

TechCommit Advent Calendar 2021 3日目を担当しますRyotaです!
よろしくお願いいたします。

pythonで機械学習を勉強するため、書籍「Pythonによるスクレイピング&機械学習 開発テクニック 」に沿って学習していました。
自然言語処理の章で学習用のデータを収集するため、青空文庫から夏目漱石などの作家の作品を一括ダウンロードする必要がありました。
一括ダウンロードサイトとして「http://keison.sakura.ne.jp/」
からダウンロードするようにと書かれていましたが、現在はそのサイトは無い様子。

そこで、自分で青空文庫から夏目漱石の書籍をダウンロードして展開するスクリプトを作成しましたので、参考になればと思い記載させていただきます。

注意事項

青空文庫のUIが変わると、正常にスクレイピングができなくなります。

環境

    windows 10
    Python 3.10.1
    bs4 0.0.1
    beautifulsoup4 4.10.0

前提知識

  • pythonの基本的知識
  • html/CSSの基本的知識

まずはコード全部

ちょっと冗長な部分もありますが大目に見てください

get_aozora.py
import urllib.request as req
import urllib
import re
from bs4 import BeautifulSoup
import time
import os
import zipfile


#青空文庫 夏目漱石(person148)リストURL
url = "https://www.aozora.gr.jp/index_pages/person148.html#sakuhin_list_1"

card_nums =[]

save_folder = ".//作品//夏目漱石//"

if not os.path.isdir(save_folder):
    os.makedirs(save_folder)

def get_card_num():
    print(url)
    try:
        res = req.urlopen(url)
    except urllib.error.HTTPError as e:
        print('raise HTTPError')
        print(e.reason)
    except urllib.error.URLError as e:
        print('raise URLError')
        print(e.reason)
    else:
        print(res.status)

    soup = BeautifulSoup(res, "html.parser")

    first_ol_element = soup.find('ol')

    for element in first_ol_element.find_all('li'): 
        sakuhin  = element.text
        #print(sakuhin)
        title_name = re.search(r'(.*)(()',sakuhin)
        #print(title_name.group(1))
        card_num =re.search(r'作品ID:([\d:]+)',sakuhin)
        #print(card_num.group(1))
        card_nums.append(card_num.group(1))

get_card_num()

file_names = []

for card_num in card_nums:
    print(card_num)
    card_url = "https://www.aozora.gr.jp/cards/000148/card" + str(card_num) + ".html"
    print(card_url)
    try:
        res = req.urlopen(card_url)
    except urllib.error.HTTPError as e:
        print('raise HTTPError')
        print(e.reason)
        continue
    except urllib.error.URLError as e:
        print('rase URLError')
        print(e.reason)
        continue
    else:
        print(res.status)

    soup = BeautifulSoup(res, "html.parser")

    download_table = soup.find('table', {'class':'download'})
    #print(download_table)
    download_path = download_table.select_one("tr:nth-of-type(2) td:nth-of-type(3)").get_text()
    #print(download_path)
    file_names.append(download_path)
    time.sleep(1)

save_folder = ".//作品//夏目漱石//"

download_folder = save_folder + "zip//"
if not os.path.isdir(download_folder):
    os.makedirs(download_folder)

for file_name in file_names:
    print(file_name)
    download_file_url = "https://www.aozora.gr.jp/cards/000148/files/" + file_name
    local = download_folder + file_name
    if not os.path.exists(local):
        print("{0}のZIPファイルをダウンロード".format(file_name))
        try:
            download_data = urllib.request.urlopen(download_file_url).read()
        except urllib.error.HTTPError as e:
            print('raise HTTPError')
            print(e.reason)
            continue
        except urllib.error.URLError as e:
            print('rase URLError')
            print(e.reason)
            continue
        else:
            print(res.status)
            res.close()

        with open(local, mode="wb") as f:
            f.write(download_data)

        time.sleep(1)
    else:
        print("{0}のファイルはダウンロード済みです。".format(file_name))


zip_files = os.listdir(download_folder)

unzip_folder = save_folder + "unzip//"
if not os.path.isdir(unzip_folder):
    os.makedirs(unzip_folder)

unzip_filenames = []

for zip_file in zip_files:
    if ".zip" in zip_file:
        print("{0}のファイルを解凍します。".format(zip_file))
        zip_file_path = download_folder + zip_file
        with zipfile.ZipFile(zip_file_path, 'r') as z:
            for info in z.infolist():
                info.filename = info.filename.encode('shift_jis').decode('utf-8')
                z.extract(info, path=unzip_folder)
                unzip_filenames.append(info.filename)

utf8_folder = save_folder + "utf8//"
if not os.path.isdir(utf8_folder):
    os.makedirs(utf8_folder)

for unzip_filename in unzip_filenames:
    if os.path.isfile(unzip_folder + unzip_filename):
        if not os.path.isfile(utf8_folder + unzip_filename):
            sr_file = open((unzip_folder + unzip_filename), 'r',encoding='shift_jis')
            dt_file = open((utf8_folder + unzip_filename), 'w',encoding='utf-8')
            for row in sr_file:
                dt_file.write(row)
            
            sr_file.close
            dt_file.close
            print("{0}ファイルの文字コードをUTF8に変換しました。".format(unzip_filename))
        else:
            print("{0}ファイルは既に文字コード変換されています。".format(unzip_filename))
    else:
        print("{0}ファイルが存在しません。".format(unzip_filename))

出力先

下記の場所に各ファイルが作成されます。
このコードを実行したディレクトリを基準としています。

  • 作品
    • 夏目漱石
      • unzip (ダウンロード後、解凍したファイル(SJIS))
      • utf8 (ダウンロード後、解凍し文字コードを変更したファイル(UTF8))
      • zip (ダウンロードしたファイル)

青空文庫の構成

青空文庫の構造は下記のようになっています。

作品ファイルはzipで圧縮されています。
文字コードはSJISになっています。

コードの構成

作品一覧ページから各作品ID(card_nums)を取得

#青空文庫 夏目漱石(person148)リストURL
url = "https://www.aozora.gr.jp/index_pages/person148.html#sakuhin_list_1"

def get_card_num():
    print(url)
    try:
        res = req.urlopen(url)
    except urllib.error.HTTPError as e:
        print('raise HTTPError')
        print(e.reason)
    except urllib.error.URLError as e:
        print('raise URLError')
        print(e.reason)
    else:
        print(res.status)

    soup = BeautifulSoup(res, "html.parser")

    first_ol_element = soup.find('ol')

    for element in first_ol_element.find_all('li'): 
        sakuhin  = element.text
        #print(sakuhin)
        title_name = re.search(r'(.*)(()',sakuhin)
        #print(title_name.group(1))
        card_num =re.search(r'作品ID:([\d:]+)',sakuhin)
        #print(card_num.group(1))
        card_nums.append(card_num.group(1))

get_card_num()

作品ID(card_nums)をもとに作品ダウンロードリンク(file_names)を取得

for card_num in card_nums:
    print(card_num)
    card_url = "https://www.aozora.gr.jp/cards/000148/card" + str(card_num) + ".html"
    print(card_url)
    try:
        res = req.urlopen(card_url)
    except urllib.error.HTTPError as e:
        print('raise HTTPError')
        print(e.reason)
        continue
    except urllib.error.URLError as e:
        print('rase URLError')
        print(e.reason)
        continue
    else:
        print(res.status)

    soup = BeautifulSoup(res, "html.parser")

    download_table = soup.find('table', {'class':'download'})
    #print(download_table)
    download_path = download_table.select_one("tr:nth-of-type(2) td:nth-of-type(3)").get_text()
    #print(download_path)
    file_names.append(download_path)
    time.sleep(1)

作品ダウンロードリンク(file_names)から作品をダウンロード

for file_name in file_names:
    print(file_name)
    download_file_url = "https://www.aozora.gr.jp/cards/000148/files/" + file_name
    local = download_folder + file_name
    if not os.path.exists(local):
        print("{0}のZIPファイルをダウンロード".format(file_name))
        try:
            download_data = urllib.request.urlopen(download_file_url).read()
        except urllib.error.HTTPError as e:
            print('raise HTTPError')
            print(e.reason)
            continue
        except urllib.error.URLError as e:
            print('rase URLError')
            print(e.reason)
            continue
        else:
            print(res.status)
            res.close()

        with open(local, mode="wb") as f:
            f.write(download_data)

        time.sleep(1)
    else:
        print("{0}のファイルはダウンロード済みです。".format(file_name))

ダウンロードしたzipファイルを解凍

for zip_file in zip_files:
    if ".zip" in zip_file:
        print("{0}のファイルを解凍します。".format(zip_file))
        zip_file_path = download_folder + zip_file
        with zipfile.ZipFile(zip_file_path, 'r') as z:
            for info in z.infolist():
                info.filename = info.filename.encode('shift_jis').decode('utf-8')
                z.extract(info, path=unzip_folder)
                unzip_filenames.append(info.filename)

解凍した作品ファイルの文字コードをUTF8に変換

for unzip_filename in unzip_filenames:
    if os.path.isfile(unzip_folder + unzip_filename):
        if not os.path.isfile(utf8_folder + unzip_filename):
            sr_file = open((unzip_folder + unzip_filename), 'r',encoding='shift_jis')
            dt_file = open((utf8_folder + unzip_filename), 'w',encoding='utf-8')
            for row in sr_file:
                dt_file.write(row)
            
            sr_file.close
            dt_file.close
            print("{0}ファイルの文字コードをUTF8に変換しました。".format(unzip_filename))
        else:
            print("{0}ファイルは既に文字コード変換されています。".format(unzip_filename))
    else:
        print("{0}ファイルが存在しません。".format(unzip_filename))

最後まで見てくださりありがとうございました。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?