FirecrawlとDifyで簡単スクレイピング

![画像.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/013f5724-64db-97f0-b763-899e07c76896.jpeg)
娘の部活のユニフォームのタグに書いてました。


# はじめに
前回の記事では、**Firecrawl**を使用したプログラミングをPythonで実装し、Webブラウザのコンテンツをテキストファイルに出力する方法を試してみました。

今回の記事では、**Dify**のワークフローを活用し、プログラミングをせずに結果を表示させる方法に挑戦します。これまでの投稿では、ローカルLLMである**Ollama**を**Dify**に接続してチャットを試したことがありますが、ワークフローの作成は今回が初めてです。どれほど簡単に実現できるのかを検証してみます。

https://qiita.com/ogi_kimura/items/d13631b3a77e18023ef9

# Difyの導入方法について  
今回は、ローカルPC上で構築する**Dify**を利用します。  
導入手順については、以下の記事で詳しく説明していますので、ぜひご覧ください。  

もしご不明点がありましたら、お気軽にお問い合わせください。  

https://qiita.com/ogi_kimura/items/d13631b3a77e18023ef9



# Firecrawlの導入方法について
今回は、**Firecrawl**を使用してWebサイトのスクレイピングを行います。
APIキーの取得方法など、導入に必要な手順は以下の記事に詳しく記載していますので、ご確認ください。

不明点がございましたら、どうぞお気軽にお問い合わせください。

https://qiita.com/ogi_kimura/items/5800af71691737848c92



# ワークフローを作ってみる
今回は、**Dify**を使ったワークフロー作成に挑戦します。
2種類のワークフローを作成し、それぞれを比較してみる予定です。

1つ目は「ナレッジを登録する方法」、2つ目は「直接Firecrawlを使ってスクレイピングする方法」です。
この記事では、まず「ナレッジを登録する方法」について、手順を追って詳しく解説します。

## 1.LLMの接続
はじめに、**Dify**に**LLM**を接続する必要があります。
今回は、**OpenAI**を**LLM**として利用します。
設定手順については、以前ご紹介した以下の記事の「APIキーの設定」を参考にしてください。

https://qiita.com/ogi_kimura/items/d13631b3a77e18023ef9


## 2.ナレッジ作成
次に、**Firecrawl**を使用した「ナレッジ」を準備します。ここでいう「ナレッジ」とは、文章やコンテンツを一定のサイズで細かく分割（チャンク化）し、それをデータベースに保存したものを指します。個人的には、**RAG**（Retrieval-Augmented Generation）に近い仕組みではないかと考えています。処理の際には、この「ナレッジ」から必要な情報を取得してLLMに渡し、最終的に結果を出力します。

今回は、私のホームページサイト（`https://www.npwitys.com`）をスクレイピングしてナレッジを作成してみます。

**Dify**を起動してログイン後、画面上部の「ナレッジ」ボタンをクリックすると、以下のような画面が表示されます。画面左側にある「ナレッジを作成」をクリックして進めます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/b9c220c5-e0bc-1781-5f4e-9c74ad404566.png)


すると、次のような画面が表示されるため、「ウェブサイトから同期」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/0c659794-ddf2-fd8b-a97b-ae60000e9b50.png)

「プロバイダーを選択する」が「Firecrawl」になっていることを確認してください。
次に、URLを入力するテキストボックスに、スクレイピング対象のURLを入力します。

画面下部にはいくつかのオプションがあります。それぞれの意味は以下の通りです。

・**制約**：読み込むページ数を指定します。
・**最大深度**：どの階層までスクレイピングするかを指定します。
・**パスを除外するおよびパスのみを含める**：正規表現を使用して指定します。

すべての入力が完了したら、「実行」をクリックして処理を開始します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/5878c5ce-665a-2039-0318-9636dc52585b.png)

処理には少し時間がかかる場合もあるのですが、画面上部の「ナレッジ」をクリックすると、作成された「ナレッジ」が一覧表示されます。
タイルにカーソルを合わせると右下に「...」ボタンが表示されるので、それをクリックし、「設定」を選択することで「ナレッジ」の名前を変更できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/d00f9801-1d21-e720-95d9-20d070099664.png)

さらに、タイルをクリックすると、以下のようなページの一覧が表示されます。
各ページのURLが確認できることがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/f5b1c55a-c1b8-4a9c-9193-59d498a2ab10.png)

