IT業界のすみっこ暮らし

ふと気がついたときの記録

業務によく使うツール一覧

随時追加予定。

Win merge

WinMerge 日本語版

Fiddler

e-algorithm.xyz

Color Picker

www.vector.co.jp

Linq pad

LINQPad - The .NET Programmer's Playground

ChatworkがChromeのシークレットウィンドウでは閲覧できない件

面白いことだと思ったので簡単にメモしておきます。

※この記事は2018/02/16 19時時点の内容になります。

1、前提

私の職場は業務にChatworkを使っています。

…そろそろSlackに移行してもいいと思うけど(ry

2、画面がおかしい

2018/02/15 18:45辺りからChromeのシークレットウィンドウからChatworkが表示できなくなりました。

f:id:papamau:20180216190124p:plain

こんな感じで真っ白になりコンテンツが確認できません。
ちょうどそのタイミングで書き込みをしていたので、分かりやすかったですね。

3、Consoleの確認

f:id:papamau:20180216184727p:plain

CORS policyでcss読み込み失敗
Access to CSS stylesheet at 'https://front.chatwork.com/build/208f9f0cd78979aa34e3797d0a35cbbf5ff70780/app/index.css' from origin 'https://kcw.kddi.ne.jp' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://kcw.kddi.ne.jp' is therefore not allowed access.
上記の失敗の影響でエラー
(index):42 Uncaught TypeError: Cannot read property 'left' of undefined
    at e.adjustInformationPanelPosition (index.js:24705)
    at e.resizeLayout (index.js:24596)
    at Array.<anonymous> (index.js:24402)
    at e.endInitLoad (index.js:24567)
    at e.buildMyStatus (index.js:23496)
    at e.buildMyStatus (index.js:14383)
    at e.updateContactList (index.js:14379)
    at e.setInitData (index.js:14642)
    at index.js:14521
    at Object.e.success (index.js:14587)

4、他のブラウザーでは普通に動く

f:id:papamau:20180216184756p:plain


5、さいごに

おそらくセキュリティ強化でこうなったかなと思うけど(おそらくCORS関連)、WEBの開発のときはJSやCSSのキャッシュのリフレッシュなどが楽でシークレットウィンドウをよく使っていた身としてはちょっとビックリしました。


以上

このブログのアクセス数について

ごく稀にこのブログのアクセス数について話をする場合があるので、その証拠?として最近のグラフを元に簡単に雑談をしたいと思います。

直近1ヶ月のアクセス数

f:id:papamau:20180205180813p:plain

あくまでもはてなブログ基準で厳密なpv数とは異なると思いますが、管理にあたって気にしているのはダッシュボードにあるこのグラフだけなのでこれにしました。

私は主に仕事をする際に調べたものを記事にしてるからか、平日は平均200ちょいくらい、土日祝日は50前後ですね。

stackoverflow.blog

上記の記事みたいに、みんな平日は業務に関するネタ、土日祝日は趣味のネタって感じで探してるからアクセス数もはっきり別れるんだと思います。

感想

作りたての頃は一日に1〜2しかアクセスがなかったのに、いつの間にか記事を見てくれる方が増えてきてとても嬉しいと思っています。

よく閲覧される記事をみてると、みんな同じところでハマっているんだなーと妙な仲間意識も湧きます。

まとめ

このブログのテーマは「ふと気がついたときの記録」なので、わざわざアクセス数を増やそうとは全く思っていないのですが、それでも見てくれる方がいることは嬉しいことです。

これからも会社の守秘義務を守りつつ、自分の業務で関わった技術ネタなどを投稿していければと思います。よろしくお願いします。

Bonfire API #1に参加してきました

f:id:papamau:20180202211559p:plain

イベント概要

ヤフー主催の技術・デザインコミュニティ「Bonfire」の中で、APIやサーバーサイド技術にフォーカスした情報共有を定期的に行う勉強会/交流会イベントです。

開発する上で浮かんでくる課題や、目まぐるしく進化を続ける市場環境とどう向き合っているか、 そのときの課題や取り組みについて共有することで、

  • 新しい知見、発見を得る
  • 技術交流の輪を広げる

ことができる場を目指しています!


ということで、大変興味があったので参加してきました。

yj-meetup.connpass.com


相変わらず道にはかなり迷いましたが、無事たどり着きました。
場所は下記イベントのときも行ったことのあるYahoo! JAPAN内のコワーキングスペース「LODGE」 pie001.hatenablog.com

感想

APIを使う側、提供する側、リージョン管理をしないといけない側などなど色んなケースの話が聞けてとても面白かったです。

特にAPIリトライの話はどの業務においても応用がし易い話だったので一番印象的です。

またこういう技術共有会があれば、是非参加して見たいと思いました。

さいごに

昨夜はまた雪が降ってて帰りが心配だったので、講演が終わった後に懇親会は参加せず早々に退場しました。参加してもっと話聞いておけばよかった〜〜と今更少し後悔してます。雪エ…

はてなブクマを眺めていたら気になるところがあった

今日も楽しくはてなブクマを眺めていたらふと気付いた。

f:id:papamau:20180201102235p:plain


あれ?タイトル(aタグ)がはみ出てる

f:id:papamau:20180201102511p:plain

「!!!!!!!」が悪さをしてるみたい?

f:id:papamau:20180201102919p:plain


因みにcssはこんな感じだった。

contents.css
div.entry-contents h3 a {
    padding: 8px 0;
    color: #303030;
    text-decoration: none;
    word-break: break-all;
}


面白いと思ったので、解決方法を考えてみた

といってもword-break: break-word;にしただけだけど。

f:id:papamau:20180201103720p:plain

contents.css
div.entry-contents h3 a {
    padding: 8px 0;
    color: #303030;
    text-decoration: none;
    word-break: break-word; /* break-all -> break-word */
}


これですっきり。

f:id:papamau:20180201103916p:plain


久々にCSSの復習にもなったので参考サイトも貼っておく。 developer.mozilla.org

+ 2018/02/01 10:50時点の話でした~~~

.dotPeek:Free .NET Decompiler

.dotPeek

Free .NET Decompiler and Assembly Browser www.jetbrains.com

検証

下記の記事で使ったソースを逆コンパイルしてみました。

ASP.NET MVC サイトのサムネイル画像を作成しサイト内で表示する - IT業界のすみっこ暮らし

f:id:papamau:20180122102739p:plain

日本語のコメントも文字化けなくコード化できました。

2017年のActivityを振り返る

GitLabのActivity in 職場

f:id:papamau:20180117185816p:plain

ほぼ毎週何かしらの案件でコードを書いたりコードレビューしたりしたのでそこそこ青いのかなー?と思います。

さすがにin職場だから土日のActivityがないことについては笑うところというか良かったねというか。

基本的に1案件は1人で行って、コードレビューも自分がされる場合はあまりなく、後輩のコードをたまーに見る場合の方が多いので色が濃くないのは残念なところというか、普段一日に30回以上コミットしたりコメント付けたりするものかー?一々細かくコミットしたり、炎上してるところなら分からんでもないけど?!?と思う今日この頃です。

個人用のGitHubは基本的にアップしたら放置なんでちょっと寂しい感じですけど、今年は何かしら常に更新していきたい何かを作りたい気持ちです。

というか個人プロジェクトはぶっちゃっけBitbucketが良いんだよなー

C#:API呼び出し

前提

リクエスト仕様

POSTメソッドのJSON形式

レスポンス仕様

JSON形式で返却

API呼び出し

レスポンスのjson文字列は個々のModelに格納するのもいいけど、そのまま使うならJObjectが良いかなと思ったり…

GetResponse
public JObject GetResponse(string apiUrl, string jsonParameter)
{
    JObject response = null;
    try
    {
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(apiUrl);
        request.Method = "POST";
        request.ContentType = "application/json;";
        // カスタムヘッダーが必要の場合(認証トークンとか)
        request.Headers.Add("custom-api-param", "value");

        using (var streamWriter = new StreamWriter(request.GetRequestStream()))
        {
            streamWriter.Write(jsonParameter);
        }

        var httpResponse = (HttpWebResponse)request.GetResponse();

        // HttpStatusCodeの判断が必要なら
        if(httpResponse.StatusCode != HttpStatusCode.OK)
        {
            throw new Exception("API呼び出しに失敗しました。");
        }

        using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
        {
            response = JObject.Parse(streamReader.ReadToEnd());
        }

        // ex) response["status"]:"success"
    }
    catch (WebException wex)
    {
        // 200以外の場合
        if (wex.Response != null)
        {
            using (var errorResponse = (HttpWebResponse)wex.Response)
            {
                using (var reader = new StreamReader(errorResponse.GetResponseStream()))
                {
                    response = JObject.Parse(reader.ReadToEnd());
                }
            }
        }
    }
    
    return response;
}

API呼び出し

var apiUrl = "http://apiurl/v1/getHogepoyo"
var jsonParameter = new JavaScriptSerializer().Serialize(new
{
    name = "Name",
    email = "EmailAddress",
    password = "Password",
    detail_info = new {
            info1 = "info1",
            info2 = "info2"}
    });

JObject response = GetResponse(apiUrl, jsonParameter);
広告を非表示にする

PythonでWebスクレイピング:Pixivのブクマ情報をファイルで記録

ソースコードが大変汚いけど、一応バックアップも兼ねてアップします。

ブクマした小説情報(ユーザー/キャプション/タグ/本文)をテキストファイルに保存するのが目的です。単体で作品を保存する場合は色んなブラウザの拡張機能があるので、それらにお世話になっていますが、小説作品だとキャプションとタグも一緒に保存したいなと思いpythonの勉強を兼ねて個人的に試してみました。

考えたのはざっくりとこんな感じでした。

  • pixivへのログイン処理
  • ブクマの情報取得
  • 削除/マイピクなどで非公開になった作品は除外
  • キャプション/タグ情報取得&pixiv専用タグ?([jump:5?]みたいなやつ)は除外

応用すれば特定タグやブクマ数の作品を抽出とか色々出来そうだけど、それは追々考えます。

余談ですが、すごく粗末なソースコードではありますが、0からのスタートだったのでPythonでの文字列の扱いとかhtmlパーシングに関しては大変勉強になったと思います。

#!/usr/bin/env PYTHONIOENCODING=UTF-8 python3
# -*- coding: utf-8 -*-
import sys
import io
import re
from robobrowser import RoboBrowser
from bs4 import BeautifulSoup

#タグ除去用
p = re.compile(r"<[^>]*?>")
# [jump:1]形式除去用
jump = re.compile(r"\[jump:.+\]")
#ファイルエンコード設定用
character_encoding = 'utf8'
pixiv_url = 'https://www.pixiv.net'
browser = RoboBrowser(parser='lxml', history=True)
browser.open('https://accounts.pixiv.net/login')
form = browser.get_forms('form', class_='')[0]
form['pixiv_id'] = '***'
form['password'] = '***'
browser.submit_form(form)
page = 1
target_url_bkm = 'https://www.pixiv.net/novel/bookmark.php?rest=hide&p='
browser.open('https://www.pixiv.net')

title = browser.find(class_='user-name').string
print(title)
print('=====================================================')
for i in range(1):
    print(target_url_bkm + str(page + i))
    browser.open(target_url_bkm + str(page + i))

    novel_items = browser.find(class_='novel-items')

    for novel in novel_items.find_all(class_='_novel-item'):
        soup = BeautifulSoup(str(novel), "html.parser")
        #print(soup.find('h1', class_='title').a)
        #print(novel)
        a_tag = novel.find('h1', class_='title').find('a')

        # novel info
        novel_url = a_tag.get('href')
        novel_title = a_tag.get_text()

        if novel_title != "-----":
            # user info
            user_tag = novel.find('a', class_='user')
            user = user_tag.get_text()
            data_user_id = user_tag.get('data-user_id')
            print(data_user_id)

            series_title_elm = novel.find('a', class_='series-title')
            if novel.find('a', class_='series-title') is None:
                series_title = ''
            else:
                series_title = series_title_elm.get_text()

            novel_id = novel_url.replace('/novel/show.php?id=', '')
            novel_file_title = ''
            if series_title != '':
                novel_file_title = user + '(' + data_user_id + ')' + ' -- [' + series_title + '] ' + novel_title + '(' + novel_id + ')'
            else:
                novel_file_title = user + '(' + data_user_id + ')' + ' -- ' +  novel_title + '(' + novel_id + ')'
            print(novel_file_title)
            print(novel_url)

            # tag
            novel_tag_elm = novel.find('ul', class_='tags')
            novel_tag_soup = BeautifulSoup(str(novel_tag_elm), "html.parser")
            #print(novel_tag_soup)
            tag_list = list()
            novel_tags = ''
            for tag in novel_tag_soup.find_all('li'):
                tag.find(class_='tag-icon').decompose()
                tag_list.append(tag.find('a').get_text())

            novel_tags = '|'.join(tag_list)
            print(novel_tags)

            #詳細画面に移動
            browser.open(pixiv_url + novel_url)
            unit = browser.find(class_='_unit')
            if unit.find(class_='title') is None:
                continue

            title = unit.find(class_='title').string
            print(title)
            unit_soup = BeautifulSoup(str(unit), "html.parser")
            unit_soup_caption = unit_soup.find(class_='caption')
            unit_soup_caption_not_br = str(unit_soup_caption).replace('<br/>','\r\n')
            caption = p.sub("", unit_soup_caption_not_br)
            ##print(caption)
            print('-----------------------------------------------------')
            unit_soup_novel_pages = unit_soup.find(class_='novel-pages')
            #不要要素削除
            unit_soup_novel_pages.find('div', class_='_reaction-buttons-container').decompose()
            # 改行は不要、特定タグを改行とする
            unit_soup_novel_pages_not_br = str(unit_soup_novel_pages).replace('[newpage]','\r\n')
            # タグ削除
            novel_pages = p.sub("", unit_soup_novel_pages_not_br)
            novel_pages = jump.sub("", novel_pages)

            with io.open(novel_file_title.replace('/','/') + '.txt', 'w', encoding=character_encoding) as file:
                file.write(novel_file_title + '\r\n' + novel_url +'\r\n\r\n-----------------------------------------------------\r\n' + novel_title +'\r\n' + '-----------------------------------------------------'+ '\r\n')
                file.write(novel_tags + '\r\n' + '-----------------------------------------------------' + '\r\n' )
                file.write(caption + '\r\n' + '-----------------------------------------------------' + '\r\n' )
                file.write(novel_pages + '\r\n' + '=====================================================' + '\r\n' )

            print('=====================================================')

Python:Beautiful関連参考

hideharaaws.hatenablog.com

qiita.com

neuron.press

stackoverflow.com

d.hatena.ne.jp

tonari-it.com

qiita.com