日記

更新頻度はあまり高くはありませんがネタがあったら書いていこうと思います。

Twitterにアニソンを歌わせてみた。

あけましておめでとうございます。

今回は複数のTwitterBotを使ってアニメソングを歌わせてみました。曲はご注文はうさぎですか??オープニングテーマ、Pettit Rabbit'sでノーポイッ!です。

gyazo.com

ちゃんとTwitterにも投稿されています。

gyazo.com

裏側

このプロジェクトを行うために下記ソフトウェアを作成しました。

github.com

このソフトウェアではボットを登録して、好きなツイートをさせることができます。また同時ツイートにも対応しているので、上の画像のようににコーラス部分にも対応できます。更にリプライを利用することでアニメや映画もTwitter上で再現することができます。ただ、Twitterの制限で同じツイートは連続でできない仕様になっているので同じセリフをしゃべらせるには若干変更しなければなりません。
設定画面はWebUIを採用しています。

f:id:PeeeeRON:20160101165851p:plain

そもそも、このソフトウェアはサーバ上で常駐することを前提としているので操作はすべてWebインタフェースを通して行うことができます。ただ、Twitterのアクセストークン等を保存するので公開サーバには置かないでください。あくまでローカルでの運用を想定しています。
また、REST APIを通じて会話をスタートさせることもできるので、サーバを起動しcron等のスケジューリングソフトウェアでHTTPリクエストを送るようにしておけば特定の会話を時間をしていして自動的にスタートすることもできます。

会話ボットなどにも活用できるのでぜひ使ってみてください。ZIPで配布したかったのですが、go-sqliteがgoxでビルドできなかったので、まだ出来ていません。Source Forgeを使いたいのですが、使い方がよくわかってません。

それでは2016年もよろしくお願い致します。

gyazo.com

コマンドラインで翻訳できるツールを作った。(バイナリ配布)

roomba.hatenablog.com

こちらの記事でGlosbeの翻訳APIをしったので、このツールのGo実装を作成してみました。さらに、いろいろな言語に対応できるようにしました。

github.com

ソースコードです

package main

import (
    "encoding/json"
    "flag"
    "fmt"
    "io/ioutil"
    "net/http"
    "net/url"
    "os"
    "strings"
)

// URL of api
const URL = "https://glosbe.com/gapi/translate"

func makeURL(src, srcLang, destLang string) string {
    v := url.Values{}
    v.Add("from", srcLang)
    v.Add("dest", destLang)
    v.Add("format", "json")
    v.Add("phrase", src)

    return URL + "?" + v.Encode()
}

// Send request.
func request(url string) (result string, err error) {
    resp, err := http.Get(url)
    if err != nil {
        return
    }

    defer resp.Body.Close()
    b, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return
    }

    var response Response
    err = json.Unmarshal(b, &response)
    if err != nil {
        return
    }

    res := make([]string, len(response.Tuc))
    count := 0
    for i, v := range response.Tuc {
        if v.Phrase.Text != "" {
            res[i] = v.Phrase.Text
            count = i
        }
    }
    result = strings.Join(res[:count+1], ",")

    return
}

func main() {

    // Setting help message.
    flag.Usage = func() {
        fmt.Fprintf(os.Stderr, `dict [OPTIONS] <src>
Options
`)
        flag.PrintDefaults()
    }

    // If this option is exist, translate to Japanese from English.
    from := flag.String("f", "", "From")
    to := flag.String("t", "", "To")
    flag.Parse()
    args := flag.Args()

    if *to == "" || *from == "" || args == nil || len(args) != 1 {
        flag.Usage()
        os.Exit(1)
    }

    result, err := request(makeURL(args[0], *from, *to))
    if err != nil {
        fmt.Println("Not found.")
    } else {
        fmt.Println(result)
    }
}

// Response object.
type Response struct {
    Result string `json:"result"`
    Tuc    []Tuc  `json:"tuc"`
    Phrase string `json:"phrase"`
    From   string `json:"from"`
    Dest   string `json:"dest"`
}

// Tuc object.
type Tuc struct {
    Phrase    Phrase    `json:"phrase"`
    Meanings  []Meaning `json:"meanings"`
    MeaningID int64     `json:"meaningId"`
    Authors   []int64   `json:"authors"`
}

// Phrase object.
type Phrase struct {
    Text     string `json:"text"`
    Language string `json:"language"`
}

// Meaning object.
type Meaning struct {
    Language string `json:"language"`
    Text     string `json:"text"`
}

使い方はGithubにも書いてありますが。例えば

$ dict -f en -t ja cat
$ 猫,ネコ,ねこ,にゃあにゃあ,neko,にゃにゃ,にゃんにゃん,ぬこ,カト,キャット