一番上のURLをクリックすると、さらに文章がチャンク化されていることが確認できます。
このようにして「ナレッジ」が生成されていることがわかりました。実際に確認することで、「ナレッジ」の内容をより深く理解することができました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/7624698c-09a7-ce49-def6-9cc89cca4aaf.png)

これで「ナレッジ」は完成です。




## 3.ワークフロー作成
次はワークフローを作成します。

a)「ブロック」の追加
b)「開始ブロック」の設定
c)「知識取得ブロック」の設定
d)「LLMブロック」の設定
e)「終了ブロック」の設定

### a)「ブロック」の追加
**Dify**の上部にある「スタジオ」をクリックしてください。
すると、左側に「最初から作成」というオプションが表示されるので、それをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/29b3607f-0ea1-1b03-3ebc-c9d15f57fc28.png)


下図のようなダイアログが表示されるので、「ワークフロー」をクリックします。
次に、「アプリのアイコンと名前」の部分にワークフローの名前を入力し、最後に「作成する」ボタンをクリックしてワークフローを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/b3fc2e61-e4de-fda8-062e-756867f68a07.png)

以下が初期状態のワークフローです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/4b8bf096-565f-1c4a-cca8-b912a44a0ec6.png)

この「ワークフロー」に「ブロック」を追加し、それらを紐づけていく作業を行います。
最初に戸惑ったのは、画面中央の検索リストが非表示になった後に「ブロック」を追加したい場合、どのように操作すればよいかがわからなかった点です。「ブロック追加」のボタンが見当たらなかったためです。
しかし、キャンバス上で右クリックすると、以下のダイアログが表示されます。このダイアログから「ブロック」を追加することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/9c06e944-af4b-a623-101b-ed810c70c53c.png)

### b) 「開始ブロック」の設定
今回は「ブロック」として、「LLM」「知識取得」「終了」の3つを追加します。既に「開始ブロック」は用意されているため、合計4つの「ブロック」になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/bf78723f-19ff-6be7-d81d-01f1c509f13c.png)

下図はすべてのブロックを追加した状態です。とりあえず、すべてのブロックを繋げてみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/35b9a64d-0ace-343e-a08a-4441ea076940.png)

まず、「開始ブロック」を選択すると、右側に詳細内容が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/cfe68d34-1ac3-b26c-ae47-7a5a7e82166c.png)


次に、「知識取得」ブロックでは、クエリをインプットとして使用します。そのため、最初に「開始ブロック」でクエリを入力させる設定を行います。
「入力フィールド」の「+」ボタンをクリックすると、以下のダイアログが表示されます。
「短文」を選択し、「変数名」「ラベル名」「最大長」を入力してから、「保存」をクリックします。
これで、ワークフロー開始時に短文の入力が求められるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/c3636e6e-8107-3fa1-97cc-fecd207573fa.png)


### c) 「知識取得ブロック」の設定
次に、「知識取得ブロック」を設定します。ここでは、先ほど作成した**Firecrawl**の「ナレッジ」を接続します。
「クエリ変数」には、前の「開始ブロック」で設定した「query」を指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/dd6d4655-cba3-dfa0-67f1-eac980e2f37d.png)

右側の「ナレッジ」のさらに右にある「+」ボタンをクリックします。
すると、「参照する知識を選択」というダイアログが表示されるので、先ほど「ナレッジ」で作成したもの（ここでは「日本をITでよくする」）を選択し、「追加」ボタンをクリックします。

「知識取得ブロック」の出力は`result`に設定されています。右側の「出力変数」の「∨」ボタンをクリックすると、出力変数の詳細情報が確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/c42e340e-f324-d9f5-0de7-30bd08c66c8f.png)


### d) 「LLMブロック」の設定
次に「LLMブロック」の設定をします。
まずは、右上の「モデル」を設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/4486a787-997e-997f-3ec1-0862a582f548.png)

いくつか選択肢がありますが、最も安価な`gpt-4o-mini`を選ぶことにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/60c232a0-2ac0-5685-af58-1dc8536f57ca.png)

次に「コンテキスト」を設定します。
コンテキストは、**LLM**に渡したい文章やコンテンツです。今回は、「知識取得ブロック」のアウトプット（`result`）をコンテキストとして使用します。ただし、コンテキストの設定は必須ではなく、変数を直接「メッセージ」（`SYSTEM`や`USER`）に埋め込むこともできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/ded02ceb-58d9-8597-eba2-1dbc7d851390.png)

