Stimulator

機械学習とか好きな技術話とかエンジニア的な話とかを書く

Pythonでyahoo画像検索した結果をimgcatに流して表示してURLをクリップボードにコピーするやつ

- はじめに -

近年では、チャットツールの発展が睦まじく、グループ内、企業内においてもチャットツールによるコミュニケーションが盛んとなっている。

チャットツールでのコミュニケーションにおいて欠かせないのが、画像によるハイコンテクストなやり取りである。
互いに同じレベルでの前提知識を持ち合わせている時、「有名な漫画のコマ」や「その場の状況を風刺する画像」を共有するコミュニケーションは、一般的な文字でのやり取りよりも時に頑強となる事が多い。

本記事では、有名な画像を検索しチャットに貼るために必要な工程である、画像検索、選択、コピーを簡略化するため、xonshを利用したPythonによる画像検索スクリプトを提示する。


つまるところコンソールだけで以下のように画像検索、URLコピーまでを扱えるようにする。
f:id:vaaaaaanquish:20180613153439p:plain
書いたスクリプトimgsearch_on_xonsh · GitHub



 

- 画像検索先 -

なんかGoogleは普通にクロールしようとするとすぐBANされるしAPIも画像検索は全然ダメなので、Yahoo!画像検索を利用する。


 

- yahoo画像検索の結果を取得する -

あるワードでYahoo!の画像検索をかけ、結果のURLを取得するPythonスクリプトを書く。
BeautifulSoupでHTMLを解析し、画像のURLだけ取ってくる。

from mimetypes import guess_extension
from urllib.request import urlopen, Request
from urllib.parse import quote
from bs4 import BeautifulSoup

def _request(url):
    # requestを処理しHTMLとcontent-typeを返す
    req = Request(url)
    try:
        with urlopen(req, timeout=5) as p:
             b_content = p.read()
             mime = p.getheader('Content-Type')
    except:
        return None, None
    return b_content, mime

def _yahoo_img_search(word):
    # yahoo!画像検索の結果から画像のURLのlistを返す
    url = 'http://image.search.yahoo.co.jp/search?n=60&p={}&search.x=1'.format(quote(word))
    byte_content, _ = _request(url)
    structured_page = BeautifulSoup(byte_content.decode('UTF-8'), 'html.parser')
    img_link_elems = structured_page.find_all('a', attrs={'target': 'imagewin'})
    # 順番守りつつset取る
    seen = set()
    seen_add = seen.add
    img_urls = [e.get('href') for e in img_link_elems if e.get('href') not in seen and not seen_add(e.get('href'))]
    return img_urls

print(_yahoo_img_search('hoge piyo'))

画像の検索結果のURLが取得できた。
これだけでも使えるけど、コンソールで選択的にコピーしたいのでもうちょっとがんばる。


 

- URLから画像をローカルに保存する -

取得したURLから、画像を一旦ローカルに保存してやる。

今回最終的な目的がコンソール上での処理なので、10枚の画像を想定。

この処理が最も重たくなるのでmultiprocessingでよしなにやる。

先述のスクリプトの関数を利用して以下。

import os
import sys
from PIL import Image
from multiprocessing import Pool
from multiprocessing import cpu_count

def _save_img(t):
    # (id+'\t'+url)を受け取って/tmp/img配下にid名で画像を保存する
    img, mime = _request(t.split('\t')[1])
    if mime is None or img is None:
        return ''
    # 拡張子
    ext = guess_extension(mime.split(';')[0])
    if ext in ('.jpe', '.jpeg', '.png', '.gif'):
        ext = '.jpg'
    if not ext:
        return ''
    # 保存
    result_file = os.path.join('/tmp/img', t.split('\t')[0] + ext)
    with open(result_file, mode='wb') as f:
        f.write(img)
    # multiprocessingからprintするにはこう
    sys.stdout.write('.')
    sys.stdout.flush()
    return result_file


def _img_d(word):
   # tmp/img無かったら作る
    if not os.path.exists('/tmp/img'):
        os.makedirs('/tmp/img')
    # wordに対して検索結果の画像URL取得し10個に絞る
    t = _yahoo_img_search(word)
    if len(t)<10:
        print('Not Found 10 IMG.')
        return [], []
    t = t[:10]
    # id付ける
    urls = [str(i)+'\t'+x for i,x in enumerate(t)]
    # multiprocessで画像ダウンロード
    cpu = cpu_count()
    p = Pool(cpu-1)
    a = p.map(_save_img, urls)
    p.close()
    print('saved images.')
    return a, t

上記スクリプトで/tmp/imgにYahoo!画像検索の結果が保存される。
あとでけす。


 

- 画像を並べてxonshを利用してimgcat -

プロンプト上で画像を表示するにはimgcatを使う。
私はiterm2を使っているので以下を利用する。
www.iterm2.com

Linuxでimgcatを使いたい場合は以下。
github.com


先述のスクリプトで保存された画像をそれぞれ読み込んで、サイズを250*250に加工する。
横に5つずつ並べてtmp/h.jpgを作成し、imgcatに流す。

imgcatに流した画像に対して、番号入力を待ち受ける形にし、番号に相当する画像URLをpbcopyによってPCのクリップボードにコピーする。

def _imgs(word):
    # 引数をつなげて画像検索、ダウンロード
    word = ' '.join(word)
    paths, urls = _img_d(word)
    if not paths or not urls:
        print('Bad input.')
    else:
        # 画像をリサイズし、縦2*横5で並べた画像を生成
        img = Image.new('RGB', (250 * 5, 500))
        for j in range(10):
            im = Image.open(paths[j]).resize((250, 250))
            if j >= 5:
                img.paste(im, ( 250*(j-5), 250))
            else:
                img.paste(im, ( 250*j, 0))
        img.save("/tmp/h.jpg")
        # imgcatで表示
        imgcat /tmp/h.jpg
        # input待ち受け
        img_num = input('image number(1~10) : ')
        # inputした結果に対応するURLをシェルコマンドを利用してクリップボードに流す
        try:
            echo -n @(urls[int(img_num)+1]) | pbcopy
        except:
            print('Bad input.')

aliases['imgs'] = _imgs

多分ここだけ編集すればxonshでなくてもPythonスクリプトとして使える。

 
このPythonによる画像加工とimgcat、pbcopy、peco辺りの組み合わせが癖になるので皆さんxonsh使うと良いと思います。

github.com



 

- おわりに -

ひどいもんだ

f:id:vaaaaaanquish:20180613172047p:plain


 
適当に書いたスクリプトなので以下gistにまとめた。
多分何かもっとスピード上げられるけど現状不満はないです。

SyntaxHighlightつけるために.xsh.pyになってるのでhoge.xshにしてxonshrcから from hoge import * してやれば良いです。
imgsearch_on_xonsh · GitHub

 

CLI上でtableを綺麗に見たくて各言語のtable表示について調べた

- はじめに -

