『ヒーリングbot』と言うものがあってだね。

メリークリスマス!!

ということで↓のアドベントカレンダーの14日目です!

(ざわざわ・・・)

www.adventar.org

はじめて

「むかし、『ヒーリングbot』と言うものがあってだね。」

おもむろに昔話をやつははじめました。

聴くところによると、漢たちは歴戦の疲れでへとへとになっておったそうな。

そんな中の唯一の救いは、美女を延々と垂れ流し続ける『ヒーリングbot』であったそうな。

ヒーリングbotを作りしものは神として拝み奉られておったそうな。

しかし、神は新世界へと去っていき、馬鹿な漢どもは救いを失い絶望しておったそうな

というわけで

今日は上のヒーリングbotを作れと言われた話です。

ただ、どのような娘っこが良いのかと聞いたとき言われたのが、、

  1. プロっぽくないほうがいい

  2. 肌色はそんなにいらない。それより大事なのは透明感

  3. アイドルみたいなのはいらない。素人娘がいい。

やはりカスらしく注文が多い奴らでした。

辿り着いた先は、とある「ヘアサロン」のモデルさんたちをクローリングして素材を取ってきて、それをslackに投稿するbotを作ろう。ということになりました。

やっと本題

今日はとあるHPをクローリングして画像ファイルを取得するものを作ります。

もろもろ楽そうだったので、言語はrubyを採用しました。

使用したライブラリは、、

Anemone - Ruby Web-Spider Framework

超ラクです。あっちゅうまにクローラーを作ることができました。

そして、HTMLの解析には、、、

www.nokogiri.org

を使いました。らくらくでしたわ。

ソースコードの紹介

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
  1. とあるサイトをクロールします
Anemone.crawl(URL, depth_limit: 0 ) do |anemone|
  1. nokogiri形式に変換&画像情報を取得します。
page.doc.xpath('//img/@src').each do |node|
  1. 画像の取得、ローカルに保存
        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日目!(ざわ・・・)

www.adventar.org

はじめに

今日はneo4jをSpring bootで触ってみたので、その話を少々します。

neo4j

ひょんなことからグラフDBを触る機会ができたので、なにがいいかなーって考えたらやっぱ有名ドコロでしょってことでneo4jを触るようになってます。そんな私です。

neo4jはオープンソースのグラフDBで、Javaで実装されています。

neo4j.com

はい、インストール

$ 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です。

ちょちょっと何か作ろう

ひどく簡単(というか↓のチュートリアルそのもの・・・)ですが、サンプルも書いてみました。

spring.io

人がチームメイトという関係でつながっているサンプルです。

まずは人のクラス

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を確認すると、ほれこの通り。ちゃんと山田、ライアン、川端がチームメイトとしてつながっております。メジャーとか行くなよ。

f:id:extinctddt:20161220070335p:plain

そして、ログをみてもこの通り。 ちゃんと検索されております。

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日目!

www.adventar.org

あれ?7日目が空いてますね・・・!

ざわ・・・

ごめんなさい・・・! 7日はそのうち・・・!

はじめに

昨日、われわれdodosoftは(たしか)六回目くらいのLT大会を開催しましたー!

(結構やってんなぁとしみじみ・・・)

参加者は11名。 今回も技術のみならず、さっまざまなジャンルの発表がありました!

では、ほんのちょっと紹介・・・

LT本編

はい、かんぱーいよりスタート!(ぶれてるけど勢いが気に入ったので、これを採用しました!)

f:id:extinctddt:20161211225932j:plain

シャンパーンが投入されております、おほほ

f:id:extinctddt:20161211230207j:plain

はい、発表スタート!

技術関係ないーだな話だと

100kmウォーキングの話だの・・・

f:id:extinctddt:20161211224743j:plain

海外での結婚式のはなしだの・・・

f:id:extinctddt:20161211224833j:plain

