発想励起的道具(エクスカーションの足しに)

創造工学ネタって全然ネットに転がってないのね、ちょっとびっくり。

うーん、「エクスカーション」って調べても全然ヒットしないのね。こういうスキルってもう頭脳労働者の「当たり前な道具」とみなされてもいいんじゃないのかって気がすんだけど。やっぱこの手のものの利用者は広告業界とかに限られちゃうらしい。もったいない。なんと「エクスカーション」を検索するとワタシのサイトが Google 検索では一ページ目に入ってくる。これはちと異常だ。皆にも普通に知って欲しいと思う。

というわけでほんとはこのネタ、単なる個人的メモにしようと思ったんだけど、どうやらちょっとだけは説明しないといけないらしい。

たとえばこんな本:

アイデア・スイッチ 次々と発想を生み出す装置

新品価格
¥1,404から
(2015/2/14 09:35時点)

発想法、というものをインターネット検索してもあんまし整理された形で説明されてるサイトはなさそうなんだけれど、ただ、書店に行けば結構この手の発想法の本は見つかります。

「発想」はまぁ言っちゃえば「発散フェーズ」と「収束フェーズ」が必要なわけですな。なんにもアイディアが浮かばない、ってときに、「とにかく数を出す」という発散を経て、それを「選択・ブラッシュアップ」するという収束をさせていくわけだけど、エクスカーションは思考の発散に使う道具。「漫然と考えているだけでは思いもよらない発想へ脱線する」ことの助けとして使う。

上で紹介した「アイディア・スイッチ」で紹介されているエクスカーションの手順の一つは以下の通り:

  1. たとえば、動物を一つ思い浮かべ、特徴や連想することを10個書き出します。
  2. 「発案のテーマ(または解決したい問題)」 x 「書き出した言葉」でアイディアを出します。思いつかない組み合わせはパスして進みます。
  3. さらに動物を9つ思い浮かべ、一つ一つに対してステップ1~2を行います。

これで100個のアイディア出しをする、ということ。もちろんこのやり方だけが正しい、というものではなく、「思いもよらない組み合わせを出すこと」「とにかく数を出すこと」の二つが満たせればそれで良い。


というわけなんだけれども、この発想法の足しになるようなもの、前々から手軽なのが欲しいなぁ、とずっと考えてて。今さっき1時間くらいで作ったものが思ったより面白かったのでメモしとこうと。てわけでここからが本題、かつ「しょーもないネタ」。

「要するに」。思わぬ言葉の組み合わせを大量に作り出すことで、連想ゲームの加速が出来れば、思わぬアイディアが出るでしょ、てことなのだけど、「思わぬキーワード」をインターネット検索に求めない手はないでしょ。こんな抽出を考えてみた:

  1. テーマのキーワードで Google 一覧検索
  2. 検索にヒットしたサイトに飛んでコンテンツ全文を形態要素解析してキーワード抽出
  3. 抽出キーワードをランダムに二つずつ選んだ組み合わせを出力

面白いかどうかわからずにやってみたら面白かった、てこと:

  1 # -*- coding: utf-8 -*-
  2 import urllib
  3 import urllib2
  4 import httplib
  5 import ssl
  6 import time
  7 import re
  8 from subprocess import Popen, PIPE
  9 import unicodedata
 10 import random
 11 try:
 12     from cStringIO import StringIO  # CPython 2.7
 13 except ImportError:
 14     from StringIO import StringIO  # Python 3.x or not CPython
 15 from HTMLParser import HTMLParser  
 16 import httplib2  # https://pypi.python.org/pypi/httplib2
 17 from bs4 import BeautifulSoup  # https://pypi.python.org/bs4/
 18 
 19 
 20 #
 21 class _GoogleHtmlResExtractor(HTMLParser):
 22 
 23     def __init__(self):
 24         HTMLParser.__init__(self)
 25         self._states = 0
 26         self.data = []
 27 
 28     def handle_starttag(self, tag, attrs):
 29         d = dict(attrs)
 30         if tag == 'p':
 31             self._states = 1
 32         elif tag == 'a' and self._states == 1:
 33             if d['href'] != '/':
 34                 self.data.append({
 35                         "href": "https://www.google.co.jp" + d['href']
 36                         })
 37                 self._states = 2
 38 
 39     def handle_endtag(self, tag):
 40         if tag == 'a':
 41             self._states = 0
 42 
 43     def handle_data(self, data):
 44         if self._states == 2:
 45             self.data[-1]["data"] = data.strip().decode("utf-8")
 46 
 47 
 48 #
 49 def query(*query_keywords):
 50     user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
 51     url = "https://www.google.co.jp/search"
 52     data = urllib.urlencode({
 53             "q": u" ".join(query_keywords).encode("utf-8"),
 54             })
 55     opener = urllib2.build_opener()
 56     opener.addheaders = [('User-Agent', user_agent)]
 57     res = opener.open(url + "?" + data)
 58     html = res.read()
 59 
 60     parser = _GoogleHtmlResExtractor()
 61     parser.feed(html)
 62 
 63     return parser.data
 64 
 65 
 66 # httplib2 wrapper
 67 class httplib2inst(object):
 68     def __init__(self, cache_dir=".cache"):
 69         self._inst = httplib2.Http(cache_dir)
 70 
 71     def get(self, url):
 72         retry = 20
 73         while retry >= 0:
 74             try:
 75                 return self._inst.request(url, "GET")
 76             except (httplib.ResponseNotReady, ssl.SSLEOFError):
 77                 retry -= 1
 78                 time.sleep(8)
 79             except httplib2.ServerNotFoundError:
 80                 break
 81         return None, None
 82 
 83 
 84 # --- main ---
 85 if __name__ == '__main__':
 86     import sys, codecs ; sys.stdout = codecs.getwriter('utf-8')(sys.stdout)
 87     opnr = httplib2inst()
 88     srcs = set()
 89     for lst in query(u"火山"):  # ここはユーザ入力にするなりお好きに
 90         headers, contents = opnr.get(lst["href"])
 91         if headers:
 92             soup = BeautifulSoup(StringIO(contents), "lxml")
 93             # 1. python-mecab を導入するのが面倒だったので subprocess で呼び出してる
 94             # 2. mecab は辞書を utf-8 でインストールした
 95             p = Popen("mecab", shell=True, stdin=PIPE, stdout=PIPE)
 96             stdoutdata, stderrdata = p.communicate(input=soup.find("body").text.encode("utf-8"))
 97             for line in re.split(r"\r?\n", stdoutdata.decode("utf-8")):
 98                 if "\t" not in line:
 99                     continue