データ分析、機械学習という仕事柄、csvやtsvを見る機会が多い。

処理する時は大体はpythonのpandasで読み込んで〜とするのだが、コンソール上で作業する時、どうしても「このcsvなんだっけ…」という事が発生する。

cat hoge.csv | head

等として上部だけ見たり、jsonならjqコマンドに流すのだが、いかんせん見栄えの問題で一瞬で判断出来なかったりする。
https://stedolan.github.io/jq/


そこで、table形式にしてコンソール上で表示して見れると嬉しいなと思って調べた事をまとめる記事。
端的に結果を最初に言うと、一般的なコンソールで使うだけならcsvkitなるCLIツールがかなり機能的で便利。tty-tableコマンドとしてjqコマンドのように扱えるtty-tableも綺麗に見れて良い。
CLIツールを今から自前で作るならGoでtablewriterpython上でも資産として利用するならpython-tabulateが便利そうという事が分かった。


追記:

ん〜!わかる!


 

- CLIにtable表示するパッケージのまとめ -

CLIツールとして有名どころではNode.js製のcli-tableがある。
また、cli-tableのAPIと互換性を持ちつつリメイクされたcli-table2や、BoxやProgressBar等の実装も含まれるpixl-cliを使う選択肢がある。

Goだとtablewriter、Pythonだとpython-tabulateが良い。

 

Node.js実装

cli-table

Node.jsで書かれたCLI向けのテーブルビジュアライザ。

github.com

npmが入ってればすぐインストールできる。

npm install cli-table

簡単に扱えて

var Table = require('cli-table');

var table = new Table({head: ['X (train)', 'Y (label)'], colWidths: [20, 20]});
table.push(['hogehoge', 'piyo1'], ['fugafuga', 'piyo2']);
console.log(table.toString());

f:id:vaaaaaanquish:20180503201742p:plain

中身でcolor.jsで色付けしてくれるので見やすい。
Horizontalにするにはlist、Verticalならdictを入れるだけ。
かなり簡易。

 

cli-table2

cli-tableの機能拡張リメイク

github.com

cli-tableのAPIを踏襲している上、セルごとに文字、色、paddingを設定できたり、行列にまたがるセルを生成できるのが強み。

npmで入れる

npm install cli-table2

基本的にはcli-tableと同じ感じで扱える。

var Table = require('cli-table2');
var table = new Table({head:['X', 'Y'], colWidths: [20, 20]});
table.push(
[{colSpan:2,content:'None'}],
['hogehoge','piyo1'],
['fuga', 'piyo2'],
['fugefuga2',{rowSpan:2,content:'piyo4\npiyo5'}],
['fugafuga3']
);
console.log(table.toString());

f:id:vaaaaaanquish:20180503205647p:plain

なんか趣向と外れてきてる気がするが面白い。

以下のAdvanced-usageに面白そうな例が沢山あるので参考に。
cli-table2/advanced-usage.md at master · jamestalmage/cli-table2 · GitHub

 

pixl-cli

上記2つとは少し違って、コマンドラインでNode.jsを作る時のユーティリティが詰まったやつ。
ユーザinputやgraphical info boxes(boxだけでなくtableも扱える)、ProgressBar等が含まれている。

github.com

boxもしくはtableを使うと良さげに表示できる

var cli = require('pixl-cli');
cli.print( cli.box("This is Example!! :D") + '\n');

f:id:vaaaaaanquish:20180503211430p:plain
良さそう。

var cli = require('pixl-cli');
var rows = [
    ['X (data)', 'Y (label)'],
    ['hoge', 'piyo1'],
    ['fuga', 'piyo2']];
cli.print(cli.table(rows)+'\n');

f:id:vaaaaaanquish:20180503211840p:plain

tableも複雑な事はできないが、list投げるだけなので所感は変わらず。

今回は割愛したがProgressBarも便利感あるし、Node.jsでCLIツール作る時はこれ使うと良さそう。

 

Ruby実装

terminal-table

ASCIIでtableをよしなに表示するやつ。

github.com

Rubyなのでgemでインストールする

gem install terminal-table

サンプルを適当に動かす。

require 'terminal-table'
rows = [['hoge', 1], ['piyo', 2], ['fuga', 'test']]
table = Terminal::Table.new :title => "Header Sample", :headings => ['X', 'Y'], :rows => rows
puts table

f:id:vaaaaaanquish:20180503213004p:plain

色も良いけど、やっぱりASCIIが可愛い。
よしなに文字幅等調整するようになってるので、tableのline変えても結構自由に動く。

でも個人的には、RubyCLIツール作る機会がほぼ無くなってしまったので使う機会があれば。

 

tty-table

rubyのTTY toolkit用に作られているtableビジュアライザ。

他のパッケージと違って、インストール時点でコマンドとして使えるのが良さ。
言語関係なくCLIで使うだけなら一番楽だと思う。

また、どの形式でtableを形成するか選べて、unicodeやASCIIが選べる。

www.npmjs.com

gemで入れる

gem install tty-table

TTY::TableやTTY::Table::Rendererが用意されているので、それぞれinitializeして表示。

require 'tty-table'
rows = [['hoge', 1], ['piyo', 2], ['fuga', 'test']]
table = TTY::Table.new rows
renderer = TTY::Table::Renderer::ASCII.new(table)
puts renderer.render

f:id:vaaaaaanquish:20180503214830p:plain

試しにASCIIで出したが良い感じ。

コマンドでも出せて、これが便利感がある。
例えば以下みたくcsvを表示してみる。

cat titanic.csv | head | tty-table

f:id:vaaaaaanquish:20180503215742p:plain
みんな大好きtitanicデータ。

コマンドだと--formatでjsonも指定できたり、--csv-delimiterで'\t'指定すればtsvをtable表示したりもできるので使い勝手がすごい。

 

Go実装

tablewriter

Goだとtablewriter一択だと勝手に思っている。

github.com

知らない間に以下も吸収されていた。
GitHub - crackcomm/go-clitable: Command line (ASCII) and Markdown table for Golang. You probably want to take a look at: https://github.com/olekukonko/tablewriter


機能面では一番使い勝手が良い。
CSVや他Separatorが扱えるだけでなく、行列内のセル、Markdown Formatまで実装されている。
Captionをつける機能もあって、多分CLIでtable作る時は一番便利に扱える。

go get  github.com/olekukonko/tablewriter

以下見たくgoファイル作ってツール化。

package main
import "os"
import "github.com/olekukonko/tablewriter"
func main() {
  data := [][]string{
    []string{"hoge", "String", "SAMPLE"},
    []string{"piyo", "Int", "10"},
    []string{"fuga", "Int", "10"},
  }
  table := tablewriter.NewWriter(os.Stdout)
  table.SetHeader([]string{"X", "TYPE", "VALUE"})
  for _, v := range data {
    table.Append(v)
  }
  table.Render()
}

f:id:vaaaaaanquish:20180503221950p:plain