艦これの話だの・・・(っと見せかけた機械学習の話に巧妙に発展したりだの・・・)

f:id:extinctddt:20161211224914j:plain

写真撮影旅行記の話だの・・・

f:id:extinctddt:20161211225019j:plain

アメリカでのエンジニア生活の話だの・・・

(酔っ払ってて写真撮ってねぇ。。)

ちょっくら技術の話になると・・・

町おこしイベントのHP作成だの・・・

f:id:extinctddt:20161211225208j:plain

じさばの話だの・・・

f:id:extinctddt:20161211225237j:plain

MashUp Award 参加しましたな話だの・・・

f:id:extinctddt:20161211225311j:plain

Hubotの話だの・・・画像による機械学習の話だの・・・

(ここいらは酔っ払ってて写真なし。。ただ、アドベントカレンダーに記事が載ってるので、詳しくはそちら参照だ!)

IoT的な話だの・・・

f:id:extinctddt:20161211225415j:plain

まぁ様々でございました

明確なテーマが無いから、そらそうよ。

といってもこれだけバリエーションがあるのも中々面白い。

これだから、止められないのですねぇ

そのあとは忘年会!

今年もがんばったね〜的な話になるかと思いきや、

前前前世がどうしたとか(私は、君の名はを見てない!)

前前前世 (movie ver.) RADWIMPS MV - YouTube

ガッキー可愛い〜とか(ドラマを見たことはないが、ガッキーを愛でるドラマであることはよくわかった!)

www.youtube.com

おやおや、お仕事関係ありませんねぇ。。

それで良いのでしょう。それでこそ我らなのでしょう。ええ、それはもう。

そして二次会へ・・・!

行った先の居酒屋にカラオケがおいてあり、こりゃラッキー!

っと思いましたが主に歌ったのは私。。。(この悲壮感よ)

f:id:extinctddt:20161211225911j:plain

次回はカラオケで全員を歌わせてやるっと謎の意気込みを持ちつつ、今回はこれにて終了。

まとめ

今年1年はdodosoft的にも色々始めた年!

ハッカソン参加したり、開発合宿やってみたり、代々木公園で青空学習やってみたり、、

しかし、まだまだ満足はしないのでありますよ。もっと面白いことをやっていこうー!

はじめてのhubotさわってみた(そして謝罪)

↓のアドベントカレンダー5日目!

www.adventar.org

失敗した・・・

お家についたのが10時すぎ、、

さて手元にはアドベントカレンダーのために書き連ねたネタ・・・

しかし何を思ったか

hubotでやきうbotを作りたい!!!

といきり立ってしまったわけです。 それが間違いだった。

貯めていたネタは二の次にbot作りにぼっとうしました。

いやー、全然できねーわ。。

ちなみに使ったのはhubotです。

本当に私がやりたかったのは、形態素解析&単語感情極性つかって、、

来たコメントに対して、

辛そうなコメントがきた時には↓でレス。

f:id:extinctddt:20161205234558j:plain

嬉しそうなコメントがきた時には↓でレス

f:id:extinctddt:20161205234631j:plain

をする予定でした。

あと、新井さんの画像検索も可能なbotちゃんにする予定でした。。。

それが、あれよあれよと時間を失い、、、

結果「arai」と言われたら新井の画像を返すアホみたいなbotになってしまいました。

↓は返信している様子

f:id:extinctddt:20161206000345p:plain

ちなみに↓がシステム構成図です。hubotはheroku上で稼働させております。

f:id:extinctddt:20161205235553p:plain

一応、ソース貼りますか?特に価値はないが、、

ちなみに、ハッシュをつけてるのは画像をつねに読み込ませるためです。

同じURLだったらキャッシュが働くのかして、SLACK内で画像が展開されなかったのでハッシュをつけてみました。

module.exports = (robot) ->
  robot.hear /arai/i, (res) ->
     res.send "画像のURL#"+Math.random()

ごめんなさい次回は頑張ります。。。