IT業界のすみっこ暮らし

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

2017年のActivityを振り返る

GitLabのActivity in 職場

f:id:papamau:20180117185816p:plain

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

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

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

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

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

C#:API呼び出し

前提

リクエスト仕様

POSTメソッドのJSON形式

レスポンス仕様

JSON形式で返却

レスポンス呼び出し

レスポンスの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

Python:utf8でテキストファイル作成

cleantext = 'あああ'
character_encoding = 'utf8'
with io.open('text1.txt', 'w', encoding=character_encoding) as file:
    file.write(cleantext)

encodingを指定しないまま日本語を入れるとまたエンコードエラーになる。

Python:UnicodeEncodeError対策

日本語の文字列を出力しようとしたらUnicodeEncodeErrorエラー!

print('あははは')
--------
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128)

原因

調べてみるとPythonエンコード系のエラーはとてもよくあることらしい?

import sys
print (sys.getdefaultencoding()) #utf-8
print(sys.stdout.encoding) #US-ASCII <- これが問題
--------
utf-8
US-ASCII

対策1

qiita.com

import sys
import io

sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8')
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')

print(sys.stdin.encoding) #UTF-8
print(sys.stdout.encoding) #utf-8
print(sys.stderr.encoding) #UTF-8
--------
UTF-8
utf-8
UTF-8

これで出力は出来たけど、腑に落ちない…ソースも長くなるし

対策2

#!/usr/bin/env PYTHONIOENCODING=UTF-8 python3
# -*- coding: utf-8 -*-
import sys

print(sys.stdin.encoding) #UTF-8
print(sys.stdout.encoding) #UTF-8
print(sys.stderr.encoding) #UTF-8
--------
UTF-8
UTF-8
UTF-8

環境変数を設定して実行

参考

Pythonの日本語処理

PythonのUnicodeEncodeErrorを知る - HDEラボ

taka110w.hateblo.jp

qiita.com

zashikiro.hateblo.jp

20171212ブクマ

Visual Studio 2017で_references.jsがない件 field-notes.sakura.ne.jp

qiita.com

www.kk3marketer.com

mizchi.hatenablog.com

employment.en-japan.com

support.google.com

www.itmedia.co.jp

C#によるWEBサイトへのログイン方法(ベーシック認証、クッキー認証) | 夏研ブログ

qiita.com

yaamaa-memo.hatenablog.com

d.hatena.ne.jp

stackoverflow.com

d.hatena.ne.jp

qiita.com

qiita.com

qiita.com

MvcSiteMapProviderのRouting関連 github.com

広告を非表示にする

SQLとJSONでエスケープ関連メモ

アプリ内でSQL直接実行の際にパラーメータにシングルクォーテーション(')が含まれている場合

stackoverflow.com

  • just replace ' with '' (that's two single quotes, not one double quote) in the string literals. That's it.

以下のようにエスケープしたものをパラメータに入れる

param.Replace("'", "''")

JSONの値の中にダブルクォーテーション(")が含まれている場合

stackoverflow.com

Replace("\"", "\\\"")


当たり前っちゃ当たり前だけどこれらで痛い目にあったのでメモ

SQL Server:データの暗号化・複合化についての諸々メモ

SQL Serverでのデータ暗号化・複合化について検証した内容のまとめ。

0、前提

  • 対象のSQL Serverのバージョンが最新ではない
  • DB単位での認証とか対称キーとかはなるべく使わずに、アプリ単位で暗号化・複合化したい
  • 開発ではEntity Frameworkを使う予定。でもSQL直接実行もOK
  • 暗号化対象は英語、日本語のほか、色んな国の言語が対象になる

1、検証用データベースの作成(localDB)

f:id:papamau:20171206172234p:plain

2、検証用テーブルの作成

f:id:papamau:20171206172350p:plain

USE [TESTDB01]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[Table_1](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](500) NULL,
    [NameEnc] [varbinary](2000) NULL
) ON [PRIMARY]

GO
※暗号化したデータのdatatypeはvarbinary。

3、色んな言語でデータを作成

f:id:papamau:20171206172549p:plain

INSERT文には必ずNをつけてnvarcharとして登録する。

INSERT INTO [dbo].[Table_1]([Name]) VALUES (N'Name');
※datatypeをnvarcharで登録しないとCharとして登録され、"???"のように文字化けになる。

4、暗号化する

UPDATE [dbo].[Table_1] SET [NameEnc] = ENCRYPTBYPASSPHRASE('ENCRYPT',Name);

f:id:papamau:20171206173015p:plain

※Nameのdatatypeがnvarcharなので、ENCRYPTBYPASSPHRASEにはnvarcharのまま暗号化される。
文字列を直接入力して暗号化したい場合はN'...'のように設定する
UPDATE [dbo].[Table_1] SET [NameEnc] = ENCRYPTBYPASSPHRASE('ENCRYPT',N'暗号化したい内容');

5、複合化する

SELECT [ID], [Name], [NameEnc]
  , CAST(DECRYPTBYPASSPHRASE('ENCRYPT',NameEnc) AS nvarchar(2000)) AS NameDec
FROM [TESTDB01].[dbo].[Table_1]

f:id:papamau:20171206173245p:plain

※複合化した後、nvarcharに変換する処理を入れる。

以上


補足:MySQLの場合

暗号化
CAST(HEX(AES_ENCRYPT('暗号化するデータ', '暗号化key')) AS CHAR)
複合化
CAST(AES_DECRYPT(UNHEX('複合化するデータ'), '暗号化Key') AS CHAR)