inputにCSVも使える上、Docも充実。
tablewriter - GoDoc


自前でtable見るツール作るならベストだし、割りとGoでCLIツール作られている事が多くなってきたので絶対使いそう。

 

Python実装

python-prettytable

redhatのdprince氏が作っているprettytable。

github.com

READMEに気合いが入っていている。

pythonなのでpipで導入する。

pip install prettytable

READMEにはset_field_namesなるmethodがあると書いてあったが見当たらず以下のように

from prettytable import PrettyTable
x = PrettyTable()
x.field_names = ['X', 'Y', 'VALUE']
x.add_row(['hoge', 'piyo1', '1'])
x.add_row(['fuga', 'piyo2', '2'])
print(x.get_string())

f:id:vaaaaaanquish:20180503224618p:plain

prettytableの良いところとして、get_html_stringなるmethodがあり、html形式のtable取得ができるので、Webアプリ等に直接返す時にちょっと使えそう。

 

csvkit

CLIツールとして利用するなら最も機能が多い。
コマンドとして入るのですぐ使える。

github.com

pipで導入していく

pip install csvkit

コマンドとして使えるようになってるはず

csvlook titanic.csv

f:id:vaaaaaanquish:20180505103957p:plain

この他、csvgrepやcsvsort、csvjoin、csvstatといったサッとコマンドとして使いたい機能が全てまとまっている。

以下ドキュメントを見るのが最も分かりやすい。
csvkit 1.0.3 — csvkit 1.0.3 documentation

csvlook hoge.csv | head としたり、csvstat見たりすることで、やりたい事は大体実現されている気がする。
良い。

 

python-tabulate

多分多くのPythonパッケージではこれが利用されてると思う。

github.com

pipで導入していく

pip install tabulate

普通にリスト投げるだけで良いので扱いやすい点でユーザが多いのだと思う。

from tabulate import tabulate
table = [["hoge",1],["piyo",2],["fuga",3]]
headers = ["X (data)", "Y (label)"]
print(tabulate(table, headers, tablefmt="grid"))

f:id:vaaaaaanquish:20180503225745p:plain

outputの形式が充実していて、一般的なtableのgrid形式から、PHP MarkdownのpipeやEmacs org-mode、wikiLaTeX markup形式としても出せる。
また、入力としてpandasも対応している。
つよい。


 

でどれが良さそうなのよ

最初にも書いた通り、「すぐコマンドとして使いたい」ならcsvkitがベスト。
tty-tableも良い。cat csvしてtty-tableコマンドに流すだけ。

CLIツールを今から作るならtablewriterでコマンド作って流す形にするのが良さそう。
機能が一番充実しているし、Docも充実している。
Goは良いぞ。

python上でも資産として利用するならpython-tabulateが便利そう。
本当の事を言うと、コンソール上で使いたいというよりxonsh上で使いたいという気持ちが強いので、個人的にはこれを使っていく事になると思う。
python-tabulateにhtml形式のoutput実装してmergeしてもらえば今の所満足そう。

rubyのとnodeは使って見た上で保留。
pixl-cliはめっちゃ使えそうだけどCLIツール最近はGoで書いてて、Node.jsはもっぱらGCP周りで使うくらいなのでう〜ん…

他言語も保留。


 

おわりに

csv簡易table閲覧コマンド作ってxontribにしていくぞ!


 

MANABIYAで「AI屋さんの1日」なるタイトルで登壇した話とその内容

- はじめに -

以下、MANABIYA techなるイベント内のAIセッションにて登壇させて頂きました。

manabiya.tech

大きなスペースでフザけたタイトルで発表するという最悪さでしたが、満員になり立ち見状態でした。
ありがとうございました。


正当な方向性でいけば登壇スライドを公開して終わりなのですが、会社のアカウントでSlideShareにアップロードするという行為に宗教上耐えきれそうにないため、会社情報を含まない範囲でここに思い出と共に書き残す形にしようかと思います。


 

- 登壇内容 -

登壇では、以下3つをテーマに話をしました。

  • AI屋さんの定義、分類は?

  • 実際AI屋さんって何やってるの?

  • 上手くAIプロジェクトを回すには?

 

AI屋さんの定義と分類

AI屋さんとは、「セッションタイトルが ‘AI’ だったので私が仕方なく付けた名称」です。

そもそも私は、機械学習や統計モデルを少々取り入れて「AI技術」だとか「人工知能技術」「AI企業」「AI部門」と声高らかに語る団体は全て嫌いです。


2012年頃から日本にも「人工知能、AIブーム」と一般的に呼ばれる"波"が来ました。

2016年後頃になると、ブーム自体が一般的に広く認知され、多くの企業が「我が社もAIをやっている」等と言うようになりました。ちょうど認知されていくタイミングと私が就職活動に勤しんでいた時期が重なり「私は機械学習がやりたいだけだ!人工知能、AIという単語を出した企業は全部こちらからお断りだ!」と躍起になっていたのを今でも覚えています。


それから数年、人工知能、AI、データサイエンティスト、機械学習エンジニアといった単語が広く普及した今になってそれらから逃れ仕事をするというのは、現代で「そもそもオタクの語源は〜」と語りながらオタクを続ける程に生きづらいでしょう。

セッションでは、生きづらい人生の歩み方を否定している訳ではなく「こうなったら言葉を潤滑油として利用してやろう」という方向性で、それぞれの言葉を実務に埋め込みながらAI屋の仕事を詳しく世に広める話をつらつら行いました。


f:id:vaaaaaanquish:20180324172357p:plain
登壇スライドより AI屋さんの分類

最初に上記の図で、AI屋の仕事というのは以下の3つに分かれる話をしました。

この分類で大事な点は「それぞれ基盤となる技術は共通」「アウトプットが違う」という所です。

ここで分析の業務とは、主に「MAU、DAUやユーザの行動履歴から広告、CM等の施策の効果を定量化、推論」「市場を分析し次の行動決定を円滑に進める」「ABテストの効果分析」等があたります。分析業務におけるアウトプットは、企業で言えば「データを分析し可視化された結果で人や施策のアクションを決定する」事です。

一方モデリングの業務とは、「データを学習できるモデルを作成する」「学習したモデルのサービスイン」といった所を指します。モデリングのアウトプットは、「実際のサービスで稼働する」となってきます。

モデリング業務は、皆サービスのユーザとしては日々感じている場合が多く、より身近かもしれません。例えば、Webサービス等において「オススメ商品がサジェストされてくる」だとか「写真にタグを自動で付けてくれる」といった類の物です。


分析、モデリングの2つのアウトプットの違いを理解せず人を雇ったり、プロジェクトメンバーを配置してしまうと悲惨な事になりがちです。

広く言われる「データサイエンティストの書くコードは汚い」といった発言は、発言者が身を置く環境が「レベルが低い」か「AI屋の認識にズレがある」という所から来ていると私は考えています。

