Flask/Python3からApache Solrに問い合わせるには

さて、ここまでApache Solrへの問い合わせの仕方を色々とみてきました。検索結果はJSON形式だったりCSV形式だったり、いくつか選ぶことはできるものの、人に優しい感じではありません。 しかしながら、コンピュータにはとても優しいもので、ということは、コンピュータを介して人に優しくさせることはできるはずです。

やり方としては無数にあります。個人的にはJavascriptでJSONを読み込んでWebブラウザに表示してしまうのが好きなのですが、今回、「みんなで翻刻」の検索システムで使ったFlask/Python3の場合のやり方を少しみてみましょう。

Flask/Python3の導入・設定・基本的な書き方は、あちこちのサイトをご覧ください。一通りできるようになった上での話になりますが、

  1. Flask/Python3からApache Solrに問い合わせる
  2. Apache Solrから返戻されたデータを受け取る
  3. 受け取ったデータを適宜整形する

といったことができるようになるといいですね。以下に、そのやり方について少し見てみましょう。

1. Flask/Python3からApache Solrに問い合わせる

この問い合わせは、要するにFlask/Python3で問い合わせ用URLを作って、それをApache Solrに投げればいいだけなのですが、 ここでいくつか注意点があります。

1-a. XSS脆弱性を作らないようにすること

「検索」は、不特定多数のユーザが送ってきた文字列を検索結果としてWeb頁に表示することになります。そこで、 変なJavascript込みの検索文字列が送られてきたときに、それをそのまま表示してしまうと、Javascriptをその検索サイト 上で実行されてしまうことになります。flaskではテンプレートエンジンjinja2でそこら辺をうまく処理してくれている ようですが、よく注意するようにしてください。

1-b. 検索文字列のエンコード

Apache Solrに対してURLで検索問い合わせを行う場合、マルチバイト文字列(要するに日本語など)を送出しても うまく処理してくれません。これはURLエンコードしておく必要があります。一部の制御文字も同様です。たとえば前回記事で登場した以下のURL

curl 'http://127.0.0.1:8983/solr/minhon3/select?q=textConn%3A%22%E5%AF%8C%E5%B1%B1%22&facet.field=entry&facet=on&fl=entry&rows=10&facet.sort=count'

のうちの「q=textConn%3A%22%E5%AF%8C%E5%B1%B1%22」の部分に注目してください。ここでは「:」が「%3A」に置き換えられていて、次に「"」が 「%22」に置き換えられ、その後、「富山」が「%E5%AF%8C%E5%B1%B1」に置き換えられています。検索問い合わせ用URLを作る際には このようにしてURLエンコードしなければなりません。(そうしないと、単にエラーが返ってきます)。

このための対策はpython3では比較的簡単です。 urllib.parse.quote()という関数がありますので、これを使って変換してやればいいだけです。もちろん、urllibモジュールのインポートも忘れてはいけません。

そうすると、たとえばAND 検索をしたい場合には、ANDの前後に空白を入れる必要がありますので、空白記号をURLエンコードして「'%20AND%20」という風にする必要がありますね。 この点もちょっと注意が必要ですが、どのタイミングでURLエンコードすればうまくいくのか、適宜試してみてください。

検索文字列がうまくできたら、次はurllib.request.urlopen(()で問い合わせして、返戻された結果を変数に入れておきましょう。たとえば以下のような感じですね。

base_url = 'http://127.0.0.1:8983/solr/minhon3/select?q='
query_fld = 'textConn%3A'
query_opt = '&facet.field=entry&facet=on&fl=entry&rows=10&facet.sort=count'
query_key = urllib.parse.quote('富山')

query_url = base_url +query_fld+query_key+query_opt

result = urllib.request.urlopen(query_url)

2. Apache Solrから返戻されたデータを受け取る

さて、次に、返戻されたデータの受け取りです。これはJSON形式で戻ってきますので、これをPythonのlist/dictの形式に変換できると非常に便利です。これは以下のようにすればOKです。

array_body = json.loads(result.read().decode('utf-8'))

そうすると、array_body の中に検索結果のJSONデータがすべて入りますので、あとはpython3でのlist/dictからデータを取り出す方法で必要なデータを取り出すことができます。 とりあえず print (array_body) してみて、どういう風にして欲しいデータを取り出すか考えてみてください。

なお、一つだけヒントを出しておくと、以下のようにすることで、docs以下のリストに入っている各dictを取り出して変数に入れることができます。

   for edoc in array_body['response']['docs']:
      obj_id = edoc['id']
      ent = edoc['entry']
      imgf = edoc['imageID']
      imgt = edoc['imageThumb']
      ent_id = edoc['entryID']

あとは、取り出したものをHTMLタグにうまく貼り込めればいいのですが、つまり、テンプレートにうまく渡せるようにすればいい、ということですね。