『プロダクトマネジメント―ビルドトラップを避け顧客に価値を届ける』を読んだ
プロダクトとは価値を顧客に運ぶことで、プロダクトマネージャーはプロダクト戦略をたてる。なぜこのプロダクトをつくっているのか、どんなアウトカムを生み出すのかをチームが忘れないようにする。
アウトプットではなくアウトカム志向で、というのはなるほど〜と思った。
組織やプロダクトマネージャーのアンチパターン(というかプロダクト主導じゃないパターン)が書かれてあってあの会社はどれに当てはまるかな、あの人はどうかな、とか考えながら読める。
プロダクトマネージャー、プロジェクトマネージャー、プロダクトオーナーの違いがわかる。
後半はもっと面白い。価値を顧客に運ぶためにチームメンバーやステークホルダーがどう思考してどう実験していくかのストーリーが面白い。プロダクトにとっての顧客に価値を運ぶことに直接関係のないことが一切出てこないのが最高だった。スクラムでイテレーション〜やレトロスペクティブ〜みたいな話が出てこずひたすらアウトカム志向でどう顧客の課題を知りどう実験していくかに集中できる本。
『エンジニアの知的生産術』を読んだ
社会人にとってやる気は貴重なリソース、というのはマジでそうだなと思った。
3,4章を読んで、自分には本をあまり時間をかけずに一度読んで、その後他の本を読むときとか何かしらのアウトプットをするときに何となくキーワードをもとに引っ張ってこれるくらいでいいかなと思った。何度も読む前提の読み方。情報は必要なタイミングで必要になるだろうから。輪読会でいくらメモしたり議論しながら読んでも1ヶ月後にはほとんど覚えてないので。とにかくたくさん自分の頭の中の棚に置いておく必要があるなと。
5,6章あたりはKJ法よさそう。付箋にとにかく書き出してボトムアップで分類していくのはなるほどと思った。
最後に学ぶ戦略というかT型人材とかπ型人材に触れてておぉ〜ってなった。あと未来に向かって点は繋げられない、過去を振り返って点を繋ぐしかできないから今やってるのを信じろみたいな話もおぉ〜てなった。
複数のspiderで異なるpipelineを通す
scrapyは使用するpipelineを全て定義する必要があり、普通に書くとどのspiderでも定義した全てのpipelineを通るようになっている。これをそれぞれのspiderで、指定したpipelineだけを通るようにする実装のメモ。
例として、slack_botとtweet_botの2つのspiderを定義する。
import scrapy class SlackBotSpider(scrapy.Spider): name = 'slack_bot' # この値を判定につかう def parse(self, response):
import scrapy class TweetBotSpider(scrapy.Spider): name = 'tweet_bot' # この値を判定につかう def parse(self, response):
slack_botとtweet_botそれぞれ専用のpipelineを1つ、共通のpipelineを1つ定義したとすると、以下のように専用のpipelineのprocess_item内で、spider.nameの値をチェックすれば良い。
class SlackPipeline: def process_item(self, item, spider): if spider.name not in ['slack_bot']: return item print('SlackPipeline') yield item ~~~省略~~~ class TweetPipeline: def process_item(self, item, spider): if spider.name not in ['twitter_bot']: return item print('TweetPipeline') yield item ~~~省略~~~ class SaveFilePipeline: def process_item(self, item, spider): print('SaveFilePipeline') yield item
settings.pyには全てのpipelineを列挙する必要がある。
ITEM_PIPELINES = { 'my_crawler.pipelines.SlackPipeline': 300, 'my_crawler.pipelines.TweetPipeline': 400, 'my_crawler.pipelines.SaveFilePipeline': 500, }
これで、slack_bot実行時はSlackPipeline->SaveFilePipeline、tweet_bot実行時はTweetPipeline->SaveFilePipelineを通すことができる(厳密には全てのpipelineのprocess_item()は呼ばれるので、処理をスキップすると言った方が正しい)。
$ scrapy crawl slack_bot SlackPipeline SaveFilePipeline $ scrapy crawl tweet_bot TweetPipeline SaveFilePipeline
参考:https://groups.google.com/d/msg/scrapy-users/msKQ7UaYh_E/ee8WSMPRpq0J
Pythonで25時のような表記をdatetimeに変換する
そのままdatetimeにしようとするとエラーが発生する。
import datetime dt_str = '2020/06/07 25:05' d = datetime .datetime .strptime(dt_str, '%Y/%m/%d %H:%M') # ValueError: time data '2020/06/07 25:05' does not match format '%Y/%m/%d %H:%M'
なので、日付部分をdatetime、時刻部分をtimedeltaとして生成し、両者を足せばOK。
import datetime dt_str = '2020/06/07 25:05' d_str = dt_str[:10] # 2020/06/07 t_str = dt_str[11:] # 25:05 hour = float(t_str[:2]) # 25 minute = float(t_str[3:]) # 05 d = datetime .datetime .strptime(d_str, '%Y/%m/%d') # 2020-06-07 00:00:00 delta = datetime .timedelta(hours=hour, minutes=minute) # 1 day, 1:05:00 dt = d + delta # 2020-06-08 01:05:00
Python3.7
PythonでTwitter自動投稿botをつくるときに調べたもの
ウェブサイトを定期的にスクレイピングし、自然言語処理をしてその結果をもとにTwitterに投稿するbotをつくった。その際に必要だった技術、調べたもののメモ。
クローラー系
scrapy
Pythonでクローラーがつくれるフレームワーク。大変お世話になりました。
今回でいうと自然言語処理をするパイプラインとTwitterに投稿するパイプラインの2つを実装した感じ。
cssセレクタ一覧
相対パスを絶対パスに変換
import scrapy class MySpider(scrapy.Spider): def parse(self, response): relative = response.css('a::attr(href)').extract_first() absolute = response.urljoin(relative)
ref https://docs.scrapy.org/en/latest/topics/request-response.html#scrapy.http.Response.urljoin
scrapyのロギング最低レベル変更
settings.pyに追記
LOG_LEVEL = "INFO"
ref https://doc-ja-scrapy.readthedocs.io/ja/latest/topics/settings.html#std:setting-LOG_LEVEL
Python言語系
文字列を○文字まで切り取る
# 'あいうえお' five_str = 'あいうえおかきくけこ'[:5]
文字列をdatetimeに変換
import datetime date_str = '2020.06.01 12:00' date = datetime.datetime.strptime(date_str, '%Y.%m.%d %H:%M')
ref Pythonで文字列 <-> 日付(date, datetime) の変換 - Qiita
JST現在時刻をdatetimeで取得
import datetime dt_jst = datetime.datetime.now( datetime.datetime.timezone(datetime.datetime.timedelta(hours=9)) )
ref Pythonで現在時刻・日付・日時を取得 | note.nkmk.me
datetimeの差を求める
タイムゾーンを考慮したdatetimeと、考慮していないdatetimeは計算できない。
import datetime dt = datetime.datetime.now() dt1_jst = datetime.datetime.now( datetime.datetime.timezone(datetime.timedelta(hours=9)) ) dt2_jst = datetime.datetime.now( datetime.datetime.timezone(datetime.timedelta(hours=9)) ) # TypeError: can't subtract offset-naive and offset-aware datetimes diff = dt - dt1_jst # OK diff = dt1_jst - dt2_jst # diffはtimedeltaオブジェクト diff.total_seconds() # 0.0
ref python - Can't subtract offset-naive and offset-aware datetimes - Stack Overflow
ref datetime --- 基本的な日付型および時間型 — Python 3.8.3 ドキュメント
docstring
Googleが公開しているdocstringガイドか、numpyというのが主流らしい。
ref styleguide | Style guides for Google-originated open-source projects
ref numpydoc docstring guide — numpydoc v1.1.dev0 Manual
ref [Python]可読性を上げるための、docstringの書き方を学ぶ(NumPyスタイル) - Qiita
カレントディレクトリの絶対パスを取得
import os
path = os.getcwd()
ref os --- 雑多なオペレーティングシステムインタフェース — Python 3.8.3 ドキュメント
自然言語処理系
janome.Tokeniserでユーザー辞書を使う
janomeは簡略辞書というフォーマットを使えたため、比較的簡単にユーザー辞書を追加することができました。
from janome.tokenizer import Tokenizer # uidc.csvの中身の例↓ # あつ森,カスタム名詞,アツモリ # とたけけ,カスタム名詞,トタケケ t = Tokenizer( udic='udic.csv', udic_type='simpledic' ) t.tokenize(text)
ref Welcome to janome's documentation! (Japanese) — Janome v0.3 documentation (ja)
特定品詞のみ抽出
from janome.tokenizer import Tokenizer for token in t.tokenize(text): if (token.part_of_speech.startswith('名詞,固有名詞') or token.part_of_speech.startswith('名詞,一般') or token.part_of_speech.startswith('形容詞,自立') or token.part_of_speech.startswith('カスタム名詞')): print(token.surface)
Twitter API系
Twitter API 利用申請手順
全て英語で書かなければならず、さらに○○文字以上の制約がある項目もあり少々面倒。DeepL翻訳にお世話になりました。
ref 2020年度版 Twitter API利用申請の例文からAPIキーの取得まで詳しく解説 | 新宿のホームページ制作会社 ITTI(イッティ)
PythonのTwitter APIクライアント
いろいろ見つかったが今回はこれを使用。
ref https://pypi.org/project/twitter/
Heroku連携系
秘匿情報をHerokuのConfig Varsから注入
Twitter APIの認証情報などはソースコードにベタ書きしたくないため、Herokuの機構をつかって環境変数にいれます。Pythonから環境変数を取得する方法は下記のとおり。
import os os.environ.get('SECRET_VALUE', 'default value')
ref https://devcenter.heroku.com/articles/getting-started-with-python#define-config-vars
Heroku Schedulerで定期実行
Herokuのアドオンで追加できる。Dyno Sizeが無料だと10分ごと、○時間ごと、○日ごと、の3パターンが使える。
Flutterで自作パッケージをpub.devに公開する
基本的にDeveloping packages & pluginsを参考に進めればOK。
パッケージを生成する
テンプレートには二種類ある。今回はDart packagesとして作成するので以下のコマンドで生成。
flutter create --template=package {package_name}
生成されたら、lib/{package_name}.dart
を編集する。
サンプルプロジェクトを追加する
--template=packageで生成したプロジェクトにはtestしか含まれてないので、必要に応じてサンプルプロジェクトを追加する。
パッケージのカレントディレクトリで以下のコマンドでexampleというプロジェクトを生成する。
# in {package_name} flutter create example
生成されたら、example/pubspec.yaml
のdependenciesに自作パッケージを追加。
dependencies: flutter: sdk: flutter {package_name}: path: ../
その後example/main.dart
に自作パッケージのサンプル実装を記述する。
README.md, CHANGELOG.md , pubspec.yaml, LICENSEの編集
pub.devに公開するにあたってこれらのファイルを正しく編集する。
基本的にそれぞれデフォルトで例が書かれてあるので参考に。LICENSEは他のパッケージを参考に。
CHANGELOG.md
## [0.0.1] - 2019-11-04 * first release
pub.devに公開
以下のコマンドでpub.devにpublishできる。
flutter packages pub publish
途中認証のためにURLをひらく必要がある。認証に成功すればこんな感じ。
認証完了後、pub.devにアップロードが成功すると晴れて自作パッケージが公開される。
Flutterで1文字目だけスタイルを変える
Text.rich
とTextSpan
を使う。TextSpanで設定したstyleはchildrenに引き継がれるので、childrenのTextSpanで上書きする。
final String text = "Flutter";
Text.rich( TextSpan( text: text.substring(0, 1), style: TextStyle( fontSize: 36, color: Colors.blue, letterSpacing: 1, ), children: <TextSpan>[ TextSpan( text: text.substring(1), style: TextStyle( fontSize: 18, color: Colors.black87, ), ), ], ), ),