f:id:vaaaaaanquish:20180324190557p:plain
登壇スライドより AI屋さんの分類

研究業務はさておき、上記の図には、各AI屋さんのそれぞれの中身を少し詳しく書いています。
独断と偏見も混じっていますが、概ね正しいと自負しています(刺されそうです)。


1つ目のスライド図で、分析は「一般的にデータサイエンティスト」、モデリングは「一般的に機械学習エンジニア」のお仕事であると記載しています。2つ目のスライド図では「データサイエンティスト」「機械学習エンジニア」の位置は、それぞれ分析、モデリングに寄りつつも重なった位置に書いています。

"一般的に"という説明から2つ目のスライド図を示した通り、近年における「データサイエンティスト」「機械学習エンジニア」という言葉は、揺らぎが強く、分析、モデリング業務を多岐に行っている場合が多くなっています。
分析業務とモデリング業務のアウトプットはかなり違うのに…


アウトプットまでの課程が変われば、そこで利用するデータや手法、工数割り振り、考え方が変わってきてしまいます。
つまり「分析で出た結果が良いのでそのままサービスインしよう!」「モデリングで出来た物が良いので」というのはかなり厳しい訳です。
これは、分析、モデリングの工程における統計に基づくデータの加工や、機械学習モデルの選択が変わってくるからです。

業務においてプロジェクトを成功に導くには、「データサイエンティスト」「機械学習エンジニア」という言葉から更に深掘りし、どういったアウトプットを要求するかという点を考え、それぞれの業務の経験、得意不得意から適材適所に人を配置しなければいけません。



最後に研究業務ですが、Yahoo! Japanという企業を例に出すと、Yahoo! JAPAN研究所を作り「学会での論文発表」「大学との共同研究」「オープンデータ、ソフトウェアの公開」という業務を行っているようです。

f:id:vaaaaaanquish:20180324184638p:plain
Yahoo! Japan研究所論文発表数:research-lab.yahoo.co.jp/gaiyo/index.htmlより引用

企業で利用できるデータを用いた国内外のトップカンファレンスでの発表、オープンデータの公開といった業務以外にも多分社内向けの分析基盤を作ったりもしているでしょう。
参考:
ソフトウェア/データ - Yahoo! JAPAN研究所 - ヤフー株式会社
研究領域 - Yahoo! JAPAN研究所 - ヤフー株式会社


研究はモデリング、分析とは違い、直接的に事業に貢献できる物ではない場合が多い業務です。
アウトプットも「論文」「機械学習アルゴリズム」「分析ソフトウェア」等になってきます。


AI屋さんのアウトプット基準で分類を定義しました。
大事なのは研究、分析、モデリング全てにおいて機械学習、統計、自然言語処理、画像認識、音声認識、情報検索…といった専門分野が基盤にあるのに対して、アウトプットで見ると全く違うという点です。

皆さんが行いたい「AI」って何ですか?
専門性ももちろんなのですが、「データサイエンティスト」「機械学習エンジニア」という言葉から更に深掘りし、どういったアウトプットを要求するかという点を考え、それぞれの業務の経験、得意不得意から適材適所に人を配置しなければいけません。(大事なので2回言っています)


ただただ「できる機械学習エンジニアが欲しい!」と言ってる人は、「フロントエンド、バックエンドの何がやりたいのか定義せずJavaScriptエンジニアが欲しい!」と言っている人と同レベルです。


 

実際のAI屋さんの業務

AI屋さんの1日を見て、どのように業務を進めているかについても話しました。

実際に私の平均的な1日を図として示しています。

f:id:vaaaaaanquish:20180324191530p:plain:w400
登壇スライドより AI屋さんの1日

よくわからない人間とのミーティングや雑務を除いて、Qiitaやはてなブログでバズってくるような「機械学習ライブラリを使ってパラメータをイジって学習〜」「データを可視化〜」という部分はかなり上澄みで、実際の業務で使われる時間はどんどん少なくなっています。

いくつかの要因がありますが、主には以下です。


統計、機械学習において最も効果的なのが学習データ、タスクの修正であるという話は、AI屋さんの中では以前より存在するかなり当たり前の話です。

実際に学習データが綺麗になればなるほど、機械学習モデルの精度は上がっていきます。
また、精度が上がりきらなければタスク自体をより機械学習に適したタスクに落とし込むという作業も有効です。

例えば、私は前職現職でもひたすら学習用の画像を見て数日を過ごす事もありました。
また、タスク自体を変更するために、年度やユーザ、特定のルールに基づいてデータを収集しなおしたり、問題設定自体を見直すために正解ラベルが入ったExcelファイルと数週間戦う事もあります。

こういった「泥臭い作業」は、クラウドソーシングで〜という場合もあるにはありますが、基本的には統計、機械学習に関する知見が必要で「これは機械学習で判断できるであろう」という前提を持たなければ出来ない作業です。

