Pythonスクレイピング 後編
はじめに:HTML解析をやめて「裏口」を使う
こんにちは、ニンジンです🥕
前編では、自作の記事収集ツールが自分のブログを相手に「取得件数:0」という結果に終わるまでをお届けしました。
この記事では、HTMLという「表口」ではなく、データが整理された「専用の裏口」——WordPress REST API——を使って、一瞬でデータを取得する方法と、自動化エンジニアとしての「最終的な着地点」を書きます。
この記事で分かること
- WordPress REST API(
/wp-json/wp/v2/posts)とは何か - APIから記事一覧をExcel化する、10数行のシンプルなコード
- CustomTkinter + threading で作った、モード切替つきGUIツールの構造
- 「ECサイトの価格調査」を最終的に断念した理由
APIを使うと、コードはここまで短くなる
import requests
import openpyxl
from datetime import datetime
# WordPressの公式APIエンドポイント
API_URL = "https://py-vbalab.com/wp-json/wp/v2/posts"
print("APIからデータを直接取得中...")
# 1. データを取ってくる
response = requests.get(API_URL)
if response.status_code == 200:
posts = response.json() # 文字の羅列をPythonの「辞書型」に変換
# Excelの準備
wb = openpyxl.Workbook()
ws = wb.active
ws.append(["投稿日", "タイトル", "URL"])
for post in posts:
# JSONの中から必要な項目を抜き出す
title = post['title']['rendered']
link = post['link']
# 日付を読みやすく加工
date_raw = post['date']
date_formated = date_raw.split('T')[0] # 2026-04-06T... を 2026-04-06 に
ws.append([date_formated, title, link])
print(f"取得: {title}")
# 保存
filename = f"Quick_Report_{datetime.now().strftime('%Y%m%d')}.xlsx"
wb.save(filename)
print(f"\n✨ 完了!爆速で '{filename}' を作成しました。")
else:
print("データが取得できませんでした。")結果:10件の記事を取得成功
?per_page=100 で最大100件、?page=2 でページ送りができます。自動レポートくん(NINJIN Edition):GUIツール化
このツールの目玉は、調査モードを画面から切り替えられる点です。
- WordPress APIモード:相手がWordPressサイトならこれ。HTML解析が不要で、正確かつ短時間でデータを抽出できます。
- Seleniumモード:APIが無い汎用サイト向けの最終手段。ブラウザを動かすので、JavaScriptで描画される動的サイトにも対応できます。
threading(マルチスレッド)でGUIを固まらせない
このツールは裏側で「スレッド」を使っています。VBAで重い処理を実行するとExcelが「応答なし」で固まった経験はありませんか。Pythonの threading を使えば、「裏で作業する担当」と「画面を動かし続ける担当」を分けられます。これにより、スクレイピング中でもログがリアルタイムに流れます。
Pythonコード(全文)
import customtkinter as ctk
import threading
import requests
import openpyxl
from datetime import datetime
import time
import os # ファイル保存確認用
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
class NinjinScraperApp(ctk.CTk):
def __init__(self):
super().__init__()
self.title("自動レポートくん - NINJIN Edition")
self.geometry("600x450")
# --- レイアウト設定 ---
self.label = ctk.CTkLabel(self, text="調査モードを選択して、URLを入力してください")
self.label.pack(pady=10)
# モード選択(セグメントボタン)
self.mode_var = ctk.StringVar(value="Selenium")
self.mode_switch = ctk.CTkSegmentedButton(self,
values=["Selenium", "WordPress API"],
variable=self.mode_var)
self.mode_switch.pack(pady=10)
self.url_entry = ctk.CTkEntry(self, placeholder_text="https://...", width=400)
self.url_entry.pack(pady=10)
self.btn_run = ctk.CTkButton(self, text="レポート作成開始",
fg_color="#FF8C00", # 人参オレンジ
command=self.start_task)
self.btn_run.pack(pady=20)
self.log_box = ctk.CTkTextbox(self, width=500, height=150)
self.log_box.pack(pady=10)
def start_task(self):
# メインスレッドが止まらないように別スレッドで実行
thread = threading.Thread(target=self.run_scraping)
thread.start()
def run_scraping(self):
mode = self.mode_var.get()
url = self.url_entry.get() #self.url_entry(GUIで入力されたURL)からURLを取得
if not url.startswith("http"):
self.update_log("⚠️ 有効なURLを入力してください。")
return
self.update_log(f"🚀 【{mode}】で調査を開始します...")
if mode == "WordPress API":
self.run_api_logic(url) # ← WordPress API版を呼び出す urlを引数として渡す
else:
self.run_selenium_logic(url) # ← ここでSelenium版を呼び出す urlを引数として渡す
self.update_log("✨ すべてのタスクが終了しました。")
def run_api_logic(self, target_url):
"""
WordPress REST APIを使用してデータを抜くロジック
"""
# 末尾に /wp-json/wp/v2/posts を付与(入力URLの整形)
base_url = target_url.rstrip('/')
api_endpoint = f"{base_url}/wp-json/wp/v2/posts"
try:
self.update_log(f"🔗 接続中: {api_endpoint}")
response = requests.get(api_endpoint, timeout=10)
if response.status_code == 200:
posts = response.json()
# Excelの準備
wb = openpyxl.Workbook()
ws = wb.active
ws.append(["投稿日", "タイトル", "URL"])
for post in posts:
title = post['title']['rendered']
link = post['link']
date_raw = post['date']
date_formated = date_raw.split('T')[0]
ws.append([date_formated, title, link])
self.update_log(f"📖 取得成功: {title[:20]}...") # 長いので省略表示
# 保存処理
filename = f"API_Report_{datetime.now().strftime('%Y%m%d_%H%M')}.xlsx"
wb.save(filename)
self.update_log(f"✅ 保存完了: {filename}")
self.show_finish_message(filename)
else:
self.update_log(f"❌ エラー: ステータスコード {response.status_code}")
self.update_log("※WordPressサイトではないか、APIが閉じられている可能性があります。")
except Exception as e:
self.update_log(f"⚠️ 想定外のエラー: {str(e)}")
def update_log(self, message):
"""ログボックスに文字を出す補助関数"""
self.log_box.insert("end", f"{message}\n")
self.log_box.see("end") # 常に最新の行を表示
def show_finish_message(self, filename):
"""完了通知(人参くんらしいメッセージ)"""
from tkinter import messagebox
messagebox.showinfo("調査完了", f"レポートの作成が終わりました!\nファイル名: {filename}")
def run_selenium_logic(self, target_url):
"""
Seleniumを使用して汎用的にデータを抜くロジック
"""
options = Options()
# ユーザーに動きを見せたい場合は headless を False に、
# 裏でこっそり動かしたい場合は True にします
options.add_argument('--headless')
driver = None
try:
self.update_log("🌐 ブラウザを起動しています...")
driver = webdriver.Chrome(options=options)
self.update_log(f"🚚 サイトへアクセス中: {target_url}")
driver.get(target_url)
# ページ読み込みと遅延読み込み対策
time.sleep(5)
driver.execute_script("window.scrollTo(0, 800);")
time.sleep(2)
self.update_log("🔍 記事リンクを探索中...")
links = driver.find_elements(By.TAG_NAME, "a")
wb = openpyxl.Workbook()
ws = wb.active
ws.append(["取得日", "タイトル", "URL"])
memo_urls = set()
count = 0
# ドメイン名を取得してフィルタリングに使用
from urllib.parse import urlparse
domain = urlparse(target_url).netloc
for link in links:
url = link.get_attribute("href")
text = link.text.strip()
# 自分のサイト内のリンクで、かつテキストがある程度長いものを抽出
if url and text and domain in url and url not in memo_urls:
if len(text) > 10: # 短すぎるリンク(「ホーム」など)を除外
clean_title = text.split('\n')[0]
ws.append([datetime.now().strftime('%Y-%m-%d'), clean_title, url])
memo_urls.add(url)
count += 1
if count % 5 == 0: # 5件ごとにログを更新
self.update_log(f"📥 {count}件目のデータを保持...")
# 保存
filename = f"Sel_Report_{datetime.now().strftime('%Y%m%d_%H%M')}.xlsx"
wb.save(filename)
self.update_log(f"✅ 調査完了!合計 {count} 件保存しました。")
self.show_finish_message(filename)
except Exception as e:
self.update_log(f"❌ Seleniumエラー: {str(e)}")
finally:
if driver:
driver.quit()
self.update_log("🔌 ブラウザを正常に終了しました。")
if __name__ == "__main__":
app = NinjinScraperApp()
app.mainloop()ロジックの心臓部
入力されたURLは、選ばれたモードに応じてそれぞれのロジックへリレーされます。
# モードに応じて呼び出す関数をバトンタッチ
if mode == "WordPress API":
self.run_api_logic(url)
else:
self.run_selenium_logic(url)- API版:
requestsでJSONを取得し、辞書型データからタイトルやURLを抜き出します。 - Selenium版:
driver.find_elementsでページ内のリンク(aタグ)を全走査し、ドメイン一致などでフィルタします。
どちらのモードでも、最終的には使い慣れたExcelファイル(.xlsx)として保存されます。
なぜ「ECサイトの価格調査」を断念したのか
当初の目的だった「ECサイトの価格調査」の実装は、最終的に見送りました。理由はシンプルで、「やろうと思えばできるが、やるべきではない」と判断したためです。
- メンテナンスの限界:大手サイトは構造変更が激しく、昨日のコードが今日動かなくなる「いたちごっこ」になります。個人での運用は現実的ではありません。
- 規約と負荷:無理なスクレイピングで負荷をかけるより、公式API(AmazonならPA-API、楽天なら楽天ウェブサービスなど)を申請するのが、長期的には最も安全で確実です。
よくある質問(FAQ)
Q/wp-json/wp/v2/posts では何件取得できますか?
Aデフォルトは10件です。?per_page=100 を付けると最大100件、?page=2 で次の100件、というようにページ送りで取得します。
QどのサイトでもREST APIが使えますか?
AWordPressサイトなら基本的に有効です。ただし、セキュリティプラグインなどでAPIを無効化・制限しているサイトもあります。403や404が返る場合は閉じられています。
QAPIとスクレイピングでは、どちらが「行儀がよい」ですか?
AAPIです。整形済みのデータを、想定された窓口から取得するので、サーバーへの負荷も、サイト構造変更の影響も小さくなります。
Qタイトルが {'rendered': '...'} という形で返るのはなぜですか?
AWordPressはタイトルを「編集用の生データ」と「表示用にHTMLエスケープ済みのデータ」に分けて返します。画面表示に使うのは rendered の方です。
QAmazonや楽天の価格もAPIで取得できますか?
A公式のPA-API(Amazon)や楽天ウェブサービスを申請すれば取得できます。規約の範囲で使え、構造変更にも強いので、無理にスクレイピングするより確実です。
QGUIのログが処理中に固まらないのはなぜですか?
Athreading で、時間のかかるスクレイピング処理を別スレッドに逃がしているためです。メインスレッド(画面の描画)は止まらないので、ログがリアルタイムに流れます。
Follow me on X
本ブログの中の人「ニンジン🥕」はXやってます。新着記事はいち早くポストでお知らせしているので、見逃したくない方は @ninjin_py_vba をフォローしてみてください🥕
@ninjin_py_vba をフォローシリーズ記事一覧
電子工作・自動化ツールの製作記録をシリーズごとにまとめています。気になるテーマからお読みください。
-
FILE.01 — IoT
Raspberry Pi Pico W × GASラズパイPico W×GASで自作!「LINEで動く温湿度&スマートリモコン」完全ロードマップ
「外出中のペットの室温が心配…」そんな思いからスタートしたIoT自作プロジェクトの総集編。温湿度監視からエアコン遠隔操作まで、仕組みをゼロから作りたい方のための全5回の開発記録です。
シリーズを読む -
FILE.02 — CNC
Arduino × レーザー刻印CNCCNC自作シリーズ
高精度な加工を目指し、本格的な自作CNC製作に挑戦中。現在は基幹パーツであるオリエンタルモーターの納品を待つ「設計・準備編」を公開。ハードとソフトの両面から、理想のマシンを形にする過程をリアルタイムにお届けします。
シリーズを読む -
FILE.03 — XY軸制御
Raspberry Pi × ステッピングモーターXYテーブルシリーズ
Raspberry Piとステッパモーターを使い、ゼロから2軸制御に挑む記録。OS設定から回路設計、多軸制御のPythonコードまで、躓きやすいポイントを徹底図解。電子工作初心者が「動く感動」を味わうための実戦ガイドです。
シリーズを読む -
FILE.04 — 業務自動化
Python × Excel × CustomTkinter【Python開発記】NINJIN Mail制作記
PythonとExcelを連携させ、実務で即戦力となるメール送信ツールを開発。CustomTkinterによるUI構築や、ミスを防ぐテンプレート活用術など、現場の「痒い所に手が届く」自動化ノウハウを細部まで丁寧に解説します。
シリーズを読む -
FILE.05 — 業務自動化
Python × Excel × GUIアプリ化Invoice Maker(請求書・見積書自動作成)
手作業の請求書作成から卒業!PythonでExcelデータを読み込み一括PDF化する「Invoice_Maker」の作り方を全3回で解説。基本ロジックからGUIアプリ化まで、実務で役立つ自動化ノウハウが満載です。
シリーズを読む




コメント