本ページはプロモーションが含まれています。

スクレイピングで集めたCSVや画像が毎月10GBずつ増えていくと、最初は軽かったcron処理も、いずれ容量の上限で止まります。レンタルサーバーとVPS、自宅PCのどれに置けば長く動くのかは、料金表を眺めるだけでは判断しづらいものです。この記事では、エックスサーバー(スタンダード)・ConoHa VPS・さくらのVPSを例に、スクレイピングの保存容量がどこで詰まるかを保持期間から逆算します。あわせて、増え方を抑える保存コードと、容量が要るときに1つ選ぶならどれかを示します。

月10GBずつ増えるcron処理の必要容量を保持期間から逆算する

環境を比べる前に、「何GBあれば足りるのか」を先に決めます。月10GBずつ増えるなら、必要な容量は保持期間(何か月分を手元に残すか)でほぼ決まります。OSやログ、一時ファイルの分も見込んで、3割程度の余裕を足しておくと安全です。

保持期間

データ量(月10GB×月数)

余裕込みの目安(×1.3)

3か月

30GB

約40GB

6か月

60GB

約80GB

12か月

120GB

約160GB

無期限

毎年120GBずつ増え続ける

どの環境でも、いずれ上限に届く

「無期限」の行が示すとおり、古いデータを外へ逃がす仕組みを作らない限り、どの環境を選んでもいつかは止まります。月10GBという数字も、取得先のページ数や画像サイズが変われば変わります。そこで私は、想定値で決め打ちせず、実際の増え方を毎日記録してから環境を選ぶようにしています。

実行環境

  • Python 3.8以降を想定しています。使っているのは pathlib・f文字列・date.fromisoformat で、いずれも3.7以前から入っている機能です。python3 --version で確認してください。
  • 計測スクリプトは標準ライブラリだけで動くため、pip install は不要です。共有サーバーのように、パッケージを自由に入れにくい環境でもそのまま置けます。

増え方と残り日数を毎日記録するスクリプト

import csv
import shutil
import sys
from datetime import date
from pathlib import Path

DATA_DIR = Path("/home/username/scraping/data")
LOG_FILE = Path("/home/username/scraping/logs/storage_log.csv")
QUOTA_GB = None   # 共有サーバーでは「プラン容量 − サイト・メール分」を入れる
WARN_DAYS = 30    # 残り日数がこれを切ったら警告
GB = 1024 ** 3


def dir_stats(root):
    total = 0
    count = 0
    for p in root.rglob("*"):
        if p.is_file():
            total += p.stat().st_size
            count += 1
    return total / GB, count


def free_gb(data_gb):
    if QUOTA_GB is not None:
        return QUOTA_GB - data_gb
    return shutil.disk_usage(DATA_DIR).free / GB


def append_log(row):
    LOG_FILE.parent.mkdir(parents=True, exist_ok=True)
    is_new = not LOG_FILE.exists()
    with LOG_FILE.open("a", newline="", encoding="utf-8") as f:
        writer = csv.writer(f)
        if is_new:
            writer.writerow(["date", "data_gb", "files", "free_gb"])
        writer.writerow(row)


def days_left(free):
    with LOG_FILE.open(encoding="utf-8") as f:
        rows = list(csv.DictReader(f))[-31:]
    if len(rows) < 2:
        return None
    # 月次アーカイブで減った日は無視し、増えた分だけを足す
    growth = 0.0
    for prev, cur in zip(rows, rows[1:]):
        growth += max(0.0, float(cur["data_gb"]) - float(prev["data_gb"]))
    days = (date.fromisoformat(rows[-1]["date"]) - date.fromisoformat(rows[0]["date"])).days
    if days <= 0 or growth <= 0:
        return None
    return free / (growth / days)