次に「メッセージ」を入力します。
`SYSTEM`メッセージとして「あなたはとても優秀なゴーストライターです。」を設定し、`USER`メッセージには「次のコンテキストを基に、このホームページの概要を３つの箇条書きで回答してください。」を入力しました。
`USER`メッセージの最後に「コンテキスト」を登録しています。`USER`の右側にある「[x]」ボタンをクリックし、「コンテキスト」を選択することで登録できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/0f6c882b-3607-f9c7-c532-b09bb1c545af.png)

以下は、「[x]」ボタンをクリックした際の状態です。リストに「コンテキスト」が追加されているのが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/e060f12f-f1e7-d0d0-aa9d-57400f1ae009.png)

### e) 「終了ブロック」の設定
最後に、「終了ブロック」を設定します。
下図のように、「出力変数」の「+」ボタンをクリックするとテキストボックスが表示されるので、変数名と変数設定を行います。今回は、「変数名｝を`answer`と設定し、「変数設定」には「LLMブロック」の`text`を指定します。この`text`は、**LLM**の回答結果を表します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/34c17437-8682-ae0c-0a83-b72caf92863a.png)

これで、「ブロック」の設定は一通り完了しました。




# 処理実行
それでは、処理を実行してみましょう。
右上の「▷実行」ボタンをクリックします。すると、下図のように入力を求められますので、「クエリー入力」に条件を入力します。今回は、`"includePaths": ["/blog/*", "/products/*"] `として、ブログや製品ページに関する情報を対象とします。設定が完了したら、「実行を開始」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/b4b8d32c-a6f9-6c2c-da40-e85da85a709a.png)

処理が進行している様子を動画で紹介します。
「終了ブロック」に到達すると、右側の「結果」に文章が表示され、3つの箇条書きが整然と出力されていることが確認できます。
![Animation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/8d11f0fc-bf2b-9b8e-0820-f344c6a6c4d2.gif)

これが結果です。ん？なんかありきたりな情報ですな...
この**Dify**はとても優秀で、各「ブロック」の入力と出力が見れるのと、途中の「ブロック」(例えば「LLMブロック」や「知識取得ブロック」）だけで単体テストもできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/93b4f29c-a05a-d0f1-cb16-cbcae0cd256e.png)

結果を確認したところ、少し一般的な情報が出力されていることに気づきました。
**Dify**はとても優秀で、各「ブロック」の入力と出力が確認できるため、途中の「ブロック」(例えば「LLMブロック」や「知識取得ブロック」)の単体テストも簡単に行えます。
右側の「トレース」タブをクリックし、「LLM」を開いてみましょう。
「LLMブロック」の入力が**URL***になっていることがわかります。これでは、**LLM**がありきたりな回答しかできないのは仕方ありません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/91bf4a36-fba5-1287-cd39-955e7064b658.png)

では、その手前の「LLMブロック」の出力を確認してみると、しっかりと出力されています。
どうやら、**Dify**の仕様上、情報がうまく渡せない可能性があるようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/e4d6ebd0-c917-9caa-db8e-ff8c51bc34f0.png)

解決策を探るため、追加の「ブロック」を使って調べてみました。
「テンプレートブロック」がうまくいきそうだと思い、「知識取得ブロック」と「LLMブロック」の間に配置しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/a2e2342c-3b6f-cecf-8dde-2594ae3b1a55.png)

「テンプレートブロック」の入力を「知識取得」のアウトプットに設定し、
次に「LLMブロック」のインプットを「テンプレートブロック」のoutputに指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/34ae6542-8ae5-8a5e-ebbd-cb6063e6f70f.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/7d49ce03-84c2-4ba5-2761-dc08a4505c6a.png)


再度処理を実行してみると、ホームページの内容に基づいた回答が得られました。
「LLMブロック」の「入力」も適切に渡されていることが確認できます。
![Animation2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/61ad5517-f904-392c-0263-3d723b75d1cc.gif)
今度は、ホームページの内容に即した回答がされています。「LLMブロック」の「入力」もちゃんと引き渡されているようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/1e9aae68-2fe8-2727-58df-563b12d76cb6.png)

このように、デバッグを繰り返しながら「ワークフロー」を調整することで、効果的に結果を得ることができました。



# おわりに
今回は、「ナレッジを登録する方法」について、Difyの操作方法を紹介しました。
「直接Firecrawlでスクレイピングする方法」については、次回の記事で説明し、両者の違いを比較してみたいと思います。

最後までご覧いただき、ありがとうございました。