<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Knowledge Management on AI2CORE - AI技術ブログ</title>
    <link>https://www.ai2core.com/tags/knowledge-management/</link>
    <description>Recent content in Knowledge Management on AI2CORE - AI技術ブログ</description>
    <generator>Hugo -- 0.146.4</generator>
    <language>ja</language>
    <lastBuildDate>Thu, 12 Feb 2026 16:35:00 +0900</lastBuildDate>
    <atom:link href="https://www.ai2core.com/tags/knowledge-management/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>OpenClawで社内Wikiを自動巡回させてみた（実例紹介）</title>
      <link>https://www.ai2core.com/posts/2026-02-12-openclaw-usecase/</link>
      <pubDate>Thu, 12 Feb 2026 16:35:00 +0900</pubDate>
      <guid>https://www.ai2core.com/posts/2026-02-12-openclaw-usecase/</guid>
      <description>OpenClawを使って、散らばった社内情報を自動収集・要約するボットを作った事例。</description>
      <content:encoded><![CDATA[<h1 id="openclawで社内wikiを自動巡回させてみた実例紹介">OpenClawで社内Wikiを自動巡回させてみた（実例紹介）</h1>
<h2 id="はじめに">はじめに</h2>
<p>「あのプロジェクトの設計思想、どのドキュメントに書いてあったっけ？」
「新メンバー向けのオンボーディング手順、最新版はWiki？それともGoogle Docs？」
「先月の定例会の議事録、誰が持ってるんだっけ…」</p>
<p>エンジニアの皆さんなら、こんな経験に一度は心当たりがあるのではないでしょうか。</p>
<p>私たちのチームでも、情報がConfluence、Notion、Google Drive、GitHub Wikiなど、様々な場所に散在していました。いわゆる「情報のサイロ化」です。この結果、必要な情報を見つけるために多大な時間を費やし、時には見つけられずに同じ質問がSlackで繰り返される、という非効率が常態化していました。</p>
<p>この問題を解決すべく、私たちは社内に散らばった情報を自動で収集・要約し、一元的に検索できるボットの開発に取り組みました。そして、その中核技術として採用したのが、Webクローラーフレームワーク「Scrapy」とLLM（大規模言語モデル）を統合した「<strong>OpenClaw</strong>」です。</p>
<p>この記事では、OpenClawを使って実際に社内Wiki（今回はConfluenceを例にします）を自動巡回し、ページの情報を収集・要約するボットを構築した実例を、具体的なコードと共に詳しくご紹介します。社内のナレッジマネジメントに課題を感じているエンジニアの方々にとって、一つの実践的な解決策となれば幸いです。</p>
<h2 id="なぜこの技術話題が重要なのか背景と課題">なぜこの技術・話題が重要なのか（背景と課題）</h2>
<p>現代の開発現場では、スピードとコラボレーションが強く求められます。その基盤となるのが、円滑な情報共有です。しかし、組織が成長し、プロジェクトが複雑化するにつれて、情報は意図せず分散し、サイロ化していく傾向にあります。</p>
<h3 id="情報サイロ化が引き起こす深刻な問題">情報サイロ化が引き起こす深刻な問題</h3>
<p>情報のサイロ化は、単なる「探し物が面倒」というレベルの問題にとどまりません。</p>
<ol>
<li><strong>生産性の低下</strong>: 開発者は1日のうち、情報の検索に平均で20%以上の時間を費やしているという調査結果もあります。これは週に1日分を情報検索に浪費している計算になり、無視できないコストです。</li>
<li><strong>オンボーディングの困難化</strong>: 新しくチームに参加したメンバーが、必要な情報を自力でキャッチアップするのが非常に困難になります。教育担当者の負担が増え、新メンバーの立ち上がりも遅れてしまいます。</li>
<li><strong>ナレッジの陳腐化と属人化</strong>: ドキュメントが更新されずに古い情報が参照されたり、特定の人物しか知らない「暗黙知」が増えたりします。結果として、誤った意思決定や、担当者不在時の業務停滞リスクが高まります。</li>
<li><strong>機会損失</strong>: 過去のプロジェクトで得られた知見やノウハウが埋もれてしまい、再利用されません。車輪の再発明が繰り返され、組織全体の学習効率が低下します。</li>
</ol>
<h3 id="llm時代における新たなアプローチ">LLM時代における新たなアプローチ</h3>
<p>こうした課題に対し、これまでも全文検索エンジンの導入など、様々な対策が取られてきました。しかし、キーワード検索だけでは、大量の検索結果の中から本当に求めている情報（コンテキスト）を見つけ出すのは依然として困難でした。</p>
<p>ここで大きな変革をもたらしたのが、LLMの登場です。LLMは、自然言語で書かれた膨大なテキストを理解し、要約したり、質問に答えたりする能力を持っています。</p>
<p>このLLMの能力と、Web上の情報を体系的に収集するWebクローリング技術を組み合わせることで、「<strong>散在する情報を自動で収集し、文脈を理解した上で要約・整理し、自然言語で対話的に検索できる</strong>」という、次世代のナレッジマネジメントが実現可能になります。</p>
<p>OpenClawは、まさにこの「クローリング」と「LLMによるデータ処理」をシームレスに繋ぐために設計されたフレームワークであり、この課題に対する強力なソリューションとなり得るのです。</p>
<h2 id="具体的な解決策や詳細な解説">具体的な解決策や詳細な解説</h2>
<p>それでは、実際にOpenClawを使って社内Confluenceを巡回する情報収集ボットを構築するプロセスを、ステップ・バイ・ステップで解説していきます。</p>
<h3 id="システム全体のアーキテクチャ">システム全体のアーキテクチャ</h3>
<p>今回構築するシステムの全体像は以下のようになります。</p>
<pre tabindex="0"><code class="language-mermaid" data-lang="mermaid">graph TD
    subgraph &#34;開発環境&#34;
        A[開発者] -- 1. 実行コマンド --&gt; B(OpenClaw実行環境);
    end

    subgraph &#34;OpenClawボット&#34;
        B -- 2. ログイン要求 --&gt; C(社内Wiki: Confluence);
        C -- 3. ログイン成功/Cookie発行 --&gt; B;
        B -- 4. ページ巡回要求 --&gt; C;
        C -- 5. HTMLを返す --&gt; B;
        B -- 6. HTMLから本文抽出 --&gt; D(LLM Pipeline);
        D -- 7. テキスト要約要求 --&gt; E[LLM API (例: GPT-4o)];
        E -- 8. 要約結果を返す --&gt; D;
        D -- 9. 処理済みデータを保存 --&gt; F[データストア (JSONファイル)];
    end

    A -- 10. 結果を確認 --&gt; F;
</code></pre><ol>
<li>開発者がローカル環境でOpenClawのクローラーを実行します。</li>
<li>クローラーはまずConfluenceにログインし、セッションを確立します。</li>
<li>指定されたスペースのページを順番に巡回し、各ページのHTMLコンテンツを取得します。</li>
<li>取得したHTMLから、ヘッダーやサイドバーなどの不要な部分を取り除き、本文テキストのみを抽出します。</li>
<li>抽出したテキストを、OpenClawのパイプライン機能を使ってLLM API（今回はOpenAIのGPT-4oを想定）に送信します。</li>
<li>LLMがテキストを要約し、その結果を返します。</li>
<li>ページのタイトル、URL、そしてLLMによる要約をセットにして、JSONファイルとして保存します。</li>
</ol>
<h3 id="step-1-環境構築">Step 1: 環境構築</h3>
<p>まず、開発環境を準備します。Python 3.8以上と、パッケージ管理ツールであるpipが必要です。仮想環境を作成することを強く推奨します。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">7
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># 仮想環境の作成とアクティベート</span>
</span></span><span style="display:flex;"><span>python -m venv .venv
</span></span><span style="display:flex;"><span>source .venv/bin/activate
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># OpenClawと必要なライブラリのインストール</span>
</span></span><span style="display:flex;"><span>pip install openclaw
</span></span><span style="display:flex;"><span>pip install python-dotenv <span style="color:#75715e"># .envファイルでAPIキーを管理するため</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="step-2-プロジェクトの作成">Step 2: プロジェクトの作成</h3>
<p>OpenClawには、プロジェクトの雛形を生成する便利なコマンドが用意されています。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>claw new confluence_crawler
</span></span><span style="display:flex;"><span>cd confluence_crawler
</span></span></code></pre></td></tr></table>
</div>
</div><p>これを実行すると、以下のようなディレクトリ構造が生成されます。</p>
<pre tabindex="0"><code>confluence_crawler/
├── confluence_crawler/
│   ├── __init__.py
│   ├── items.py         # 収集するデータの構造を定義
│   ├── middlewares.py
│   ├── pipelines.py     # データ処理のパイプラインを定義
│   ├── settings.py      # プロジェクト全体の設定
│   └── spiders/         # クローラー本体を配置
│       └── __init__.py
└── claw.cfg
</code></pre><p>Scrapyを使ったことがある方なら、非常によく似た構造であることに気づくでしょう。OpenClawはScrapyをベースに構築されているため、Scrapyの知識を活かすことができます。</p>
<h3 id="step-3-itemの定義">Step 3: Itemの定義</h3>
<p>まず、収集したいデータの構造を<code>items.py</code>で定義します。今回はページのタイトル、URL、本文、そしてLLMによる要約を保存することにします。</p>
<p><strong><code>confluence_crawler/items.py</code></strong></p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">8
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> scrapy
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ConfluencePageItem</span>(scrapy<span style="color:#f92672">.</span>Item):
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># 収集するデータのフィールドを定義</span>
</span></span><span style="display:flex;"><span>    url <span style="color:#f92672">=</span> scrapy<span style="color:#f92672">.</span>Field()
</span></span><span style="display:flex;"><span>    title <span style="color:#f92672">=</span> scrapy<span style="color:#f92672">.</span>Field()
</span></span><span style="display:flex;"><span>    content <span style="color:#f92672">=</span> scrapy<span style="color:#f92672">.</span>Field()
</span></span><span style="display:flex;"><span>    summary <span style="color:#f92672">=</span> scrapy<span style="color:#f92672">.</span>Field() <span style="color:#75715e"># LLMによる要約を格納するフィールド</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="step-4-クローラーspiderの実装">Step 4: クローラー（Spider）の実装</h3>
<p>次に、ボットの心臓部であるクローラーを<code>spiders</code>ディレクトリに作成します。今回は、Confluenceにログインし、特定のスペース内のページを巡回するクローラーを実装します。</p>
<p><strong><code>confluence_crawler/spiders/confluence_spider.py</code></strong></p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">34
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">35
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">36
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">37
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">38
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">39
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">40
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">41
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">42
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">43
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">44
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">45
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">46
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">47
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">48
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">49
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">50
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">51
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">52
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">53
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">54
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">55
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">56
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">57
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">58
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">59
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">60
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">61
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">62
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">63
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">64
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">65
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">66
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">67
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">68
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">69
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">70
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">71
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">72
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">73
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">74
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">75
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">76
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> scrapy
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> confluence_crawler.items <span style="color:#f92672">import</span> ConfluencePageItem
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ConfluenceSpider</span>(scrapy<span style="color:#f92672">.</span>Spider):
</span></span><span style="display:flex;"><span>    name <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;confluence&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># .envファイルなどから設定を読み込むことを推奨</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># ここでは例として直接記述</span>
</span></span><span style="display:flex;"><span>    CONFLUENCE_BASE_URL <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;https://your-company.atlassian.net&#34;</span>
</span></span><span style="display:flex;"><span>    LOGIN_URL <span style="color:#f92672">=</span> <span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">{</span>CONFLUENCE_BASE_URL<span style="color:#e6db74">}</span><span style="color:#e6db74">/login&#34;</span>
</span></span><span style="display:flex;"><span>    START_URL <span style="color:#f92672">=</span> <span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">{</span>CONFLUENCE_BASE_URL<span style="color:#e6db74">}</span><span style="color:#e6db74">/wiki/spaces/YOUR_SPACE_KEY/pages&#34;</span>
</span></span><span style="display:flex;"><span>    USERNAME <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;your-email@example.com&#34;</span>
</span></span><span style="display:flex;"><span>    PASSWORD <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;your-password-or-api-token&#34;</span> <span style="color:#75715e"># APIトークンの使用を推奨</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">start_requests</span>(self):
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># 最初にログインページにアクセス</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">yield</span> scrapy<span style="color:#f92672">.</span>Request(
</span></span><span style="display:flex;"><span>            url<span style="color:#f92672">=</span>self<span style="color:#f92672">.</span>LOGIN_URL,
</span></span><span style="display:flex;"><span>            callback<span style="color:#f92672">=</span>self<span style="color:#f92672">.</span>login,
</span></span><span style="display:flex;"><span>            dont_filter<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">login</span>(self, response):
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># ログインフォームに認証情報をPOST</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># CSRFトークンなどが必要な場合は、responseから抽出して含める</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Confluenceのログイン仕様に合わせて調整が必要</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> scrapy<span style="color:#f92672">.</span>FormRequest<span style="color:#f92672">.</span>from_response(
</span></span><span style="display:flex;"><span>            response,
</span></span><span style="display:flex;"><span>            formdata<span style="color:#f92672">=</span>{
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#34;username&#34;</span>: self<span style="color:#f92672">.</span>USERNAME,
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#34;password&#34;</span>: self<span style="color:#f92672">.</span>PASSWORD
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            callback<span style="color:#f92672">=</span>self<span style="color:#f92672">.</span>after_login
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">after_login</span>(self, response):
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># ログインが成功したかを確認</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># ログイン後のリダイレクト先やページの内容で判断</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#e6db74">&#34;dashboard&#34;</span> <span style="color:#f92672">in</span> response<span style="color:#f92672">.</span>url:
</span></span><span style="display:flex;"><span>            self<span style="color:#f92672">.</span>logger<span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;Login successful!&#34;</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#75715e"># ログイン成功後、目的のページの巡回を開始</span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">yield</span> scrapy<span style="color:#f92672">.</span>Request(url<span style="color:#f92672">=</span>self<span style="color:#f92672">.</span>START_URL, callback<span style="color:#f92672">=</span>self<span style="color:#f92672">.</span>parse_space)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">else</span>:
</span></span><span style="display:flex;"><span>            self<span style="color:#f92672">.</span>logger<span style="color:#f92672">.</span>error(<span style="color:#e6db74">&#34;Login failed!&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">parse_space</span>(self, response):
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># スペース内のページ一覧から各ページへのリンクをたどる</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># CSSセレクタは実際のConfluenceのHTML構造に合わせて調整</span>
</span></span><span style="display:flex;"><span>        page_links <span style="color:#f92672">=</span> response<span style="color:#f92672">.</span>css(<span style="color:#e6db74">&#39;a.content-list-item-title::attr(href)&#39;</span>)<span style="color:#f92672">.</span>getall()
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> link <span style="color:#f92672">in</span> page_links:
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">yield</span> response<span style="color:#f92672">.</span>follow(link, callback<span style="color:#f92672">=</span>self<span style="color:#f92672">.</span>parse_page)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># 「次へ」のリンクがあればたどる（ページネーション対応）</span>
</span></span><span style="display:flex;"><span>        next_page <span style="color:#f92672">=</span> response<span style="color:#f92672">.</span>css(<span style="color:#e6db74">&#39;a.pagination-next::attr(href)&#39;</span>)<span style="color:#f92672">.</span>get()
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> next_page:
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">yield</span> response<span style="color:#f92672">.</span>follow(next_page, callback<span style="color:#f92672">=</span>self<span style="color:#f92672">.</span>parse_space)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">parse_page</span>(self, response):
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># ページから情報を抽出</span>
</span></span><span style="display:flex;"><span>        item <span style="color:#f92672">=</span> ConfluencePageItem()
</span></span><span style="display:flex;"><span>        item[<span style="color:#e6db74">&#39;url&#39;</span>] <span style="color:#f92672">=</span> response<span style="color:#f92672">.</span>url
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># タイトルを抽出</span>
</span></span><span style="display:flex;"><span>        item[<span style="color:#e6db74">&#39;title&#39;</span>] <span style="color:#f92672">=</span> response<span style="color:#f92672">.</span>css(<span style="color:#e6db74">&#39;h1#title-text a::text&#39;</span>)<span style="color:#f92672">.</span>get() <span style="color:#f92672">or</span> \
</span></span><span style="display:flex;"><span>                        response<span style="color:#f92672">.</span>css(<span style="color:#e6db74">&#39;h1#title-text::text&#39;</span>)<span style="color:#f92672">.</span>get()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># 本文を抽出（メインコンテンツエリアを指定）</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># このセレクタが最も重要で、サイトの構造に合わせて調整が必要</span>
</span></span><span style="display:flex;"><span>        content_html <span style="color:#f92672">=</span> response<span style="color:#f92672">.</span>css(<span style="color:#e6db74">&#39;div#main-content&#39;</span>)<span style="color:#f92672">.</span>get()
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># HTMLタグを除去してプレーンテキストにする</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">from</span> w3lib.html <span style="color:#f92672">import</span> remove_tags
</span></span><span style="display:flex;"><span>        item[<span style="color:#e6db74">&#39;content&#39;</span>] <span style="color:#f92672">=</span> remove_tags(content_html)<span style="color:#f92672">.</span>strip()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Itemをパイプラインに渡す</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">yield</span> item
</span></span></code></pre></td></tr></table>
</div>
</div><p><strong>【注意点】</strong></p>
<ul>
<li><strong>認証</strong>: 上記は基本的なフォーム認証の例です。実際のConfluenceのバージョンや設定（SSOなど）によっては、より複雑な処理（APIトークンの利用、Cookieの手動設定など）が必要になります。</li>
<li><strong>CSSセレクタ</strong>: ConfluenceのHTML構造はバージョンによって変わる可能性があります。ブラウザの開発者ツールを使い、実際のページの構造を確認してセレクタを調整してください。これがクローリングの肝となります。</li>
</ul>
<h3 id="step-5-llm要約パイプラインの実装">Step 5: LLM要約パイプラインの実装</h3>
<p>ここがOpenClawの真骨頂です。Spiderから渡されたItemを受け取り、LLMを使って要約処理を行うパイプラインを<code>pipelines.py</code>に記述します。</p>
<p>まず、OpenAIのライブラリをインストールします。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pip install openai
</span></span></code></pre></td></tr></table>
</div>
</div><p>次に、プロジェクトのルートに<code>.env</code>ファイルを作成し、APIキーを記述します。</p>
<p><strong><code>.env</code></strong></p>
<pre tabindex="0"><code>OPENAI_API_KEY=&#34;sk-...&#34;
</code></pre><p>そして、パイプラインを実装します。</p>
<p><strong><code>confluence_crawler/pipelines.py</code></strong></p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">34
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">35
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">36
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">37
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">38
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">39
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">40
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">41
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">42
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">43
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">44
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">45
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">46
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">47
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">48
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">49
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">50
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">51
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">52
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">53
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">54
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">55
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">56
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">57
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">58
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">59
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">60
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">61
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">62
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">63
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">64
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">65
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> os
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> dotenv <span style="color:#f92672">import</span> load_dotenv
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> openai <span style="color:#f92672">import</span> OpenAI
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> itemadapter <span style="color:#f92672">import</span> ItemAdapter
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># .envファイルから環境変数を読み込む</span>
</span></span><span style="display:flex;"><span>load_dotenv()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">LlmSummaryPipeline</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">open_spider</span>(self, spider):
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Spider開始時に呼び出される</span>
</span></span><span style="display:flex;"><span>        api_key <span style="color:#f92672">=</span> os<span style="color:#f92672">.</span>getenv(<span style="color:#e6db74">&#34;OPENAI_API_KEY&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> api_key:
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">raise</span> <span style="color:#a6e22e">ValueError</span>(<span style="color:#e6db74">&#34;OPENAI_API_KEY is not set in .env file&#34;</span>)
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>client <span style="color:#f92672">=</span> OpenAI(api_key<span style="color:#f92672">=</span>api_key)
</span></span><span style="display:flex;"><span>        spider<span style="color:#f92672">.</span>logger<span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;LLM Summary Pipeline is enabled.&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">close_spider</span>(self, spider):
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Spider終了時に呼び出される</span>
</span></span><span style="display:flex;"><span>        spider<span style="color:#f92672">.</span>logger<span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;LLM Summary Pipeline is finished.&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">process_item</span>(self, item, spider):
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># 各Itemがパイプラインを通過するたびに呼び出される</span>
</span></span><span style="display:flex;"><span>        adapter <span style="color:#f92672">=</span> ItemAdapter(item)
</span></span><span style="display:flex;"><span>        content <span style="color:#f92672">=</span> adapter<span style="color:#f92672">.</span>get(<span style="color:#e6db74">&#39;content&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> content:
</span></span><span style="display:flex;"><span>            adapter[<span style="color:#e6db74">&#39;summary&#39;</span>] <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;Content is empty.&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> item
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># 長すぎるコンテンツはLLMに渡す前に切り詰める</span>
</span></span><span style="display:flex;"><span>        max_tokens_for_content <span style="color:#f92672">=</span> <span style="color:#ae81ff">8000</span>  <span style="color:#75715e"># モデルのコンテキスト長に応じて調整</span>
</span></span><span style="display:flex;"><span>        truncated_content <span style="color:#f92672">=</span> content[:max_tokens_for_content]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">try</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#75715e"># LLMに要約を依頼するプロンプト</span>
</span></span><span style="display:flex;"><span>            prompt <span style="color:#f92672">=</span> <span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            以下の社内Wikiのページ内容を、3つの箇条書きで簡潔に要約してください。
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            このページが「誰の」「どんな課題を解決する」ためのドキュメントなのかが明確にわかるようにしてください。
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            ---
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            </span><span style="color:#e6db74">{</span>truncated_content<span style="color:#e6db74">}</span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            ---
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            &#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>            
</span></span><span style="display:flex;"><span>            response <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>client<span style="color:#f92672">.</span>chat<span style="color:#f92672">.</span>completions<span style="color:#f92672">.</span>create(
</span></span><span style="display:flex;"><span>                model<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;gpt-4o&#34;</span>,  <span style="color:#75715e"># または &#34;gpt-3.5-turbo&#34; など</span>
</span></span><span style="display:flex;"><span>                messages<span style="color:#f92672">=</span>[
</span></span><span style="display:flex;"><span>                    {<span style="color:#e6db74">&#34;role&#34;</span>: <span style="color:#e6db74">&#34;system&#34;</span>, <span style="color:#e6db74">&#34;content&#34;</span>: <span style="color:#e6db74">&#34;あなたは優秀なアシスタントです。渡された文章を的確に要約します。&#34;</span>},
</span></span><span style="display:flex;"><span>                    {<span style="color:#e6db74">&#34;role&#34;</span>: <span style="color:#e6db74">&#34;user&#34;</span>, <span style="color:#e6db74">&#34;content&#34;</span>: prompt}
</span></span><span style="display:flex;"><span>                ],
</span></span><span style="display:flex;"><span>                temperature<span style="color:#f92672">=</span><span style="color:#ae81ff">0.3</span>,
</span></span><span style="display:flex;"><span>                max_tokens<span style="color:#f92672">=</span><span style="color:#ae81ff">500</span>
</span></span><span style="display:flex;"><span>            )
</span></span><span style="display:flex;"><span>            
</span></span><span style="display:flex;"><span>            summary <span style="color:#f92672">=</span> response<span style="color:#f92672">.</span>choices[<span style="color:#ae81ff">0</span>]<span style="color:#f92672">.</span>message<span style="color:#f92672">.</span>content<span style="color:#f92672">.</span>strip()
</span></span><span style="display:flex;"><span>            adapter[<span style="color:#e6db74">&#39;summary&#39;</span>] <span style="color:#f92672">=</span> summary
</span></span><span style="display:flex;"><span>            spider<span style="color:#f92672">.</span>logger<span style="color:#f92672">.</span>info(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;Successfully summarized: </span><span style="color:#e6db74">{</span>adapter<span style="color:#f92672">.</span>get(<span style="color:#e6db74">&#39;title&#39;</span>)<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">except</span> <span style="color:#a6e22e">Exception</span> <span style="color:#66d9ef">as</span> e:
</span></span><span style="display:flex;"><span>            adapter[<span style="color:#e6db74">&#39;summary&#39;</span>] <span style="color:#f92672">=</span> <span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;Failed to generate summary: </span><span style="color:#e6db74">{</span>e<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>            spider<span style="color:#f92672">.</span>logger<span style="color:#f92672">.</span>error(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;Error during summarization for </span><span style="color:#e6db74">{</span>adapter<span style="color:#f92672">.</span>get(<span style="color:#e6db74">&#39;url&#39;</span>)<span style="color:#e6db74">}</span><span style="color:#e6db74">: </span><span style="color:#e6db74">{</span>e<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># 次のパイプライン（または出力）にItemを渡す</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> item
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="step-6-設定の有効化">Step 6: 設定の有効化</h3>
<p>最後に、作成したパイプラインと、結果をJSONファイルに出力する設定を<code>settings.py</code>で有効にします。</p>
<p><strong><code>confluence_crawler/settings.py</code></strong></p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># ... (他の設定) ...</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 作成したパイプラインを有効にする</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 数値が小さいほど先に実行される</span>
</span></span><span style="display:flex;"><span>ITEM_PIPELINES <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>   <span style="color:#e6db74">&#39;confluence_crawler.pipelines.LlmSummaryPipeline&#39;</span>: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 結果をJSONファイルに出力する設定</span>
</span></span><span style="display:flex;"><span>FEEDS <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;output.json&#39;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;format&#39;</span>: <span style="color:#e6db74">&#39;json&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;encoding&#39;</span>: <span style="color:#e6db74">&#39;utf8&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;store_empty&#39;</span>: <span style="color:#66d9ef">False</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;fields&#39;</span>: [<span style="color:#e6db74">&#39;title&#39;</span>, <span style="color:#e6db74">&#39;url&#39;</span>, <span style="color:#e6db74">&#39;summary&#39;</span>], <span style="color:#75715e"># 出力するフィールドを指定</span>
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;indent&#39;</span>: <span style="color:#ae81ff">4</span>,
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># クローリングの挙動に関する設定</span>
</span></span><span style="display:flex;"><span>ROBOTSTXT_OBEY <span style="color:#f92672">=</span> <span style="color:#66d9ef">False</span> <span style="color:#75715e"># 社内Wikiなのでrobots.txtは無視</span>
</span></span><span style="display:flex;"><span>DOWNLOAD_DELAY <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span> <span style="color:#75715e"># サーバーに負荷をかけないように1秒待つ</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># ... (他の設定) ...</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="step-7-実行と結果の確認">Step 7: 実行と結果の確認</h3>
<p>すべての準備が整いました。プロジェクトのルートディレクトリで以下のコマンドを実行します。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>claw crawl confluence
</span></span></code></pre></td></tr></table>
</div>
</div><p>クローラーが動き出し、ログイン、ページ巡回、情報抽出、LLMによる要約、ファイルへの保存が順次実行されます。ログに進行状況が表示されるので、エラーが出ていないか確認しましょう。</p>
<p>処理が完了すると、<code>output.json</code>というファイルが生成されているはずです。中身を見てみましょう。</p>
<p><strong><code>output.json</code>（出力例）</strong></p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>[
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;title&#34;</span>: <span style="color:#e6db74">&#34;【設計書】〇〇機能のAPI仕様&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;url&#34;</span>: <span style="color:#e6db74">&#34;https://your-company.atlassian.net/wiki/spaces/DEV/pages/12345/API&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;summary&#34;</span>: <span style="color:#e6db74">&#34;・このドキュメントは、〇〇機能のバックエンドAPIを利用するクライアント開発者を対象としています。\n・APIのエンドポイント一覧、リクエスト/レスポンスのフォーマット、認証方法について詳細に解説しています。\n・クライアント実装時に発生しがちなエラーコードとその対処法も網羅しており、開発効率の向上を目的としています。&#34;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;title&#34;</span>: <span style="color:#e6db74">&#34;新メンバー向けオンボーディングガイド&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;url&#34;</span>: <span style="color:#e6db74">&#34;https://your-company.atlassian.net/wiki/spaces/DEV/pages/67890/&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;summary&#34;</span>: <span style="color:#e6db74">&#34;・このガイドは、新しく開発チームに参加したメンバーがスムーズに業務を開始できるようにするためのものです。\n・開発環境のセットアップ手順、主要なリポジトリへのアクセス方法、チームのコミュニケーションルールについて説明しています。\n・最初の1週間で取り組むべきタスクリストが提示されており、早期の戦力化をサポートします。&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>]
</span></span></code></pre></td></tr></table>
</div>
</div><p>このように、社内Wikiの情報が構造化され、かつLLMによって要点がまとめられたデータセットを自動で作成することができました。</p>
<h2 id="メリットとデメリットあるいは他ツールとの比較">メリットとデメリット（あるいは他ツールとの比較）</h2>
<p>OpenClawを使ったこのアプローチには、いくつかのメリットとデメリットがあります。</p>
<h3 id="メリット">メリット</h3>
<ul>
<li><strong>柔軟性と拡張性の高さ</strong>: Scrapyベースであるため、複雑なログイン処理、JavaScriptで動的に生成されるコンテンツへの対応（Playwright連携など）、巡回ルールの細かなカスタマイズなど、非常に柔軟なクローリングが可能です。</li>
<li><strong>LLM連携の容易さ</strong>: パイプライン機構により、クローリング処理とLLMによるデータ処理をきれいに分離して実装できます。要約だけでなく、感情分析、エンティティ抽出、翻訳など、様々なLLMタスクを簡単に追加できます。</li>
<li><strong>非構造化データに強い</strong>: Webページという典型的な非構造化データをインプットとし、LLMの力で構造化データ（JSONなど）に変換できる点が最大の強みです。</li>
<li><strong>既存の資産を活用可能</strong>: 豊富なScrapyのドキュメントやコミュニティの知見をそのまま活用できます。</li>
</ul>
<h3 id="デメリット">デメリット</h3>
<ul>
<li><strong>学習コスト</strong>: ScrapyやWebクローリングの基本的な知識（HTTP、HTML、CSSセレクタなど）が必要です。全くの初心者には少しハードルが高いかもしれません。</li>
<li><strong>LLMのAPIコスト</strong>: 処理するページ数やテキスト量に比例して、LLMのAPI利用料が発生します。大規模なWikiを対象にする場合は、コストの見積もりが重要になります。</li>
<li><strong>メンテナンスコスト</strong>: クローリング対象のWebサイト（社内Wiki）のUIが変更されると、CSSセレクタなどを修正する必要があります。定期的なメンテナンスが不可欠です。</li>
</ul>
<h3 id="他ツールとの比較">他ツールとの比較</h3>
<table>
  <thead>
      <tr>
          <th style="text-align: left">ツール/アプローチ</th>
          <th style="text-align: left">メリット</th>
          <th style="text-align: left">デメリット</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left"><strong>OpenClaw (本記事)</strong></td>
          <td style="text-align: left">柔軟なクローリングとLLM連携のバランスが良い。拡張性が高い。</td>
          <td style="text-align: left">Scrapyの学習コスト。APIコスト。メンテナンスコスト。</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>自前で実装 (Requests+BS4+LangChain)</strong></td>
          <td style="text-align: left">自由度が最も高い。</td>
          <td style="text-align: left">非同期処理、エラーハンドリング、パイプライン等をすべて自作する必要があり、開発・保守コストが高い。</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>RAG特化フレームワーク (LlamaIndex等)</strong></td>
          <td style="text-align: left">データソースコネクタが豊富。RAG構築に特化していて手軽。</td>
          <td style="text-align: left">クローリングのカスタマイズ性が限定的。複雑な認証やサイト構造に対応しきれない場合がある。</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>ノーコード/ローコードツール</strong></td>
          <td style="text-align: left">実装が非常に簡単で迅速。</td>
          <td style="text-align: left">細かい制御が難しい。対応しているデータソースや処理が限られる。ブラックボックス化しやすい。</td>
      </tr>
  </tbody>
</table>
<p>私たちのケースでは、社内WikiがSSO認証を導入しており、クローリングの際に特殊なヘッダー情報を付与する必要があったため、柔軟にリクエストをカスタマイズできるOpenClawが最適でした。</p>
<h2 id="現場で使える実践的なtips">現場で使える実践的なTips</h2>
<p>基本的な実装に加えて、より実用性を高めるためのTipsをいくつかご紹介します。</p>
<h3 id="1-差分クローリングで効率化とコスト削減">1. 差分クローリングで効率化とコスト削減</h3>
<p>毎回すべてのページをクロールするのは非効率で、LLMのコストもかさみます。ページの最終更新日時を取得し、前回クロールした日時以降に更新されたページのみを処理対象とすることで、大幅な効率化が可能です。</p>
<ul>
<li>Confluence APIなどを利用して最終更新日時を取得する。</li>
<li>Spiderの<code>parse_page</code>メソッド内で、更新日時をチェックし、古ければ<code>yield</code>しないロジックを追加する。</li>
<li>前回の実行日時をファイルやデータベースに保存しておく。</li>
</ul>
<h3 id="2-プロンプトエンジニアリングで要約の質を向上">2. プロンプトエンジニアリングで要約の質を向上</h3>
<p>LLMによる要約の質はプロンプトに大きく依存します。目的に合わせてプロンプトを工夫しましょう。</p>
<p><strong>悪い例:</strong>
<code>「以下の文章を要約して。」</code></p>
<p><strong>良い例:</strong></p>
<pre tabindex="0"><code>あなたは〇〇チームのテックリードです。
以下のドキュメントは、新しくチームに参加したメンバーが読むことを想定しています。
このドキュメントの【目的】、【主要な技術要素】、【注意すべき点】の3つの観点で、箇条書きで分かりやすく要約してください。
---
{本文テキスト}
---
</code></pre><p>役割（Role）、対象読者、出力形式などを具体的に指定することで、より意図に沿った質の高い要約が得られます。</p>
<h3 id="3-収集した情報の活用法slackボット連携">3. 収集した情報の活用法：Slackボット連携</h3>
<p>収集してJSONにまとめたデータは、そのままでは宝の持ち腐れです。これを活用する最も効果的な方法の一つが、Slackボットとの連携です。</p>
<ol>
<li><strong>データのインデックス化</strong>: 生成された<code>output.json</code>を、Elasticsearchやベクトルデータベース（Chroma, Pineconeなど）に登録します。</li>
<li><strong>Slackボットの作成</strong>: SlackのBoltフレームワークなどを使って、ユーザーからの質問を受け付けるボットを作成します。</li>
<li><strong>検索と回答</strong>: ユーザーがSlackで「〇〇機能の仕様について教えて」と質問すると、ボットはそのキーワードでデータベースを検索します。</li>
<li><strong>結果の提示</strong>: 検索にヒットしたページの要約とURLをSlackに投稿します。これにより、チームメンバーはSlackを離れることなく、必要な情報に迅速にアクセスできるようになります。</li>
</ol>
<h3 id="4-定期実行による情報の鮮度維持">4. 定期実行による情報の鮮度維持</h3>
<p>作成したクローラーは、GitHub ActionsやJenkins、あるいはサーバーのcronなどを使って定期的に（例えば毎晩）実行するようにしましょう。これにより、社内ナレッジベースを常に最新の状態に保つことができます。</p>
<h2 id="まとめ">まとめ</h2>
<p>本記事では、情報のサイロ化という多くの組織が抱える課題に対し、OpenClawを用いて社内Wikiを自動巡回し、LLMで情報を要約・構造化するボットを構築した実例をご紹介しました。</p>
<p>このアプローチにより、私たちは以下の成果を得ることができました。</p>
<ul>
<li><strong>情報検索時間の大幅な短縮</strong>: 複数のツールを横断して探す手間がなくなり、Slackボットを通じて数秒で関連情報にたどり着けるようになった。</li>
<li><strong>ナレッジの可視化と再利用</strong>: 埋もれていた過去のドキュメントが掘り起こされ、チーム全体の知識資産として活用されるようになった。</li>
<li><strong>新メンバーのオンボーディング効率化</strong>: 新メンバーが自律的に情報をキャッチアップしやすくなった。</li>
</ul>
<p>OpenClawは、柔軟なWebクローリング能力と強力なLLMによるデータ処理能力を兼ね備えた、非常にポテンシャルの高いフレームワークです。今回ご紹介した社内Wikiの巡回だけでなく、競合他社のWebサイト分析、業界ニュースの自動収集と要約、採用サイトからの情報抽出など、その応用範囲は無限大です。</p>
<p>情報過多の時代において、必要な情報を効率的に収集・整理・活用する能力は、個人にとっても組織にとっても重要な競争力となります。この記事が、皆さんの組織のナレッジマネジメントを一段階進化させるための一助となれば、これほど嬉しいことはありません。ぜひ、お使いの社内ツールで試してみてはいかがでしょうか。</p>
]]></content:encoded>
      <category>Case Study</category>
      <category>OpenClaw</category>
      <category>Automation</category>
      <category>Knowledge Management</category>
    </item>
  </channel>
</rss>