みたいな感じで実行します。
マークダウンとか英語で書いてる時この日本語英語でなんて言うんだろうって時にさっと検索できるのでいいと思います。 うえで上げたブログにかかれているようにaliasを好きな(自然)言語で設定するといいです。

バイナリも配布しているのでGoの環境を作らなくてもお気軽に試すことができるのでぜひ導入してみてください。

バイナリダウンロードリンク

Downloads | dict

良いお年を〜

GolangでSteamのセール情報を取得するクローラを作った。

Amazon.co.jp: Rubyによるクローラー開発技法 巡回・解析機能の実装と21の運用例: るびきち, 佐々木 拓郎: 本 という本を読んでいて何か実際に実装してみたいなと思い、Steamのセールを取得して保存するクローラをGoで作成しました。
このソフトウェアではSteamのセールページをクローリングしてゲームのセール情報をすべて取り出すことができます。

Go言語で並列化を実装したので約400ページ、約6000ゲームの情報を10秒程で取得できます。また、取得したものをCSV形式でファイルに保存するようにしました。
並列化部分の実装は下記のようになっています。

// StartCrawl start crawling.
func (c *Crawler) StartCrawl() (err error) {
    doc, err := goquery.NewDocument(url)
    if err != nil {
        return
    }

    // Getting the number of pages.
    var pageNum int
    doc.Find(".search_pagination_right").Children().Each(func(i int, s *goquery.Selection) {
        if i == 2 {
            pageNum, err = strconv.Atoi(s.Text())
            if err != nil {
                return
            }
        }
    })

    // Starting goroutine.
    resultCh := make(chan []Game, pageNum)
    for i := 1; i < pageNum+1; i++ {
        url := fmt.Sprintf("%s&page=%d", url, i)
        go c.crawl(url, resultCh)
    }

    // Collectiong games.
    for i := 1; i < pageNum+1; i++ {
        gs := <-resultCh
        c.games = append(c.games, gs...)
    }
    close(resultCh)

    return
}

ページネーションからページ数を取得できるので、取得したページのURLを作成し、1ページにつき1つのGoroutineを稼働させChannelを使って回収しています。HTMLのスクレイピングにはgoqueryというライブラリを使用しました。

また、コードはすべてGithub上で公開しています。

github.com

Python+tweepyでフォロワー増殖装置を作った。(+TwitterAPIの考察)

それがそのソースコードです。

Function to increase follower on twitter

qWordというタプルにフォローしたいアカウントのキーワードを入れることで自動的にフォローを行います。 さらにリフォロー率をあげるために、If文でフォロワーよりフォローしている人数が多いユーザーをフォローするようにしています。 Twitter APIにはリクエスト制限(180リクエスト/15min)があるので、1ワードにつき50回リクエストをするので3ワード(150リクエスト)で15分のスリープを行います。

しかしながら、フォローしまくってしまうと161エラーという「もうフォローできない」という趣のエラーが出てしまいます。 1日のフォロー制限は1000人のはずですが、これはそれよりも早い段階で出てしまうようです。 さらに、Twitter APIのユーザ検索は検索上位1000人を検索出来ると書いてあるのですが、実際には27回リクエストつまり540人しか検索出来ずに、それ以降は同じユーザがでてきました。

以上の理由から1000人は自動フォローできませんでした。 他のやり方を知っている方がいましたら教えていただきたいです。

Python+tweepyで自動フォロー装置を作った。

PythonでTwitterAPIを使ってみました。 PythonにはtweepyというTwitterAPIのラッパーライブラリがあるのでそれを使ってみました。

こんな感じになりました。

def followAndUnfollow(api):
    followersIds = api.followers_ids()
    friendsIds = api.friends_ids()


    #フォロアーにそのユーザが存在しなければアンフォロー
    for friendId in friendsIds:
        count = 0
        for followerId in followersIds:
            if friendId==followerId:
                break
            count+=1   
        if count == len(followersIds):
            try:
                api.destroy_friendship(friendId)
                print 'Destoroyed friendship with %s' %friendId
            except tweepy.error.TweepError:
                print 'I could not destroy this friendship.:('

    #フォローしていないフォロアーがいればフォロー
    for followerId in followersIds:
        count=0
        for friendId in friendsIds:
            if followerId == friendId:
                break
            count += 1
        if count == len(friendsIds):
            try:
                api.create_friendship(followerId, True)
                print 'Created friendship with %s :)' %followerId
            except tweepy.error.TweepError:
                print 'I could not create this friendship.:('

あとはこれを適当なサーバにデプロイしてスケジューラで実行すれば、勝手にフォロー、アンフォローをしてくれると思います。

たまにidを参照できずにエラーが出るので例外処理を入れました。