100                 word, _, inf = line.partition("\t")
101                 # 以下の品詞による絞込みはお好きにどうぞ
102                 if re.match(r"[ -/:-@[-`{-~]+", word):
103                     continue
104                 if re.match(r"[a-zA-Z]+", word):
105                     continue
106                 nword = unicodedata.normalize('NFKC', word)
107                 if re.match(r"[a-zA-Z]+", nword):
108                     continue
109                 typ = inf.split(",")[:2]
110                 if typ[0] in (u"記号", u"接続詞", u"連体詞") or u"助" in typ[0] or u"動" in typ[0] or typ[1] in (u"非自立", u"代名詞", u"数"):
111                     continue
112                 srcs.add(word)
113                 #print(u"{}\t{}".format(word, u",".join(typ)))
114     # set 全部をランダムシャッフルして2つずつペアを出力
115     sels = list(srcs)
116     random.shuffle(sels)
117     for i in range(0, len(sels) / 2 * 2, 2):
118         print(u"{} x {}".format(sels[i], sels[i + 1]))

このまんま使いたいという奇特な方は…

  • python 2.7 用に書いたので 3.x の方はテキトーに直して使えばよろしい
  • httplib2、BeautifulSoup、mecab 使ってるのでインストールしといてね

てのと、形態要素解析の結果の使い方は各自お好きにどうぞ、てのと。こんなん出ました:

 1 リフト x 水分
 2 確か x 入植
 3 サイズ x セントヘレンズ
 4 カスケード x プルームテクトニクス
 5 文明 x 時代
 6 速度 x ダイヤモンド
 7 パンツ x 粘土
 8 タスマン x 伝記
 9 研究 x 雪
10 分 x ダム
11 開聞岳 x 施行
12 無意味 x 執筆
13 隆起 x ランハム
14 競馬 x インタラクティブ
15 マリアナ x 文庫
16 歴史 x 立ち入り
17 ノート x 島
18 ウィクショナリー x 全裸
19 文藝春秋 x ウインタースポーツ
20 破局 x 地中海
21 北東 x 急激
22 ...
23 棚 x 証拠
24 ガラス x 南日吉
25 相撲 x 宮沢
26 砕物 x ヘル
27 全 x 秒間
28 海上保安庁 x オアシス
29 背 x 杏
30 御嶽 x 足
31 カープ x 夢
32 年 x 塩
33 がん x ©
34 瀬戸 x 秋
35 効率 x 古く
36 クラカタウ x 八甲田山
37 海王星 x 洞
38 海溝 x ほか
39 届け x 注意報
40 地点 x 稜
41 図表 x 温水
42 急峻 x 秘訣
43 意外 x 警報
44 堤 x 有無
45 全書 x 日々
46 姉妹 x 倍
47 機体 x 字
48 北陸 x 総合
49 美 x 毎月
50 前者 x 判断
51 アップ x 採用
52 ウェブ x 雪山
53 九州 x 現在
54 ...
55 多発 x 対象
56 扱い x 履歴
57 連発 x 紹介
58 首都 x 風
59 洪 x モスクワ
60 死亡 x 活発
61 船 x 準備
62 京都 x 金星
63 断面 x アート
64 前月 x 真紀
65 飛行場 x ストロンボリ
66 調査 x 恵山
67 石黒 x 「
68 サプリ x ヴェスヴィオ
69 観察 x 潮流
70 朝日学生新聞社 x 雲仙岳
71 度 x 家屋
72 入門 x 取得
73 席 x 英語
74 今後 x すぐ
75 マッカズ x 弾
76 気圧 x 政府
77 人工 x 魚
78 肘 x 物
79 羅臼山 x 週末
80 造山 x つかの間
81 水中 x 評価
82 アレート x 潤一
83 尖峰 x 山地
84 ...

「思わぬ」はともすれば「バカバカシイ」ので笑っちゃうようなのが平気で生み出される。一個人が漫然と考えてても絶対に出てこない組み合わせが楽しく、「木造 x 洞爺湖」なんて秀逸ね。

こういうスクリーンセーバ欲しいんだよなぁ。漠然と過ごす時間に眺めてるだけでヒラメキの元になるようなものね。とはいえ、こうやってスクリプトを丸ごとメモしとけば、いつでも使いたいときに使えるかな、と思うので、こんなインチキなもんでも役に立たんでもないと思う。道具立てに時間かけたくないですもの。