加えて、そういった「泥臭くない作業以」はフレームワークやマシンの充実で自動化が進んでいます。
例えば、パラメータの調整手法の充実、機械学習ライブラリや可視化ツール、分散処理技術、GPUスパコンの存在がそれらを支えています。
Yahoo! Japanにはどうやらkukaiと呼ばれるスパコンもあるらしいです(https://about.yahoo.co.jp/pr/release/2017/06/19b/)。
(もちろんこれら基盤にお金が必要だったりという場合もありますがその話はここでは取り上げません)。


適切な環境やツール、手法さえ選び使う事ができれば、泥臭く最も効果のある作業に集中しアウトプットの質を上げる事が可能なのです 。
AI屋さんの仕事のサイクルというのは「泥臭い作業をしながら、泥臭くない作業をガンガン自動化して効率化していく」という所に尽きます。


作業の効率化の方法は、研究業務にも近く、研究業務のアウトプットが社内外の分析、モデリング業務を支えているというイメージです。


 

AIプロジェクトを上手く回すには

AIプロジェクトを回すコツを3つ紹介しました。

  • 使いたいのは誰か決める
  • 泥臭くない作業を自動化する
  • 要件を順に詳しく決定していく

上2つは、今までの話と関連しています。

分析、モデリング、研究とそれぞれ利用する技術は同じでも、アウトプットの形が違います。
「データサイエンティスト」「機械学習エンジニア」といった言葉も良いですが、より深めて見ないとプロジェクト毎倒れてしまいます。

また泥臭くない作業というのは積極的に自動化したい所です。
もちろん自動化しやすい最適なフレームワークやツール、手法の選定はAI屋さんが行うのですが、インフラ屋さんやDB屋さん、果てはハード屋さんの協力も必要でしょう。
時にはお金も必要でしょう。
「AI屋さんにGPU買ったけど日の半分画像フォルダとにらめっこしてる」という事が多くあると思いますが、そういった時に特に自動化を進められるような協力体制が出来ているとプロジェクト全体が良く回るようになるでしょう。


3つ目の要件の決定ですが、目的が決まる毎にAI屋さんと相談すると良さそうです。
大きな理由は以下の2つです。

  • ある目的のために集められたデータが
他目的に使えるとは限らない

  • サービス要件を満たせるかどうかわからない

例えば、泥臭く集めたデータというのはAI屋さんが目的に応じて特定のルールで集め、目的に応じてクレンジング、加工してある物である場合が多いです。
それらのデータをそのまま利用して、他タスクでも成果を出すというのは非常に困難です。
別の目的で利用した時に考えられる、エラーや課題を許容できる場合であれば良いですが、それらが想定しきれないようであればでデータ集めからやり直しとなってしまう場合もありますので相談はお早めに。

また目的によっては、せっかく作ったモデルがサービス要件を満たせないという事もあります。
シンプルな例としてDeep Learningがあります。
Deep Learningにおける巨大なニューラルネットワークモデルでは、GPU推論で数秒かかってしまう場合があります。毎日数万アクセスがあるサービスで、1ユーザの利用で数秒消費するというモデルを利用するのは、企業として相当な体力が必要です。AI屋さんの端くれである私としては、別モデルの利用、転移学習、枝刈り、蒸留…といった回避策が思い付くのですが、目的が曖昧であるとそれら選択肢から選ぶ事すらできません。
目的は徐々に相談しながら決めていくと、プロジェクト全体を見た時に成功しやすいでしょう。


登壇中も言いましたが、拡大解釈していくとこれらはAIプロジェクトに限った話ではない部分も多いかと思います。
しかしながら、泥臭い作業が多い等、AI屋さんならではの話も含んでいるため、それらを考慮し調整しながらサイクルを回す事で、プロジェクト全体が効率よく回るようになるでしょう。


 

- 登壇の感想とか -

実際の登壇はもう少し実例を出して話をしたとは言え、かなり広い話になってしまいました。
より詳しく技術の話をしても良かったのですが、会社のアレコレが面倒なのと、他登壇者、参加者の様子を見て、初めてAIという広い表題を扱いました。

個人的には今後の登壇について考えさせられる事が多く、良い経験となりました。
会社名義で登壇するのは面倒事多すぎるので、自身の技術を自身で強く磨いていかないとと思いました。


クロスセッションでは、AI関連の質問を受けて他登壇者とセッションしたのですが、夢も希望もない話をしてしまったので、夢と希望が持てる話もネタとして持っておかないとなと思いました。


 

- 他のAIセッションの登壇 -

他のAIセッションの登壇を紹介しておきます。
大体全部オススメです。
ブームもあってか他の部屋も盛況だったようです。

Aidemyのハンズオンは多分Aidemyを登録してやれよという事でしょう!
(あとフューチャーアーキテクトの登壇もあったような…)


 

- おわりに -

MANABIYAというイベントですが、最初に貰った資料がペラペラで信用できず、突然のクラウドファンディングで登壇予定の人がキレてTwitterで辞退宣言したり、連絡のやり取りがめちゃくちゃ遅かったり、懇親会の2次募集すると言ってされなかったりと、「本当にこれだけのエンジニア集めて成功に持っていけるのか・・・?」という不安がめちゃくちゃありました。

当日もバタバタしたスタッフとタイトなスケジュールが組まれていたのですが、何とか全てのタスクが回っていて、大炎上するような人は何とか居なかったようです。

イベント初回でこの規模を、レバレジーズという会社の規模で回しきったというのは、素直にすげえなあと思います。
元学校という会場で学祭のようなわちゃわちゃ感で、それなりのクオリティのイベントに収まっていました。
(クオリティが高いのは登壇者がすごいからか…)

 
このイベントが本当にteratailなるサービスの宣伝になっているのか本当に謎ですが、実際にフォロワーに声をかけてもらったり、AIセッション登壇者内でも交流する事ができ、自分としてはそれなりに良い時間を過ごさせて頂きました。





ありがとうレバレジーズ。ありがとうteratail。
teratail登録はしてみたけど、質問する事がないのでこれから考えます。


追記:
より正しい情報があった

追記2:
その場で質問を壁に貼って誰かが答える面白コンテンツが思い出深かったです。


 

xonshのPROMPTにdatetimeを表示する

- はじめに -

xonshで作業をしているとつい時間を忘れてしまうので、時間を表示してやるメモ。

POWERLINEで良くみるやつをxonshrcで実装。


 

- timeを表示する -

コンソール上の右側に表示するにはこんな感じ

from time import strftime
$RIGHT_PROMPT = lambda: strftime('[ %H:%M:%S ]')

f:id:vaaaaaanquish:20180316235141p:plain
こんな感じ


xonshrcを書く - Stimulatorでも書いた通り、コマンド前は$PROMPT、右は$RIGHT_PROMPT、下にバー状に出しておきたければ$BOTTOM_TOOLBARに関数を突っ込む。


上記をlambda使わずに書くと以下
文字列を返す関数をPROMPT系の変数に突っ込んでおけば、キー入力時に評価される。

from time import strftime
def get_time():
    def prompt():
        return strftime('[ %H:%M:%S ]')
    return prompt
$RIGHT_PROMPT = get_time()

文字色や背景色などのフォーマットは、xonshのprompt-toolkitの普通のやつと似たような感じにすれば良い。


 

- datetimeを出す -

右にあっても見なさそうだったのでPROMPTに出す。

from datetime import datetime as dt
prompt = " {INTENSE_RED}{user}{INTENSE_GREEN}@{INTENSE_BLUE}{hostname}{INTENSE_YELLOW} [ {cwd} ] {GREEN}$ "
$PROMPT = lambda: dt.now().strftime('[ %Y-%m-%d %H:%M:%S ]')  + prompt

キー入力ごとに更新される。
f:id:vaaaaaanquish:20180317003150g:plain


以下参考
https://github.com/xonsh/xonsh/blob/adcd20f72fcbe6962533cb3ad78a4a9ec396e150/xonsh/readline_shell.py
https://github.com/xonsh/xonsh/blob/72f3bc0d089ea91d4e5288bb1c44ebfbe81db43e/xonsh/ptk/shell.py

 

- おわりに -

xonshにはそもそもxonsh.tools内に時間表記のメソッドがあったりするが、イマイチだったのでtime、datetimeを入れてきた方が早そう。
(Tools (xonsh.tools) — xonsh 0.6.4 documentation)


リアルタイムに表示したくなって数日試したけど、あんまり良い方法がなかったのでアイデアが欲しい。
xonshではなくprompt-toolkit側を見ないとダメそうなのでちょっとしんどそう


 

Qiitaの特定記事やタグ付記事をいいね、ストックしているユーザを見るPythonスクリプト

- はじめに -

Qiitaは、プログラミングに関する知識を記録・共有するためのサービスです。

Qiitaアカウントには企業情報が紐付いている場合があり、Qiitaの様々な記事から情報を取得し分析する事で「機械学習を記事を多くストックしている勉強熱心な会社はここだ!」等といった事が分かるのではないかという思いから、Qiitaのアカウント情報を取得するスクリプトを書きました。

その時のただのメモです。


 

- Qiita API -

QiitaにはAPIが存在します。

以下のQiita記事でAPIPythonクライアントを書いている人が既に居たので、こちらを利用します。
Qiita API v2のPythonラッパー実装した - Qiita

pipで導入します。

pip install qiita_v2

以下から「個人用アクセストークン」にread権限を付けて発行します。
https://qiita.com/settings/applications

その際表示されるtokenをメモしておきます。

これで後はいいね数を取得するだけなのですが、実際にこのPythonクライアントの中を見てみると、APIに存在する「いいね数の取得」に対応していませんでした。
qiita_py/client.py at master · petitviolet/qiita_py · GitHub

仕方ないので以下のようにメソッドを追加しつつ、記事のID(stock_id)に対していいねしたユーザとストックしたユーザを取得するスクリプトを書きました。

from qiita_v2.client import QiitaClient

def list_item_likes(self, item_id, params=None, headers=None):
    return self.get("/items/{}/likes".format(item_id), params, headers)
QiitaClient.list_item_likes = list_item_likes

stock_id = ''
token = ''
client = QiitaClient(access_token=token)

# いいねしたユーザ
res = client.list_item_likes(stock_id)
like_users = [x["user"]['id'] for x in res.to_json()]
for i in range(int(int(res.result_count)/20)):
    res = client.list_item_likes(stock_id, params={"page":i+2})
    like_users += [x["user"]['id'] for x in res.to_json()]

# ストックしたユーザ
res = client.list_item_stockers(stock_id)
stock_users = [x["id"] for x in res.to_json()]
for i in range(int(int(res.result_count)/20)):
    res = client.list_item_stockers(stock_id, params={"page":i+2})
    stock_users += [x['id'] for x in res.to_json()]

APIの制限は認証している状態で1000回/時、そうでなければIPアドレスごとに60回/時です。
数記事であれば大丈夫でしょう。


 

- ユーザ情報の取得 -

list_item_likes、list_item_stockersのresponseとなるjsonには""organization""なるキーが含まれています。

しかし、Qiitaプロフィールで表示されるものには「organization」と「Organizations」が存在しており、Qiita Organizationに登録されていない会社は前者、登録されている会社は後者に含まれ、APIでは前者しか取得できないのが現状です。

データ分析のため、後者のOrganizationsを取得したい事からユーザ情報取得のスクリプトを書きました。

import requests
from bs4 import BeautifulSoup
import time

def get_accounts_org(accounts):
    data = {}
    for account in accounts:
        res = requests.get("https://qiita.com/{}".format(account))
        bs = BeautifulSoup(res.text, "lxml")
        organization = [y.attrs["content"] for y in [x.find("meta") for x in bs.findAll("li", attrs={"itemprop":"memberOf"})]]
        host = [x.text.strip() for x in bs.findAll("div", attrs={"class": "newUserPageProfile_info_body"}) if x.find("i", attrs={"class": "fa-building-o"}) is not None]
        data.update({account: {"org":organization, "host":host}})
        time.sleep(5)
    return data

like_orgs = get_accounts_org(like_users)

like_usersリストに入っているユーザ情報から「organization(host)」「Organizations(org)」を取得する事ができました。


 

- 実際に見てみる -

実際に「記事にいいねしたユーザ」について見てみました。

テストとして以下の記事を選びました。
技術共有サービスQiitaで開催されていた2017年Advent Calenderにて、いいね獲得数ランクによってIBM、Greenという会社から表彰された記事ですので、いいね数もそれ相応かと思います。
qiita.com

集計は適当にCounterで

from collections import Counter

d = Counter()
for y in [(k,v) for k,v in like_orgs.items() if v["org"] or v["host"]]:
    for x in y[1]["org"]:
        d[x]+=1
    for x in y[1]["host"]:
        if x not in y[1]["org"]:
            d[x]+=1
for x in d.most_common(10):
    print(x)

結果は以下のようになりました

('株式会社リブセンス', 21)
('株式会社ミクシィ', 5)
('フューチャーアーキテクト株式会社', 4)
('フリーランス', 4)
('スタディプラス株式会社', 4)
('株式会社オールアバウト', 3)
('株式会社モバイルファクトリー', 3)
('アイレット株式会社(cloudpack)', 3)
('IBM Japan', 3)
('株式会社ゆめみ', 3)

記事の著者もリブセンスという会社ですが、やはり同僚の記事はいいねしたくなる傾向にあるのでしょうか。
私も社内SNSに「アイツの記事バズってるよww」みたいに同僚の記事を貼る人間を見たことがあるので、そういう影響もあるでしょう。

いかんせん、データ数が少ないので何も言えませんが、取得して見るまではできました。


 

- Qiitaの特定タグのついた記事を集める -

Qiitaには、記事にタグを付ける機能があります。

特定のタグがついているAPIを叩くため、上記と同じくQiitaClientに新しくメソッドを追加して実行します。

from qiita_v2.client import QiitaClient

def get_tag_items(self, item_id, params=None, headers=None):
    return self.get("/tags/{}/items".format(item_id), params, headers)
QiitaClient.get_tag_items = get_tag_items

tag_name = '機械学習'
token = 'hoge'
client = QiitaClient(access_token=token)

res = client.get_tag_items(tag_name)


ひとまず機械学習タグのついた記事を100 * 20 件分取得してみます。
QiitaのAPI制限を考えると、一度取得した情報はcacheしておくのが正解だと思います(以下では一切やっていません)。

ids = []
users = []
for i in range(100):
    res = client.get_tag_items("機械学習", params={"page":i+1})
    ids += [x["id"] for x in res.to_json()]
    users += [x["user"]["id"] for x in res.to_json()]

上記スクリプトにより、機械学習タグのついた記事を投稿しているユーザ、投稿された記事のidリストが取得できました。


 

- 機械学習記事を投稿、いいねしたユーザ情報を見てみる -

前述したget_accounts_org関数を利用して、機械学習タグ付き記事を投稿したユーザが属する企業、機械学習タグ付き記事をいいねしたユーザが属する企業について、Countをとってみます。

以下が直近2000記事で見た「機械学習タグのついた記事」を多く投稿する社員のいる企業です。

('TIS株式会社', 6)
('株式会社トップゲート', 5)
('株式会社ブレインパッド', 4)
('Team AI', 4)
('株式会社リブセンス', 4)
('株式会社 ドワンゴ', 4)
('Nuco Inc.', 4)
('株式会社Nextremer', 4)
('株式会社アカツキ', 3)
('株式会社クラウドワークス', 3)

Qiitaで機械学習記事といえば、あのアカウントとあのアカウントとあのアカウントかな…?という一覧になりました。
データが増加してもQiita活動家が社内に居るか居ないか、に寄ってしまいそうです。

これ以下はほぼ1~2だったので面白い結果を得るまで数を増やすのは少し大変そうです。
 
 
以下は直近2000記事で見た「機械学習タグのついた記事」にいいねを多くした社員のいる企業です。

('株式会社リブセンス', 31)
('Fringe81株式会社', 24)
('株式会社クラウドワークス', 22)
('TIS株式会社', 21)
('株式会社VASILY', 16)
('株式会社リクルートライフスタイル', 12)
('Retty株式会社', 12)
('フューチャーアーキテクト株式会社', 11)
('P&D – Planning and Development – ', 11)
('株式会社アカツキ', 11)
('Shinonome, inc.', 11)
('株式会社LIFULL', 10)
('株式会社トップゲート', 10)
('株式会社オールアバウト', 10)
('株式会社ゆめみ', 9)
('フリーランス', 8)
('株式会社 ドワンゴ', 8)
('株式会社Nextremer', 8)
('株式会社ACCESS', 8)
('株式会社エイチームライフスタイル', 8)
('株式会社エイチームブライズ', 8)
('株式会社BitStar', 8)
('The University of Tokyo', 7)
('株式会社div(ディブ).', 7)
('株式会社WACUL', 6)
('株式会社Fusic', 6)
('Kyoto University', 6)
('株式会社Rosso', 6)
('株式会社サイバーエージェント', 6)
('エムスリー株式会社', 6)

リブセンスという会社はQiitaに相当な時間を使っているという事がわかりました。
こちらも上位は「あのアカウントかな…」と個人が見え隠れする結果となりました。

こちらは、東大、京大やフリーランスがトップ30に入ってきた辺りを見るに、データが集まれば少し改善しそうです。


 

- おわりに -

APIの制限数によって1日ではこの程度でした。

多くデータを集めた上で、「機械学習タグのついた記事にいいねするユーザ」の数を企業単位で集計することで、特定の技術に興味関心が強い企業が見られるかも…という結果になりました。

Qiita APIではユーザに紐付いたLinkedinやFacebookTwitterのIDも取得できるため、次回はそれらを含めた集計を出せればと思います。


 

『人工知能プログラミングのための数学がわかる本』が機械学習研究入門書としてとても良さそうだった

- はじめに -

本を読んで筆者に媚を売る記事シリーズです。

人工知能プログラミングのための数学がわかる本」という書籍を筆者の石川 聡彦(Aidemy)@akihiko_1022さんから譲り受けました。

人工知能プログラミングのための数学がわかる本

人工知能プログラミングのための数学がわかる本

明日2/24発売ですが、筆者である石川さんがCEOを務めるAidemyさんと人工知能機械学習のイベントにてご縁があり頂く形になりました。


そもそもAidemyは、Python及び機械学習のための知識と実装に関する学習を行えるWebサービスです。

aidemy.net

似たサービスではUdemy(https://www.udemy.com/jp/)というアメリカのサービスがかなりのシェアを誇っています。

Aidemyは後発ですが、丁寧な日本語解説と内容の質の高さから、機械学習界隈でも「Aidemyは良い」という声を聞く程優良なサービスです。
こういった初学者向けのサービスの僅かなミスを論う意地の悪い界隈でも評判が良いのがすごい。

私自身も最初のコースだけやりましたが、よく出来たWebエディタと正しい導き方を見て素晴らしいなと思いました。


筆者がそのAidemy CEOの石川さんという事で、丁寧な導きと潔い切り口で書かれた本でした。


 

- どんな人が読むと良さそうか -

個人的には以下のような人にオススメです
- 機械学習の研究室に入りたい、研究をはじめたい
- 機械学習の論文が読みたい
- 機械学習における数式を噛み砕いて理解したい
- 高校、大学数学の知識を呼び戻したい
- これからMLPシリーズや高レベルな書籍を読む

とにかく「機械学習の研究室に一冊あるとめっちゃ捗る」。
これだけは間違いなく声を大にして言えます。
研究入門として素晴らしい構成です。

以下書籍の雰囲気です。

f:id:vaaaaaanquish:20180223211609j:plainf:id:vaaaaaanquish:20180223211600j:plain
めっちゃ丁寧できれい

本当に高校数学から大学における確率、線形代数までこの優しさで解説されているのでGoodです。

 
対して「機械学習を業務でやる」「データサイエンスの知識が欲しい」人には直結して学習効果の高い本ではなさそうです。

直結して効果が薄い、というのは実際の業務や実装では「これくらい知ってて当然」という場合が多々あると思われるからです。私の知っている機械学習エンジニア各位なら、10分で読了して、内容に準じた小テストまでこなせるでしょう。
ただそういった人達でも、自分の復習と理解の落とし込みのためであればかなり良い書籍であるという事は間違いないと思いました。

加えて「数学、線形代数をより理解したい」という人は満足できないでしょう。高校、大学数学の基礎から、機械学習への導入の持っていき方が素晴らしい書籍であって、定義を明確に数式を展開していくものではないです。


 

- 書籍の良かったところ -

前述の通り、高校、大学数学の基礎から、機械学習への導入の持っていき方は非常に滑らかに感じました。

数学といっても本当に2次方程式平方根、指数関数といったレベルから説明が入ります。
三角関数、集合、行列、ベクトル、確率、…と進み総復習のような形です。

それらの説明に対し全ての節に「人工知能ではこう使われる!」という説明が入っています。
これがなかなか一般的な書籍にない所で、「この為に勉強してるんだ」という実感が持てる所が、入門書としての格を上げています。

f:id:vaaaaaanquish:20180223230129j:plain:w400
こう使われる!

また、数式内にも色や線を利用して丁寧に解説が挟まれていたり、イメージの図もより優しい表現が使われています。

 
機械学習の仕組みを読み解く上での基礎となるワードが配置されているかつ、実際の機械学習に使われる箇所まで説明される書籍というのはなかなか無く、研究をはじめる前に読んでおくことで他の少しレベルを上げた書籍が一気に読みやすくなるでしょう

そういった点からタイトルを「機械学習研究入門として良さそう」としています。

 
加えて、最後の章では実際に「Boston Housing Dataset」「青空文庫」「MNIST」といった入門向けデータセットを利用してデータ分析、自然言語処理、画像認識の実体験を進める事ができます。

これらもコードがGithubで見られるようになっており、実際に学んだ知識を使いながらスムーズに体験できる所が最も素晴らしい所だと思います。

 
あと以下の記事の時にも書きましたが、こういった書籍のコラムは本当に良いです。

vaaaaaanquish.hatenablog.com

機械学習界隈で使われるワード」というのは意外と外に出回るものではありません。
機械学習界隈の人間同士の会話の中で自然に出てくるワードの知見が得られるのも書籍の良いところだと改めて感じました。


 

- 書籍で足りないところ -

導入までが素晴らしい書籍ですが、「じゃあこの本で得た定義で機械学習の研究室や勉強会でドヤ顔できるか」と言われたらできないでしょう。

最終章でDeep Learningニューラルネットワーク誤差逆伝播法、勾配法をピックアップしてより詳しく説明していますが、それでもさらに話を進めて研究レベルに持っていくには一歩足りないイメージです。
ただ「連続とは何か」みたいなレベルから書いていたら辞書みたいなサイズになってしまうので、どこかで情報を切らないといけない訳ですが、そういう意味では潔い書籍であるとも言えます。
さらに深く学ぶにはより専門的な書籍を探しましょう。

(これはつまり「この後Aidemyをやれ」という事なのかも…)

 
個人的にはこれ以上批判すべき所がなく「面白くねえな…」「これ普通に高専で研究始める前とかに読みたかったわ…」となりました。


 

- おわりに -

「松尾豊氏推薦!」という強めのワードと、なかなか可愛い表紙が特徴的な一冊です。

あと姑息な宣伝ですが、そんなAidemyの石川さんと私が登壇するMANABIYAというイベントがあるらしいです。
石川のハンズオンは既に満員みたいですが是非私の与太話を聞きに来て下さい。
manabiya.tech


今回Aidemyのステッカーも貰ったので、さらに媚を売るためにPCに貼った写真で終わりにしたいと思います。

f:id:vaaaaaanquish:20180223205736j:plain:w400
かわいい表紙

普通に研究室や会社に一冊あると、ふとした時に復習できる良書籍だと私は思いました。

 

人工知能プログラミングのための数学がわかる本

人工知能プログラミングのための数学がわかる本

 

共同通信と朝日新聞の記事URLを含むツイートを削除するPythonスクリプト

- はじめに -

この記事の起点となったのは、2018年1月25日、共同通信が配信した「山中氏、科学誌創刊に深く関与か」というタイトルの記事が、同日午後8時頃「山中所長が給与全額寄付」というタイトルの記事に書き換えられていた件である。

下記ツイートの通り、追記や編集の知らせ無しにネットメディアが大幅に修正された場合、記事公開当初と意見の辻褄が合わず、自身の発言に責任が取れなくなる場合がある。


自身が良いと共有した記事が卑猥、卑劣な記事になっている場合を防ぐため、特定のメディアの記事に言及していたツイートを削除するスクリプトについてメモしておく。


事前に必要な要件は以下の通り


 

- スクリプト -

最初に全体のスクリプト

API周りのKeyは、http://phiary.me/twitter-api-key-no-japanese から電話番号をアカウントに紐付けた後、https://apps.twitter.com/ にて取得する。

全ツイート情報を含むCSVは、Twitter公式のSettingsから、全ツイート取得の申請を出すと登録しているメールアドレスに30分程でダウンロードリンクが送られてくる。

f:id:vaaaaaanquish:20180126231631p:plain:w300

import tweepy
import urllib
import csv
import urllib.request

CONSUMER_KEY = ''
CONSUMER_SECRET = ''
ACCESS_TOKEN = ''
ACCESS_SECRET = ''
CSV_PATH = 'tweets.csv'
DOMAIN_LIST = ["this.kiji.is", "www.asahi.com"]

auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
auth.set_access_token(ACCESS_TOKEN, ACCESS_SECRET)
api = tweepy.API(auth)


def expand(url):
    req = urllib.request.Request(url, method='HEAD')
    resp = urllib.request.urlopen(req)
    return resp.url


def expand_url(url):
    eurl = expand(url)
    while eurl != url:
        url = eurl
        eurl = expand(url)
    return eurl


def main():
    with open(CSV_PATH, 'r') as f:
        reader = csv.reader(f)
        header = next(reader)
        for i, row in enumerate(reader):
            flag = False
            for x in list(set(row[9].split(","))):
                if x != "":
                    try:
                        y = expand_url(x)
                        for domain in DOMAIN_LIST:
                            if urllib.parse.urlparse(y).netloc == domain:
                                flag = True
                    except KeyboardInterrupt:
                        raise
                    except:
                        pass
            if flag:
                api.destroy_status(row[0])

if __name__ == '__main__':
    main()

print等は適宜。

ツイートが削除されるスクリプトなので確かめながら使う。


 

- 適当な色々 -

以下は駄文である。

このスクリプトを書くにあたっての実験的な色々とか。

短縮URLの展開がurllibだけでできるようになってた

短縮URL 展開 Python」みたいに適当にググると、Python2系のhttplib.HTTPConnectionを使ってHEADメソッド投げるスクリプトが沢山でてくるのは知ってたけど、3系からurllib.request.RequestでHEADできるの知らなかった。

Python3系で短縮URLを展開するのは以下みたく

import urllib.request
root_u = "http://hogehoge"

def expand(url):
    """curl --head url"""
    req = urllib.request.Request(url, method='HEAD')
    resp = urllib.request.urlopen(req)
    return resp.url

def expand_url(url):
    """短縮URLをできるだけ展開する"""
    eurl = expand(url)
    while eurl != url:
        url = eurl
        eurl = expand(url)
    return eurl

print(root_u, expand_url(root_u))

個人的には使い所は今のところないがハッピーな気がする

 

CSVの読み込み

CSVは大体30分くらいでメールが来て、ツイート数226Kで40Mくらいのzipになってた。
前回ダウンロードした時より大分大きくなってる気がした。

試しに1000件くらいやってみたけど、割りと接続できないURLがあったので、そちらも削除した方が良いような気がした。

import urllib
import csv
with open('tweets.csv', 'r') as f:
    reader = csv.reader(f)
    header = next(reader)
    for i, row in enumerate(reader):
        # 複数URLはカンマ区切り
        for x in list(set(row[9].split(","))):
            if x != "":
                try:
                    y = expand_url(x)
                    print("base: {}\nexpanded: {}\ndomain: {}".format(x, y, urllib.parse.urlparse(y).netloc))
                except KeyboardInterrupt:
                    raise
                except:
                     print("Not Found : ", x)
        if i > 1000:
            break

headerはtweet_idとexpanded_urlsしか使ってないけど、textとか時間も考慮した方が良い気がした。

 

記事の書き換えについて

全体を通して嫌な気分になる話だったが、解決方法が見当たらず難しい問題だと思う。

日本の今の社会形態からして、記者が常に誠実かつ知識を多く習得し続けるというのは難しいだろうし、それらを補正するには専門家の意見を割く事になる。
記者側としても「スピード感持って数多くの読まれる記事を出したい」という気持ちは強いだろうし時間の制約は大きい。

その点ネットでは公開直後から意見が集まる訳なので、今回あくまで修正方法が下衆だったという話にして、今後「指摘があったので修正しました」「間違っていたので差し止めます」が気軽に言える社会になっていけば良いなと思う。

法律の範囲内であれば、間違う事自体は決して悪い事ではない。


 

- おわりに -

花金の飲み会おわりの勢いで書いた。

間違ってたら修正すればええねん。


 
追記 2018/01/27 0:14 :
ミスを指摘されたのでサイレントで修正しました。