『ヒーリングbot』と言うものがあってだね。
メリークリスマス!!
ということで↓のアドベントカレンダーの14日目です!
(ざわざわ・・・)
はじめて
「むかし、『ヒーリングbot』と言うものがあってだね。」
おもむろに昔話をやつははじめました。
聴くところによると、漢たちは歴戦の疲れでへとへとになっておったそうな。
そんな中の唯一の救いは、美女を延々と垂れ流し続ける『ヒーリングbot』であったそうな。
ヒーリングbotを作りしものは神として拝み奉られておったそうな。
しかし、神は新世界へと去っていき、馬鹿な漢どもは救いを失い絶望しておったそうな
というわけで
今日は上のヒーリングbotを作れと言われた話です。
ただ、どのような娘っこが良いのかと聞いたとき言われたのが、、
プロっぽくないほうがいい
肌色はそんなにいらない。それより大事なのは透明感
アイドルみたいなのはいらない。素人娘がいい。
やはりカスらしく注文が多い奴らでした。
辿り着いた先は、とある「ヘアサロン」のモデルさんたちをクローリングして素材を取ってきて、それをslackに投稿するbotを作ろう。ということになりました。
やっと本題
今日はとあるHPをクローリングして画像ファイルを取得するものを作ります。
もろもろ楽そうだったので、言語はrubyを採用しました。
使用したライブラリは、、
Anemone - Ruby Web-Spider Framework
超ラクです。あっちゅうまにクローラーを作ることができました。
そして、HTMLの解析には、、、
を使いました。らくらくでしたわ。
ソースコードの紹介
require 'anemone' require 'nokogiri' URL = '<URL>' Anemone.crawl(URL, depth_limit: 0 ) do |anemone| anemone.on_every_page do |page| i = 0 page.doc.xpath('//img/@src').each do |node| if node.value =~ /https.*jpg/ p node.value open(node.value) do |file| open(i.to_s+'.jpg', "w+b") do |out| out.write(file.read) end end end i+=1 end end end
- とあるサイトをクロールします
Anemone.crawl(URL, depth_limit: 0 ) do |anemone|
- nokogiri形式に変換&画像情報を取得します。
page.doc.xpath('//img/@src').each do |node|
- 画像の取得、ローカルに保存
open(node.value) do |file| open(i.to_s+'.jpg', "w+b") do |out| out.write(file.read) end end
みたいな感じです。いや〜かんたんですね。
あとはbotにすれば完成と言ったところですが、、
やはりカスらしく要求はエスカレートします。
次は「素人の中でも美人を投稿してほしい」と言ってきやがりました。はは、まいったね。
ということで機械学習してみましょうか。と心に決めつつ本日は終了。
さいごに
彼女の目を盗んでささっと書いたこのブログ。存在が知られると怒られるかもしれないこのブログ。
クリスマスに何をしとるんじゃわしは。はは、まいったね。
というわけで、じゃあの!
neo4jをSpring bootで触ってみた
↓のアドベントカレンダーの7日目!(ざわ・・・)
はじめに
今日はneo4jをSpring bootで触ってみたので、その話を少々します。
neo4j
ひょんなことからグラフDBを触る機会ができたので、なにがいいかなーって考えたらやっぱ有名ドコロでしょってことでneo4jを触るようになってます。そんな私です。
neo4jはオープンソースのグラフDBで、Javaで実装されています。
はい、インストール
$ brew install neo4j
はい、起動
neo4j start
ええ、それはもう。
つぎにSpring boot
導入しましょう
Spring Data Neo4jとかいう便利なやつがいるので、 今回はそれを使います。
mavenの定義はこのような感じに・・・
(Gradleで書こうかと思ったけど、まぁ今回はいいかなと・・・)
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.springframework</groupId> <artifactId>gs-accessing-data-neo4j</artifactId> <version>0.1.0</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.2.RELEASE</version> </parent> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-neo4j</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <repositories> <repository> <id>spring-releases</id> <name>Spring Releases</name> <url>https://repo.spring.io/libs-release</url> </repository> </repositories> </project>
はい、OKです。
ちょちょっと何か作ろう
ひどく簡単(というか↓のチュートリアルそのもの・・・)ですが、サンプルも書いてみました。
人がチームメイトという関係でつながっているサンプルです。
まずは人のクラス
import java.util.Collections; import java.util.HashSet; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import org.neo4j.ogm.annotation.GraphId; import org.neo4j.ogm.annotation.NodeEntity; import org.neo4j.ogm.annotation.Relationship; @NodeEntity public class Person { @GraphId private Long id; private String name; private Person() { // Empty constructor required as of Neo4j API 2.0.5 }; public Person(String name) { this.name = name; } /** * Neo4j doesn't REALLY have bi-directional relationships. It just means when querying * to ignore the direction of the relationship. * https://dzone.com/articles/modelling-data-neo4j */ @Relationship(type = "TEAMMATE", direction = Relationship.UNDIRECTED) public Set<Person> teammates; public void worksWith(Person person) { if (teammates == null) { teammates = new HashSet<>(); } teammates.add(person); } public String toString() { return this.name + "'s teammates => " + Optional.ofNullable(this.teammates).orElse( Collections.emptySet()).stream().map( person -> person.getName()).collect(Collectors.toList()); } public String getName() { return name; } public void setName(String name) { this.name = name; } }
ノードであることは@Relationshipで宣言して、主キーみたいなもんのIDは@GraphIdで宣言して、ノード間の関係は@Relationshipで宣言して、って感じですか。
よくできてますね〜。ええ、はい。
次に人を検索するためのリポジトリ
import org.springframework.data.neo4j.repository.GraphRepository; public interface PersonRepository extends GraphRepository<Person> { Person findByName(String name); }
これで名前での検索が可能になります。
では、実際にノードを定義していきましょう。
import java.util.Arrays; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories; @SpringBootApplication @EnableNeo4jRepositories public class Application { private final static Logger log = LoggerFactory.getLogger(Application.class); public static void main(String[] args) throws Exception { SpringApplication.run(Application.class, args); } @Bean CommandLineRunner demo(PersonRepository personRepository) { return args -> { personRepository.deleteAll(); Person yamada = new Person("山田 哲人"); Person ogawa = new Person("小川 泰弘"); Person kawabata = new Person("川端 慎吾"); List<Person> team = Arrays.asList(yamada, ogawa, kawabata); log.info("Before linking up with Neo4j..."); team.stream().forEach(person -> log.info("\t" + person.toString())); personRepository.save(yamada); personRepository.save(ogawa); personRepository.save(kawabata); yamada = personRepository.findByName(yamada.getName()); yamada.worksWith(ogawa); yamada.worksWith(kawabata); personRepository.save(yamada); ogawa = personRepository.findByName(ogawa.getName()); ogawa.worksWith(kawabata); // We already know that roy works with greg personRepository.save(ogawa); // We already know craig works with roy and greg log.info("Lookup each person by name..."); team.stream().forEach(person -> log.info( "\t" + personRepository.findByName(person.getName()).toString())); }; } }
実行してみて〜
DBを確認すると、ほれこの通り。ちゃんと山田、ライアン、川端がチームメイトとしてつながっております。メジャーとか行くなよ。
そして、ログをみてもこの通り。 ちゃんと検索されております。
2016-12-20 06:25:05.978 INFO 85511 --- [ main] o.n.o.drivers.http.request.HttpRequest : Thread: 1, url: http://localhost:7474/db/data/transaction/commit, request: {"statements":[{"statement":"MATCH (n:`Person`) WHERE n.`name` = { `name` } WITH n MATCH p=(n)-[*0..1]-(m) RETURN p, ID(n)","parameters":{"name":"山田 哲人"},"resultDataContents":["graph","row"],"includeStats":false}]} 2016-12-20 06:25:05.989 INFO 85511 --- [ main] stretch.neo4j.Application : 山田 哲人's teammates => [川端 慎吾, 小川 泰弘] 2016-12-20 06:25:05.990 INFO 85511 --- [ main] o.n.o.drivers.http.request.HttpRequest : Thread: 1, url: http://localhost:7474/db/data/transaction/commit, request: {"statements":[{"statement":"MATCH (n:`Person`) WHERE n.`name` = { `name` } WITH n MATCH p=(n)-[*0..1]-(m) RETURN p, ID(n)","parameters":{"name":"小川 泰弘"},"resultDataContents":["graph","row"],"includeStats":false}]} 2016-12-20 06:25:06.001 INFO 85511 --- [ main] stretch.neo4j.Application : 小川 泰弘's teammates => [山田 哲人, 川端 慎吾] 2016-12-20 06:25:06.002 INFO 85511 --- [ main] o.n.o.drivers.http.request.HttpRequest : Thread: 1, url: http://localhost:7474/db/data/transaction/commit, request: {"statements":[{"statement":"MATCH (n:`Person`) WHERE n.`name` = { `name` } WITH n MATCH p=(n)-[*0..1]-(m) RETURN p, ID(n)","parameters":{"name":"川端 慎吾"},"resultDataContents":["graph","row"],"includeStats":false}]} 2016-12-20 06:25:06.014 INFO 85511 --- [ main] stretch.neo4j.Application : 川端 慎吾's teammates => [山田 哲人, 小川 泰弘]
あら、便利ですねぇ。
最後に
neo4jを導入した。そして、javaを使ってノード作成、検索ができるようになった!
本当に簡単なことしかやってないので、次回はもっと面白いこともやってみよう!
ということで、さようなら。
投稿が遅れたこと、、、、すまないと思っている。
2016年 dodosoft LT大会&忘年会!
↓のアドベントカレンダーの11日目!
あれ?7日目が空いてますね・・・!
ざわ・・・
ごめんなさい・・・! 7日はそのうち・・・!
はじめに
昨日、われわれdodosoftは(たしか)六回目くらいのLT大会を開催しましたー!
(結構やってんなぁとしみじみ・・・)
参加者は11名。 今回も技術のみならず、さっまざまなジャンルの発表がありました!
では、ほんのちょっと紹介・・・
LT本編
はい、かんぱーいよりスタート!(ぶれてるけど勢いが気に入ったので、これを採用しました!)
シャンパーンが投入されております、おほほ
はい、発表スタート!
技術関係ないーだな話だと
100kmウォーキングの話だの・・・
海外での結婚式のはなしだの・・・
艦これの話だの・・・(っと見せかけた機械学習の話に巧妙に発展したりだの・・・)
写真撮影旅行記の話だの・・・
アメリカでのエンジニア生活の話だの・・・
(酔っ払ってて写真撮ってねぇ。。)
ちょっくら技術の話になると・・・
町おこしイベントのHP作成だの・・・
じさばの話だの・・・
MashUp Award 参加しましたな話だの・・・
Hubotの話だの・・・画像による機械学習の話だの・・・
(ここいらは酔っ払ってて写真なし。。ただ、アドベントカレンダーに記事が載ってるので、詳しくはそちら参照だ!)
IoT的な話だの・・・
まぁ様々でございました
明確なテーマが無いから、そらそうよ。
といってもこれだけバリエーションがあるのも中々面白い。
これだから、止められないのですねぇ
そのあとは忘年会!
今年もがんばったね〜的な話になるかと思いきや、
前前前世がどうしたとか(私は、君の名はを見てない!)
前前前世 (movie ver.) RADWIMPS MV - YouTube
ガッキー可愛い〜とか(ドラマを見たことはないが、ガッキーを愛でるドラマであることはよくわかった!)
おやおや、お仕事関係ありませんねぇ。。
それで良いのでしょう。それでこそ我らなのでしょう。ええ、それはもう。
そして二次会へ・・・!
行った先の居酒屋にカラオケがおいてあり、こりゃラッキー!
っと思いましたが主に歌ったのは私。。。(この悲壮感よ)
次回はカラオケで全員を歌わせてやるっと謎の意気込みを持ちつつ、今回はこれにて終了。
まとめ
今年1年はdodosoft的にも色々始めた年!
ハッカソン参加したり、開発合宿やってみたり、代々木公園で青空学習やってみたり、、
しかし、まだまだ満足はしないのでありますよ。もっと面白いことをやっていこうー!
はじめてのhubotさわってみた(そして謝罪)
↓のアドベントカレンダー5日目!
失敗した・・・
お家についたのが10時すぎ、、
さて手元にはアドベントカレンダーのために書き連ねたネタ・・・
しかし何を思ったか
hubotでやきうbotを作りたい!!!
といきり立ってしまったわけです。 それが間違いだった。
貯めていたネタは二の次にbot作りにぼっとうしました。
いやー、全然できねーわ。。
ちなみに使ったのはhubotです。
本当に私がやりたかったのは、形態素解析&単語感情極性つかって、、
来たコメントに対して、
辛そうなコメントがきた時には↓でレス。
嬉しそうなコメントがきた時には↓でレス
をする予定でした。
あと、新井さんの画像検索も可能なbotちゃんにする予定でした。。。
それが、あれよあれよと時間を失い、、、
結果「arai」と言われたら新井の画像を返すアホみたいなbotになってしまいました。
↓は返信している様子
ちなみに↓がシステム構成図です。hubotはheroku上で稼働させております。
一応、ソース貼りますか?特に価値はないが、、
ちなみに、ハッシュをつけてるのは画像をつねに読み込ませるためです。
同じURLだったらキャッシュが働くのかして、SLACK内で画像が展開されなかったのでハッシュをつけてみました。
module.exports = (robot) -> robot.hear /arai/i, (res) -> res.send "画像のURL#"+Math.random()
ごめんなさい次回は頑張ります。。。