「このテーマは需要があるか」を、感覚で決めていないか。検索APIは、その問いに数字で答える。
書く前に調べる。その工程を自動化します
📋 この記事でわかること
- 検索APIのパラメータと、その使い分け
- 人気順・新着順で見えてくるものの違い
- 有料記事だけを検索して価格帯を調べる方法
- 上位記事のタイトルから頻出語を抽出する処理
- 複数キーワードを一括で比較するスクリプト
- 調査結果をCSVとレポートに出力する手順
記事を書く前に「そのテーマに需要があるか」を確かめる工程は、多くの人が飛ばしています。書いてから読まれないと気づくのは、時間の使い方として効率が良くありません。noteには内部の検索APIがあり、キーワードごとにどれだけの記事が存在し、どれが読まれているかを数字で取得できます。この記事では、そのAPIをPythonから使い、キーワード調査を自動化する方法を解説します。認証は不要なので、入門記事の内容だけで実行できます。
この記事でやること
- キーワードで記事を検索し、上位の傾向をつかむ
- 人気記事のタイトルから、使われている言葉を集計する
- 有料記事の価格帯を調べて、値付けの参考にする
- 複数キーワードを比較して、狙う切り口を決める
検索APIのパラメータ
使うのは /api/v3/searches です。検索対象と並び順を指定でき、記事以外にユーザーやマガジンも検索できます。
| パラメータ | 指定できる値 | 意味 |
|---|---|---|
| context | note | 記事を検索する |
| context | user | クリエイターを検索する |
| context | magazine | マガジンを検索する |
| context | hashtag | ハッシュタグを検索する |
| context | noteForSale | 有料記事に絞って検索する |
| q | 任意の文字列 | 検索キーワード |
| size | 数値 | 1回に取得する件数 |
| start | 数値 | 取得開始位置(ページングに使う) |
| sort | popular / hot / new | 並び順 |
並び順の3種類は、それぞれ見えるものが違います。目的に応じて使い分けてください。
- popular(人気順)——そのキーワードで最も読まれている記事。何が支持されているかを知るときに使います。
- hot(急上昇)——最近伸びている記事。今まさに関心が集まっている切り口が分かります。
- new(新着順)——直近の投稿。そのテーマに何人が参入しているかという競合の密度が見えます。
基本の検索を実装する
まずは1キーワードを検索して結果を表示する、最小のコードです。
import requests
UA = ("Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36")
def pick(d: dict, *keys, default=None):
"""camelCase / snake_case の表記ゆれを吸収する"""
for k in keys:
if k in d and d[k] is not None:
return d[k]
return default
def search(keyword: str, context: str = "note",
sort: str = "popular", size: int = 20, start: int = 0) -> dict:
"""note内を検索して data 部分を返す"""
res = requests.get(
"https://note.com/api/v3/searches",
params={
"context": context,
"q": keyword,
"size": size,
"start": start,
"sort": sort,
},
headers={"User-Agent": UA, "Accept": "application/json"},
timeout=20,
)
res.raise_for_status()
return res.json().get("data", {})
def extract_notes(data: dict) -> list:
"""レスポンスから記事の配列を取り出す"""
notes = data.get("notes", {})
if isinstance(notes, dict):
return notes.get("contents", [])
if isinstance(notes, list):
return notes
return data.get("contents", [])
if __name__ == "__main__":
data = search("Python 自動化", sort="popular", size=20)
notes = extract_notes(data)
print(f"取得件数: {len(notes)}")
print("-" * 60)
for n in notes:
likes = pick(n, "likeCount", "like_count", default=0)
price = pick(n, "price", default=0) or 0
user = n.get("user", {})
author = user.get("nickname", user.get("urlname", "?"))
mark = "有料" if price else "無料"
print(f"[{mark}] スキ{likes:>5} | {n.get('name', '')[:38]}")
print(f" by {author}")
レスポンスの構造が環境によって異なる可能性があるため、extract_notes() で複数の形に対応させています。非公式APIを扱うときの定石です。
検索結果をページングで多く集める
1回のリクエストで取れる件数には限りがあります。start をずらしながら複数回リクエストして、まとまった件数を集めます。
import time
from search_basic import search, extract_notes, pick
SLEEP_SEC = 1.2
def search_many(keyword: str, total: int = 100,
sort: str = "popular", size: int = 20) -> list:
"""指定件数までページングしながら検索する"""
results = []
start = 0
while len(results) < total:
data = search(keyword, sort=sort, size=size, start=start)
notes = extract_notes(data)
if not notes:
break
results.extend(notes)
print(f" start={start}: {len(notes)} 件(累計 {len(results)} 件)")
if len(notes) < size:
break # これ以上ない
start += size
time.sleep(SLEEP_SEC)
return results[:total]
def summarize(keyword: str, notes: list):
if not notes:
print("結果がありません")
return
likes = [pick(n, "likeCount", "like_count", default=0) or 0 for n in notes]
prices = [pick(n, "price", default=0) or 0 for n in notes]
paid = [p for p in prices if p > 0]
likes_sorted = sorted(likes, reverse=True)
median = likes_sorted[len(likes_sorted) // 2]
print(f"\n=== 「{keyword}」の検索結果分析 ===")
print(f" 取得件数 : {len(notes)} 件")
print(f" スキ最大 : {max(likes)}")
print(f" スキ平均 : {sum(likes) / len(likes):.1f}")
print(f" スキ中央値 : {median}")
print(f" 有料記事の数 : {len(paid)} 件"
f"({len(paid) / len(notes) * 100:.1f}%)")
if paid:
print(f" 価格の範囲 : {min(paid):,}円 〜 {max(paid):,}円")
print(f" 価格の平均 : {sum(paid) / len(paid):,.0f}円")
if __name__ == "__main__":
kw = "副業 始め方"
notes = search_many(kw, total=100, sort="popular")
summarize(kw, notes)
スキ数の平均と中央値を両方出しているのは、一部の突出した記事に平均が引っ張られるためです。中央値のほうが「よくある記事の水準」を表します。上位記事のスキが数千でも中央値が10なら、そのキーワードは一部の記事だけが伸びている状態だと分かります。
タイトルの頻出語を集計する
上位記事のタイトルには、そのキーワードで読まれるための言葉が集まっています。頻出語を抽出すると、自分のタイトルを作るときの参考になります。
"""
検索上位のタイトルから、よく使われている語を抽出する。
形態素解析ライブラリを使わず、標準ライブラリだけで動かす。
"""
import re
from collections import Counter
from search_paging import search_many
from search_basic import pick
# 意味を持たない語は除外する
STOP_WORDS = {
"する", "した", "して", "こと", "もの", "ため", "よう", "です",
"ます", "から", "まで", "ので", "けど", "でも", "とか", "って",
"この", "その", "あの", "どの", "私は", "自分", "今回", "note",
}
def tokenize(title: str) -> list:
"""
日本語のタイトルを、ざっくり語に分割する。
漢字+ひらがな、カタカナ、英数字のまとまりを拾う。
"""
if not title:
return []
patterns = [
r"[一-龥]{2,}", # 漢字2文字以上
r"[ァ-ヶー]{3,}", # カタカナ3文字以上
r"[a-zA-Z]{3,}", # 英字3文字以上
]
words = []
for p in patterns:
words.extend(re.findall(p, title))
return [w for w in words if w not in STOP_WORDS]
def analyze_titles(keyword: str, total: int = 100, top: int = 25):
notes = search_many(keyword, total=total, sort="popular")
counter = Counter()
for n in notes:
counter.update(tokenize(n.get("name", "")))
print(f"\n=== 「{keyword}」上位{len(notes)}記事のタイトル頻出語 ===")
for word, count in counter.most_common(top):
bar = "■" * min(count, 30)
print(f" {word:<12} {count:>3} {bar}")
return counter
def show_number_patterns(keyword: str, total: int = 100):
"""タイトルに数字を含む記事の割合を調べる"""
notes = search_many(keyword, total=total, sort="popular")
with_number = [n for n in notes
if re.search(r"\d", n.get("name", ""))]
print(f"\n=== 数字を含むタイトルの割合 ===")
print(f" {len(with_number)} / {len(notes)} 件"
f"({len(with_number) / max(len(notes), 1) * 100:.1f}%)")
print("\n 例:")
for n in with_number[:8]:
likes = pick(n, "likeCount", "like_count", default=0)
print(f" スキ{likes:>4} | {n.get('name', '')[:40]}")
if __name__ == "__main__":
kw = "ブログ 収益化"
analyze_titles(kw)
show_number_patterns(kw)
形態素解析を使わない理由
正確な単語分割にはMeCabやJanomeといったライブラリが必要ですが、導入に手間がかかります。この用途では「漢字の連なり」「カタカナの連なり」を拾うだけでも傾向は十分つかめます。まず動くものを作り、精度が足りなければ後から差し替えるという順序が実用的です。より精密に分析したい場合は pip install janome で導入できます。
▶ タグの需要を調べるなら、専用のAPIがあります。
ハッシュタグの母数と競合度を数値化する方法です。
有料記事の価格帯を調べる
有料記事を書くときに悩むのが値付けです。context=noteForSale を使えば、同じテーマの有料記事がいくらで売られているかを調べられます。
"""
有料記事に絞って検索し、価格帯の分布を調べる。
値付けの判断材料にする。
"""
import time
from collections import Counter
import requests
UA = ("Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36")
# 価格帯の区切り
BANDS = [
(0, 300, "〜300円"),
(300, 500, "300〜500円"),
(500, 1000, "500〜1,000円"),
(1000, 2000, "1,000〜2,000円"),
(2000, 5000, "2,000〜5,000円"),
(5000, 10 ** 9, "5,000円〜"),
]
def search_paid(keyword: str, total: int = 100, size: int = 20) -> list:
"""有料記事に絞って検索する"""
results = []
start = 0
while len(results) < total:
res = requests.get(
"https://note.com/api/v3/searches",
params={
"context": "noteForSale",
"q": keyword,
"size": size,
"start": start,
"sort": "popular",
},
headers={"User-Agent": UA},
timeout=20,
)
if res.status_code != 200:
break
data = res.json().get("data", {})
notes = data.get("notes", {})
contents = notes.get("contents", []) if isinstance(notes, dict) else notes
if not contents:
break
results.extend(contents)
if len(contents) < size:
break
start += size
time.sleep(1.2)
return results[:total]
def band_of(price: int) -> str:
for low, high, label in BANDS:
if low < price <= high:
return label
return "不明"
def analyze_price(keyword: str, total: int = 100):
notes = search_paid(keyword, total=total)
prices = [n.get("price", 0) or 0 for n in notes]
prices = [p for p in prices if p > 0]
if not prices:
print(f"「{keyword}」の有料記事が見つかりませんでした")
return
prices_sorted = sorted(prices)
median = prices_sorted[len(prices_sorted) // 2]
print(f"\n=== 「{keyword}」の有料記事 価格分析 ===")
print(f" 対象 : {len(prices)} 件")
print(f" 最安 : {min(prices):,} 円")
print(f" 最高 : {max(prices):,} 円")
print(f" 平均 : {sum(prices) / len(prices):,.0f} 円")
print(f" 中央値 : {median:,} 円 ← 値付けの目安")
print("\n --- 価格帯の分布 ---")
counter = Counter(band_of(p) for p in prices)
for _, _, label in BANDS:
count = counter.get(label, 0)
if count:
bar = "■" * count
pct = count / len(prices) * 100
print(f" {label:<14} {count:>3} 件 ({pct:>4.1f}%) {bar}")
print("\n --- スキが多い有料記事 ---")
top = sorted(
notes,
key=lambda n: n.get("likeCount", n.get("like_count", 0)) or 0,
reverse=True,
)[:8]
for n in top:
likes = n.get("likeCount", n.get("like_count", 0))
print(f" {n.get('price', 0):>6,}円 / スキ{likes:>4} | "
f"{n.get('name', '')[:32]}")
if __name__ == "__main__":
analyze_price("Webライティング")
中央値を目安として推している理由は、極端に高額な記事が平均を押し上げるためです。「その分野で普通に売れている価格」を知りたいなら中央値を見るのが妥当です。値付けの考え方そのものは価格設定の記事でも解説しています。
複数キーワードを一括で比較する
候補となるキーワードが複数あるとき、まとめて調べて比較すると判断が早くなります。
"""
複数キーワードを一括で調査し、比較表として出力する。
"""
import csv
import time
from datetime import datetime
from search_paging import search_many
from search_basic import pick
KEYWORDS = [
"副業 始め方",
"在宅ワーク 主婦",
"Webライター 未経験",
"ブログ 収益化",
"せどり 初心者",
]
def evaluate(keyword: str, total: int = 60) -> dict:
"""キーワード1つを評価する"""
popular = search_many(keyword, total=total, sort="popular")
time.sleep(1.0)
recent = search_many(keyword, total=30, sort="new")
if not popular:
return {"keyword": keyword, "status": "結果なし"}
likes = [pick(n, "likeCount", "like_count", default=0) or 0
for n in popular]
prices = [pick(n, "price", default=0) or 0 for n in popular]
paid = [p for p in prices if p > 0]
likes_sorted = sorted(likes, reverse=True)
median_likes = likes_sorted[len(likes_sorted) // 2]
return {
"keyword": keyword,
"sampled": len(popular),
"max_likes": max(likes),
"median_likes": median_likes,
"paid_ratio": round(len(paid) / len(popular) * 100, 1),
"median_price": (sorted(paid)[len(paid) // 2] if paid else 0),
"recent_count": len(recent),
}
def main():
rows = []
for kw in KEYWORDS:
print(f"\n### 調査中: {kw}")
rows.append(evaluate(kw))
time.sleep(2.0)
print("\n" + "=" * 78)
print(f"{'キーワード':<20}{'最大スキ':>8}{'中央スキ':>8}"
f"{'有料率':>8}{'中央価格':>10}")
print("-" * 78)
for r in rows:
if r.get("status"):
print(f"{r['keyword']:<20} {r['status']}")
continue
print(f"{r['keyword']:<20}{r['max_likes']:>8}{r['median_likes']:>8}"
f"{r['paid_ratio']:>7}%{r['median_price']:>10,}")
stamp = datetime.now().strftime("%Y%m%d")
filename = f"keyword_research_{stamp}.csv"
with open(filename, "w", encoding="utf-8-sig", newline="") as f:
writer = csv.DictWriter(f, fieldnames=list(rows[0].keys()))
writer.writeheader()
writer.writerows(rows)
print(f"\n{filename} に保存しました")
if __name__ == "__main__":
main()
この比較表から読み取れることを整理します。
| 指標 | 高いとき | 低いとき |
|---|---|---|
| 最大スキ | 需要は確実にある | そもそも関心が薄い可能性 |
| 中央値スキ | 普通に書いても読まれる | 一部の記事だけが伸びている |
| 有料率 | お金を払う人がいる領域 | 無料で満たされている |
| 新着件数 | 競合が多い | 参入余地がある |
狙い目は「最大スキは高いが、新着が少ない」という組み合わせです。需要はあるのに書いている人が少ない状態を意味します。逆に「最大スキが低く、新着が多い」領域は、労力に対して結果が出にくい可能性が高くなります。
▼ RECOMMENDATION ▼
需要を調べたら、届ける手段も用意する
リサーチで良いテーマを見つけても、読者に届かなければ数字は動きません。noteの中で待つだけでなく、外からアクセスを集める導線が必要です。アメブロであれば、アメプレスProがいいね・フォロー・アクセス獲得を自動で回します。さらにnoteと違ってASPアフィリエイトが使えるため、リサーチで見つけた需要を収益に変換しやすくなります。月額2,980円、365日LINEサポート付きです。
※ 当サイトはアフィリエイト広告を含みます。
クリエイターを検索して参考先を見つける
context=user を指定すると、キーワードに関連するクリエイターを検索できます。同じ分野で先行している人を見つけるのに使えます。
import requests
UA = ("Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36")
def search_users(keyword: str, size: int = 20) -> list:
res = requests.get(
"https://note.com/api/v3/searches",
params={
"context": "user",
"q": keyword,
"size": size,
"start": 0,
},
headers={"User-Agent": UA},
timeout=20,
)
res.raise_for_status()
data = res.json().get("data", {})
users = data.get("users", {})
return users.get("contents", []) if isinstance(users, dict) else users
def show(keyword: str):
users = search_users(keyword)
print(f"=== 「{keyword}」に関連するクリエイター ===")
print(f"{'フォロワー':>9} {'記事数':>6} 名前")
print("-" * 60)
def follower_of(u):
return u.get("followerCount", u.get("follower_count", 0)) or 0
for u in sorted(users, key=follower_of, reverse=True):
followers = follower_of(u)
notes = u.get("noteCount", u.get("note_count", 0))
name = u.get("nickname", "")
urlname = u.get("urlname", "")
print(f"{followers:>9,} {notes:>6} {name[:20]}")
print(f"{'':>9} {'':>6} https://note.com/{urlname}")
if __name__ == "__main__":
show("Webライティング")
フォロワーの多い人がどんな記事を書いているかは、そのまま /api/v2/creators/{urlname}/contents で調べられます。参考にするのは構成や切り口であって、内容をそのまま真似ることではありません。
調査結果をレポートにまとめる
調べた内容をテキストファイルに残しておくと、後から見返せます。実行するたびに日付付きのレポートを出力する形にします。
"""
キーワード調査の結果をMarkdownレポートとして出力する。
"""
from datetime import datetime
from search_paging import search_many
from search_basic import pick
from title_words import tokenize
from collections import Counter
def build_report(keyword: str, total: int = 60) -> str:
popular = search_many(keyword, total=total, sort="popular")
if not popular:
return f"# {keyword}\n\n結果が取得できませんでした。\n"
likes = [pick(n, "likeCount", "like_count", default=0) or 0
for n in popular]
prices = [pick(n, "price", default=0) or 0 for n in popular]
paid = [p for p in prices if p > 0]
counter = Counter()
for n in popular:
counter.update(tokenize(n.get("name", "")))
lines = [
f"# キーワード調査: {keyword}",
"",
f"調査日時: {datetime.now().strftime('%Y-%m-%d %H:%M')}",
f"サンプル数: {len(popular)} 件",
"",
"## 数値サマリー",
"",
"| 指標 | 値 |",
"|------|-----|",
f"| スキ最大 | {max(likes)} |",
f"| スキ平均 | {sum(likes) / len(likes):.1f} |",
f"| 有料記事 | {len(paid)} 件 |",
]
if paid:
lines.append(f"| 価格中央値 | {sorted(paid)[len(paid) // 2]:,} 円 |")
lines += ["", "## タイトル頻出語", ""]
for word, count in counter.most_common(20):
lines.append(f"- {word}({count}回)")
lines += ["", "## 上位記事", ""]
top = sorted(
popular,
key=lambda n: pick(n, "likeCount", "like_count", default=0) or 0,
reverse=True,
)[:15]
for n in top:
likes_n = pick(n, "likeCount", "like_count", default=0)
user = n.get("user", {})
lines.append(
f"- **{n.get('name', '')}** \n"
f" スキ{likes_n} / by {user.get('nickname', '')}"
)
return "\n".join(lines)
def main(keyword: str):
report = build_report(keyword)
stamp = datetime.now().strftime("%Y%m%d")
safe = keyword.replace(" ", "_").replace("/", "_")
filename = f"research_{safe}_{stamp}.md"
with open(filename, "w", encoding="utf-8") as f:
f.write(report)
print(report)
print(f"\n{filename} に保存しました")
if __name__ == "__main__":
import sys
kw = sys.argv[1] if len(sys.argv) > 1 else "note 使い方"
main(kw)
リサーチ結果をどう使うか
数字が出たあと、それをどう解釈するかが本題です。よくある判断の型を整理します。
| 状況 | 解釈 | 取るべき行動 |
|---|---|---|
| 上位が数千スキ、中央値も高い | 需要が大きく、質も求められる | 切り口を絞って差別化する |
| 上位は高いが中央値がほぼ0 | 一部の有名人だけが読まれている | より具体的なキーワードに変える |
| 全体的にスキが少ない | 需要が小さい可能性 | 別の言い回しでも検索してみる |
| 新着が多く更新も活発 | 競合が激しい | ニッチな組み合わせを探す |
| 有料記事が多く売れている | お金を払う読者がいる | 有料化を検討する価値あり |
| タイトルに数字が多い | 具体性が好まれる領域 | 自分も数字を入れる |
note内の検索と外部検索は別物
この記事で扱っているのはnote内部の検索です。GoogleやYahooからの検索流入とは別の指標であることに注意してください。note内で人気がなくても、検索エンジンからは読まれ続けている記事は存在します。両方の経路を意識するのが正しい設計です。検索エンジン側の対策はnoteのSEO記事で解説しています。
まとめ:書く前に調べる工程を自動化する
検索APIを使ったリサーチは、認証不要で実装も容易です。それでいて「そのテーマに需要があるか」という、書く前にもっとも知りたい情報が得られます。
この記事の要点
- 検索は
/api/v3/searches。context と sort で用途が変わる - popular=支持されているもの、new=競合の密度
- スキ数は平均と中央値の両方を見る
context=noteForSaleで有料記事の価格帯が分かる- タイトル頻出語は、正規表現だけでも十分傾向がつかめる
- 複数キーワードを比較して、参入する切り口を決める
- 狙い目は「需要はあるが新着が少ない」領域
- note内検索と外部検索は別の指標として扱う
調査に時間をかけすぎると書く時間がなくなります。1テーマ5分程度で判断できる状態を作り、あとは書くことに集中するのが健全なバランスです。この記事のスクリプトは、その5分を作るためのものです。
FAQ|note検索APIについてよくある質問
レスポンスに総件数を示すフィールドが含まれる場合がありますが、確実ではありません。この記事のコードでは、取得した件数から傾向を判断する方式にしています。総件数そのものより「上位20件がどれくらい読まれているか」のほうが、実際の判断には役立ちます。件数が多くても、上位以外は読まれていないケースがほとんどだからです。
popularは累積の人気、hotは直近の勢いを反映していると考えられます。長く読まれ続けている定番記事を知りたいならpopular、今関心が集まっている切り口を知りたいならhotが向いています。両方を取得して差分を見ると、「最近になって注目され始めたテーマ」が浮かび上がることがあります。
60〜100件あれば傾向は十分つかめます。それ以上取得しても、下位の記事はほとんど読まれていないため判断材料になりません。リクエスト数を増やすとサーバー負荷も上がるので、必要最小限に留めてください。複数キーワードを比較する場合は、各60件程度で揃えると比較しやすくなります。
キーワードが具体的すぎる可能性があります。スペースで区切った複合キーワードは、両方を含む記事しか対象にならないため件数が激減します。まず単語1つで検索し、件数を確認してから絞り込んでいくのが確実です。また、レスポンス構造の変更で取り出せていないケースもあるため、生のJSONを保存して確認してみてください。
集計した数字や傾向を自分の分析として書くことに問題はありません。「このキーワードの上位記事は数字入りタイトルが7割」といった内容は、有用な情報です。ただし、他人の記事タイトルを大量に列挙してまとめサイトのようにする使い方は避けてください。分析結果を自分の言葉で書くことと、他人のコンテンツを並べることは別物です。
価格が高い記事が売れているとは限らない点に注意してください。APIから取得できるのは価格とスキ数であり、販売数は分かりません。スキが多い有料記事は関心を集めていると推測できますが、購入されたかどうかは別です。価格帯はあくまで「その分野の相場感」として参考にし、自分の記事の内容量と読者層から最終判断してください。
公開された制限値はありませんが、無制限ではありません。短時間に大量のリクエストを送れば429が返ります。この記事のコードでは1.2秒以上の間隔を空けています。複数キーワードを一括調査する場合は、キーワード間にも2秒程度の待ちを入れてください。調査は毎日行うものではないので、間隔を広めに取っても実用上の支障はありません。
できます。context を magazine や hashtag に変えるだけです。マガジン検索は、そのテーマでどんなまとめ方がされているかを知るのに役立ちます。ハッシュタグについては専用のエンドポイントがあり、記事数や関連タグまで取得できるため、そちらのほうが情報量が多くなります。詳しくはハッシュタグの記事で解説しています。
上がります。Janomeを使えば品詞情報が取れるため、名詞だけを抽出するといった処理ができ、ノイズが減ります。導入は pip install janome だけで済み、外部の辞書も不要です。ただし処理速度は落ちるため、100件程度の分析であれば正規表現方式でも実用に耐えます。精度が足りないと感じてから切り替えるので十分です。
1テーマあたり5〜10分が目安です。それ以上かけても判断は大きく変わりません。リサーチの目的は「書くべきか書かないべきか」を素早く決めることであり、完璧な分析ではありません。調べることに時間を使いすぎて書けなくなるのは本末転倒です。スクリプトを整えて、実行して数字を見るだけで判断できる状態にしておくのが理想です。