def main():
    data_gb, files = dir_stats(DATA_DIR)
    free = free_gb(data_gb)
    append_log([date.today().isoformat(), f"{data_gb:.2f}", files, f"{free:.2f}"])
    left = days_left(free)
    print(f"data={data_gb:.2f}GB files={files} free={free:.2f}GB days_left={left}")
    if left is not None and left < WARN_DAYS:
        print(f"WARNING: あと約{left:.0f}日で容量が尽きる見込み", file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    main()

ブロックごとに何をしているかを説明します。

  • 先頭の定数:保存先、記録用CSV、警告のしきい値をまとめています。QUOTA_GB は共有サーバー用の設定です。共有サーバーで shutil.disk_usage を呼ぶと、契約上の上限(クォータ)ではなくサーバー全体のディスクの空きが返ることがあります。そのため、管理画面で確認したプラン容量から、サイトとメールの使用分を引いた値を手で入れます。
  • dir_stats:保存先の配下を rglob("*") ですべてたどり、合計サイズ(GB)とファイル数を数えます。ファイル数も記録するのは、共有サーバーでは容量より先にファイル数の上限で詰まることがあるためです。
  • append_log:1日1行をCSVに追記します。ファイルが無ければ見出し行から書き始めます。
  • days_left:直近31行(約1か月分)から、1日あたりの増加量を求めます。後で紹介する月次アーカイブで元データを消した日は値が減るため、増えた日の差分だけを合計しています。そのうえで、空き容量を1日あたりの増加量で割って残り日数を出します。
  • main:記録と表示を行います。残りが30日を切ったら終了コード1で抜けるので、cronのログや監視の仕組みで異常として拾えます。

VPSや自宅機では、OS側の数値も合わせて確認できます。df -h でディスクの空き、df -i でinode(ファイル1つごとに消費する管理領域)の残りが見られます。共有サーバーでは、これらの値はサーバーの管理画面で確認します。

エックスサーバー(スタンダード)・ConoHa VPS・さくらのVPS・自宅PCが詰まる場所

2026年11月時点の各社公式料金ページの数値は、契約期間やキャンペーンによって表示が変わります。誤った金額を載せないために、この記事では金額と容量を転記しません。代わりに、公式ページで何を確認し、どう比べるかを示します。

公式ページで確認する項目

確認項目

エックスサーバー(スタンダード)

ConoHa VPS/さくらのVPS

自宅PC・自宅サーバー

ディスク容量

プランの仕様ページ

プランごとの仕様表

手持ちのディスク次第

容量を後から増やす方法と費用

上位プランへの変更が可能か

追加ストレージの有無と月額、プラン変更の可否

ディスクの購入費(一度きり)

ファイル数(inode)の上限

仕様ページや管理画面に記載があるか

自分でフォーマットしたファイルシステム次第

同左

cronの可否と実行条件

管理画面のcron設定、実行時間の制限の有無

root権限で自由に設定できる

自由に設定できる

利用規約

サイト公開以外の目的で大容量を保存してよいか

禁止事項(大量アクセスなど)

回線契約の規約

月額の見え方

契約期間による差

時間課金と長期割引の違い

電気代

エックスサーバー(スタンダード):容量より先にファイル数と規約で詰まりやすい

  • cronは管理画面から設定できるため、Pythonスクリプトを毎日動かすこと自体は可能です。root権限(サーバー全体を管理する権限)は無いので、OSのパッケージは入れられません。
  • 共有サーバーはWebサイトを公開するための容量として提供されています。スクレイピングで得たデータを溜め続けるのは、本来の用途から外れます。上限に届くと書き込みが失敗し、同じアカウントで公開しているサイトやメールにも影響が出ます。
  • 画像を1枚ずつ保存すると、月10GBでもファイル数が膨らみます。ファイル数の上限がある場合は、容量より先にそちらで止まります。

ConoHa VPS・さくらのVPS:容量は自分で管理できるが、増やすたびに月額が上がる

  • root権限があり、cronもsystemd timer(Linux標準の定期実行の仕組み)も自由に使えます。ファイル数の上限も、自分で作ったファイルシステムの範囲で決まります。
  • 詰まるのは費用です。容量を増やすには、上位プランへ変更するか追加ストレージを契約することになり、どちらも月額が積み上がります。保持期間を決めずに溜め続けると、毎月の支払いが増え続けます。
  • ディスクが満杯になると、データの書き込みだけでなく、OSのログ出力やパッケージ更新まで失敗します。データを追加ストレージなど別のディスクに置けば、満杯になってもOS側は止まりません。

自宅PC・自宅サーバー:容量は安いが、停電と再起動で止まる

  • 大容量のディスクを一度買えば月額はかからないため、「無期限に溜める」用途には最も向いています。
  • 止まる原因は、停電、OS更新後の再起動、スリープ、ディスク故障です。再起動後に処理が自動で再開するかどうかは、自分で設定しておく必要があります。
  • 自宅機での定期実行の組み方と、VPSへ移す判断の目安は 自宅サーバーsystemd timer運用とVPS移行26年11月 にまとめています。

以前は1フォルダに全保存、今は日付分割・取得済みスキップ・月次tarで書く

どの環境を選ぶ場合でも、保存の書き方を変えるだけで、容量・ファイル数・取得先への負荷をまとめて抑えられます。

以前の書き方

import os
import requests

for url in image_urls:
    r = requests.get(url)
    name = url.split("/")[-1]
    with open(os.path.join("data", name), "wb") as f:
        f.write(r.content)

この書き方には、次の問題があります。

  • 毎日すべての画像を取り直すため、取得先に同じリクエストを繰り返し送ってしまう。
  • ファイル名が同じ別画像で上書きが起きる。
  • 1つのフォルダにファイルが集中し、一覧の表示やバックアップが遅くなる。
  • "data" が相対パスなので、cronから実行するとカレントディレクトリ(作業中のフォルダ)が変わり、意図しない場所に保存される。
  • リクエストの間隔を空けていない。

今の書き方:日付ディレクトリと取得済みURLの記録

外部パッケージとして requests を使います。pip install requests で入れてください。特定のバージョンに依存する機能は使っていないので、バージョンの指定は不要です。入っているバージョンは pip show requests で確認できます。

import hashlib
import time
from datetime import date
from pathlib import Path
from urllib.parse import urlparse

import requests

DATA_DIR = Path("/home/username/scraping/data")
INDEX_FILE = DATA_DIR / "downloaded_urls.txt"
INTERVAL_SEC = 5
HEADERS = {"User-Agent": "example-collector/1.0 (+https://example.com/contact)"}


def load_index():
    if INDEX_FILE.exists():
        return set(INDEX_FILE.read_text(encoding="utf-8").split())
    return set()


def save_images(urls):
    seen = load_index()
    day_dir = DATA_DIR / "images" / date.today().strftime("%Y/%m/%d")
    day_dir.mkdir(parents=True, exist_ok=True)
    with requests.Session() as session, INDEX_FILE.open("a", encoding="utf-8") as index:
        session.headers.update(HEADERS)
        for url in urls:
            if url in seen:
                continue  # 取得済みのURLにはリクエストを送らない
            r = session.get(url, timeout=30)
            r.raise_for_status()
            suffix = Path(urlparse(url).path).suffix or ".bin"
            name = hashlib.sha256(url.encode()).hexdigest()[:16] + suffix
            (day_dir / name).write_bytes(r.content)
            index.write(url + "\n")
            seen.add(url)
            time.sleep(INTERVAL_SEC)
  • 定数:保存先を絶対パスで指定し、cronから実行しても保存場所がずれないようにしています。HEADERS には、取得先が問い合わせ先を確認できるよう、自分の連絡先URLを含めたUser-Agent(アクセス元を名乗る文字列)を入れます。
  • load_index:これまでに取得したURLの一覧をテキストファイルから読み込みます。
  • save_images の前半:images/2026/11/02 のような日付別のフォルダを作ります。1フォルダあたりのファイル数が抑えられ、月単位でのまとめ処理もしやすくなります。
  • ループ内:取得済みのURLは、リクエストを送る前にスキップします。新しいURLだけを取得し、ファイル名はURLのハッシュ値(URLから計算した固定長の文字列)にするので、同じ名前の別画像による上書きが起きません。保存したらURLを一覧に追記し、INTERVAL_SEC 秒待ってから次へ進みます。

今の書き方:前月分を月初にtarでまとめる

import shutil
import tarfile
from datetime import date, timedelta
from pathlib import Path

DATA_DIR = Path("/home/username/scraping/data")
ARCHIVE_DIR = Path("/home/username/scraping/archive")


def archive_last_month(kind, compress):
    last_month = date.today().replace(day=1) - timedelta(days=1)
    src = DATA_DIR / kind / last_month.strftime("%Y/%m")
    if not src.exists():
        return None
    ARCHIVE_DIR.mkdir(parents=True, exist_ok=True)
    mode = "w:gz" if compress else "w"
    ext = ".tar.gz" if compress else ".tar"
    dest = ARCHIVE_DIR / f"{kind}_{last_month:%Y-%m}{ext}"
    with tarfile.open(dest, mode) as tar:
        tar.add(src, arcname=src.relative_to(DATA_DIR))
    with tarfile.open(dest) as tar:
        tar.getmembers()  # 読めることを確認してから元を消す
    shutil.rmtree(src)
    return dest


if __name__ == "__main__":
    archive_last_month("csv", compress=True)      # CSVはテキストなのでgzipで縮む
    archive_last_month("images", compress=False)  # JPEG・PNGは圧縮済みなのでまとめるだけ
  • 前月の求め方:今月1日の前日が前月の末日になるので、そこから YYYY/MM のフォルダを特定します。
  • まとめ方:CSVはgzip付きのtarにして容量を減らします。画像はもともと圧縮されているため、gzipをかけてもほとんど縮みません。圧縮なしのtarで1ファイルにまとめ、ファイル数だけを減らします。
  • 削除の前に読み込み確認:作ったアーカイブが開けることを確かめてから、元のフォルダを消します。

画像のtarは同じディスク上にある限り、容量そのものは減りません。容量を空けるには、アーカイブを手元のPCなど別の場所へ移してからサーバー側を消します。たとえば手元から rsync -av username@myserver:/home/username/scraping/archive/ /home/username/backup/ で取得し、中身を確認してからサーバー上のアーカイブを削除します。

取得先サイトへの配慮:robots.txt・利用規約・アクセス間隔

容量の心配をする前に、そもそも毎日取得してよいデータかどうかを確認します。月10GB増えるということは、それだけの量を相手のサーバーから受け取っているということです。

  • robots.txt:サイトが巡回プログラムに向けて公開しているアクセスのルールです。標準ライブラリの urllib.robotparser で、対象のURLを取得してよいか確認できます。
  • 利用規約:自動取得の禁止、取得したデータの再配布や保存の禁止が書かれていないか確認します。画像には著作権があるため、保存と利用の範囲にも注意が必要です。
  • アクセス間隔:1件ごとに数秒以上空けます。上のコードの INTERVAL_SEC は、短くしない方向で調整してください。並列に取得して時間を縮めることはしません。
  • 取り直さない:取得済みのURLをスキップする仕組みは、自分の容量を節約すると同時に、相手への負荷も減らします。
  • 制限に従う:ログインが必要なページ、アクセス制限、ブロックを回避する目的で書き方を変えることはしません。エラーが続く場合は取得を止め、規約や提供されているAPIの有無を確認します。
from urllib.robotparser import RobotFileParser

rp = RobotFileParser("https://example.com/robots.txt")
rp.read()
if not rp.can_fetch("example-collector/1.0", "https://example.com/images/"):
    raise SystemExit("robots.txtで許可されていないため取得しない")

取得処理の最初に、robots.txtを読み込んで、対象のパスが許可されているかを確認します。許可されていなければ、何も取得せずに終了します。

関連する選択肢

毎日決まった時刻にスクリプトを動かすだけなら、共有のレンタルサーバーでも足ります。cronが使えるプランを選べば、自分でOSを管理する必要はありません。

レンタルサーバー エックスサーバー

※ 広告を含みます(A8.net)。リンク経由でお申し込みがあった場合、手数料を受け取ることがあります。

「容量が要る」ならVPSを1つ選ぶ:cron構成と止まる条件

3つの環境が止まる条件

環境

毎日動かせる条件

止まる条件

手元のPC・自宅サーバー

常時電源が入っていて、再起動後に自動で再開する設定がある

停電、OS更新後の再起動、スリープ、ディスク故障

共有レンタルサーバーのcron(エックスサーバー スタンダードなど)

データ量が小さく、保持期間が短い

容量やファイル数の上限到達、cronの実行時間制限、規約上の用途外利用

VPS(ConoHa VPS・さくらのVPSなど)

保持期間に見合った容量を契約し、残量を監視している

ディスクの満杯、OS更新の放置、支払いの停止

結論:保持期間を決めてVPSに置く

「容量が要る」という条件で1つ選ぶなら、私はVPSを選びます。理由は次の3つです。

  1. 容量とファイル数を、自分で確認して自分で増やせる。共有サーバーのように、サイトやメールを巻き込んで止まることがない。
  2. 停電や自宅回線の影響を受けず、再起動されてもcronは自動で動き続ける。
  3. データ用のディスクを分けておけば、満杯になってもOSは止まらない。

ConoHa VPSとさくらのVPSのどちらにするかは、最初の表で出した「余裕込みの目安」を満たす構成について、公式料金ページで次の2つを比べて決めます。

  • 目安の容量を満たすプランそのものの月額
  • 小さいプランに追加ストレージを足した場合の月額

ただし、無期限で保存する場合は、毎年120GBずつ増える分の費用がかかり続けます。その場合は「直近の保持期間分はVPS、古いアーカイブは自宅の大容量ディスクへ」と分けると、月額の増加を止められます。

VPSでのcron設定

VPSでは、最初に仮想環境を作って requests を入れます。python3 -m venv /home/username/scraping/.venv の後に、/home/username/scraping/.venv/bin/pip install requests を実行します。続いて crontab -e で次の3行を登録します。

0 2 * * * cd /home/username/scraping && .venv/bin/python collect.py >> logs/collect.log 2>&1
0 5 * * * /home/username/scraping/.venv/bin/python /home/username/scraping/check_storage.py >> /home/username/scraping/logs/storage.log 2>&1
30 4 1 * * /home/username/scraping/.venv/bin/python /home/username/scraping/archive_month.py >> /home/username/scraping/logs/archive.log 2>&1
  • 1行目:毎日2時に取得処理を実行します。
  • 2行目:毎日5時に容量を記録し、残り日数を確認します。
  • 3行目:毎月1日4時30分に、前月分をtarにまとめます。取得処理と時間が重ならないようにずらしています。
  • いずれも標準出力とエラー出力をログに残し、止まったときに原因を追えるようにしています。

毎日動かし続けるには、これに加えて storage.log のWARNINGを週に一度は確認すること、OSのセキュリティ更新を定期的に当てること、アーカイブを月に一度は手元へ移すことが必要です。ブラウザ操作を伴う重い処理を同じVPSで動かす場合のメモリの考え方は、Selenium毎日実行 VPS・自宅・共有比較|26年10月 を参照してください。

この記事の結論

スクレイピングで取得したCSVや画像が毎月約10GB増える処理は、置き場所によって止まり方が違います。共有サーバーでは容量とファイル数の上限と利用規約、VPSでは追加容量の月額、自宅PCでは停電と再起動が詰まる場所です。まず保持期間を決めて「月数×10GB×1.3」で必要容量を出し、計測スクリプトで実際の増え方と残り日数を記録してください。保存は日付ディレクトリに分け、取得済みのURLは取り直さず、月初に前月分をtarにまとめます。容量が要るという条件で1つ選ぶならVPSが妥当で、ConoHa VPSとさくらのVPSは、必要容量を満たす構成の月額を公式料金ページで比べて決めます。