[Python] pip 사용 시 CERTIFICATE_VERIFY_FAILED 에러

Proxy를 쓴다던가 해서 인증서 오류 등으로 CERTIFICATE_VERIFY_FAILED 에러가 났을 때-중간에서 내용물을 MITM등으로 본다던가 하려고 하는 거겠지…-, 아래처럼 –trusted-host 옵션을 사용한다.

pip install pycurl –proxy PROXY_ADDRESS:PORT –trusted-host pypi.python.org

pip install selenium --proxy IP:PORT --trusted-host pypi.org --trusted-host files.pythonhosted.org

이걸 아예 계속 쓰려면 Windows 기준으로는 $HOME/pip/pip.ini 파일을 만든 후 다음과 같이 써준다.

[global]
proxy = http://12.26.226.2:8080
trusted-host = pypi.python.org
 pypi.org
 files.pythonhosted.org

[Python] BeautifulSoup/requests 모듈, shell script로 네이버 금시세 기록하기

금 시세 기록을 위해 만든 python 스크립트

1. BeautifulSoup을 설치하자.

$ sudo apt-get install python-bs4

2. 네이버 금시세를 얻어와서 출력하자. Text file로 만들거니까..

# gold.py
import requests
from bs4 import BeautifulSoup
page = 1
while (1):
        reqstr = 'http://info.finance.naver.com/marketindex/goldDailyQuote.nhn?page=' + str(page)
        req = requests.get(reqstr)
        html = req.text
        soup = BeautifulSoup(html, 'html.parser')
        my_date = soup.select(
                'body > div > table > tbody > tr > td:nth-of-type(1)'
                )
        my_values = soup.select(
                'body > div > table > tbody > tr > td:nth-of-type(2)'
                )
        if (len(my_values) <= 0):
                break
        row = 0
        for title in my_values:
                print(my_date[row].text + '\t' + title.text)
                row = row + 1
        page = page + 1

3. 매일 실행시키려면, crontab 등록을 위해 shell script를 하나 만들자

#/bin/sh

python /home/dasomoli/src/gold.py > /mnt/NAS/Data/Gold/`date +%F`.txt

4. crontab에 등록하자

$ crontab -e

0 1 * * * /home/dasomoli/src/record_gold.sh

 

DB만들어서 기록하면, 한번 요청으로 끝나겠지만 귀찮고, 육아로 시간이 없으므로 오늘은 여기까지!

[Algorithm] KMP algorithm

String match algorithm인 KMP algorithm은 text에서 뛰어넘을 수 있는 만큼 뛰어넘으면서 string이 match되는지 확인한다. 여기서 얼마나 뛰어넘을 수 있는지에 대한 정보를 얻기 위해 Failure function이라는 함수를 이용한다.

Failure function은 찾으려는 string의 각 위치마다 공통인 prefix와 suffix의 최대 길이를 찾아 저장해둔다. 예를 들면, “ababa” 는  { 0, 0, 1, 2, 3 } 과 같이 저장한다. 구현 시에는 -1 해서 저장한다. 예에서의 값은 { -1, -1, 0, 1, 2 } 이 된다.

void get_failure_function(char str[], int len, int failure[])
{
	int i;

	failure[0] = -1;

	for (i = 1; i < len; i++) {
		int f = failure[i - 1];

		while (1) {
			if (str[i] == str[f + 1]) {
				failure[i] = f + 1;
				break;
			}
			if (f <= -1) {
				failure[i] = -1;
				break;
			}
			f = failure[f];
		}
	}
}

 

찾을 때는, 위에서 계산한 값을 이용해 string을 찾는 도중에 틀리면 바로 이전까지 맞았던 부분의 failure function value를 찾아 이전까지 같은 부분을 건너뛰고 다음 부분부터 비교한다. 예를 들어, 위의 string을 찾으려고 할 때, text가 “ababcababa” 라면, string의 처음부터 비교하다가 5번째에서 틀리면, 4번째까지는 맞으므로, 4번째의 failure function value를 찾으면 2이다. 이는 앞의 2글자는 맞다는 말이므로, text에서 4-2 만큼을 건너뛴 후, string의 3번째부터 다시 비교한다. string의 3번째도 틀리므로, 2번째의 failure function value를 찾으면 0인데, 이는 0만큼 맞다는 말(=하나도 안맞다는 말)이므로 text를 2-0 만큼 건너뛴 후, string의 1번째와 다시 비교한다. string의 첫번째와 다르면  1만큼만 이동하고, 다시 1번째와 비교한다. 맞았을 때도 맞은 부분만큼을 이용해서 건너뛴다(5번째에서 맞았으므로, 3글자가 맞고, 5-3만큼 이동한 후, 4번째부터 비교).

아래 구현은 match되는 개수를 return하도록 하였다.

int get_matched_count(char text[], int text_len, char string[], int str_len, int failure[])
{
	int count = 0;
	int t = 0;
	int s = 0;

	while (t < text_len - str_len + 1) {
		while (s < str_len) {
			if (text[t + s] != string[s])
				break;
			s++;
		}

		if (s == str_len) {
			// MATCH!
			count++;
		}

		if (s < 1) {
			t++;
			s = 0;
		} else {
			t += s - 1 - failure[s - 1];
			s = failure[s - 1] + 1;
		}
	}

	return count;
}

 

앞 부분과 공통 부분이 없는 string인 경우, 더 빨리 건너뛴다. 예를 들면, “abababababc” 에서 “ababc”({ 0, 0, 1, 2, 0 })를 찾는 경우, 5번째에서 틀리면, 앞에서부터 0글자가 같으므로, 4-0만큼 이동한 후, 1번째부터 다시 비교한다.

위 두 구현 함수의 사용 부분은 다음과 같이 될 것이다.

		int T, S;
		int count;
		int *failure_function = NULL;

		char *text = "ababcababababababababa";
		char *string = "ababa";

		T = strlen(text);
		S = strlen(string);

		failure_function = (int *)malloc(sizeof(int) * S);
		// skip the check of the memory allocation

		get_failure_function(string, S, failure_function);

		count = get_matched_count(text, T, string, S, failure_function);

		printf("%d matched\n", count);

 

[Algorithm] Union & Find – Disjoint-set structure

Union & find 연산은 서로 다른 그룹을 하나의 그룹으로 합칠 때 사용한다. Minimum spanning tree를 만드는 Kruskal 알고리듬 등에서 사용한다.

어떤 A B C D E F G H의 8개의 항목들이 1~4까지의 그룹에 속해 있을 때, 각 항목의 그룹이 다음과 같이 있다고 하자.

{ A – 1, B – 1, C – 2, D – 2, E – 3, F – 3, G – 3, H – 4 }

여기서 각 항목이 가진 그룹만 보면 { 1, 1, 2, 2, 3, 3, 3, 4 } 와 같다.

언뜻 생각해보면, B 항목이 속한 그룹(1번 그룹)과 G 항목이 속한 그룹(3번 그룹)을 합쳐서 { 1, 1, 2, 2, 1, 1, 1, 4 } 와 같이 만든다고 하면, G가 속한 그룹(3번 그룹)을 찾아서 이 그룹인 모든 항목(E, F, G)을 찾아서 1번 그룹으로 바꿔주어야 하므로 모든 노드를 확인해서 3인 그룹이 있다면 1로 바꾸어야 한다. 이걸 모든 노드를 돌면서 확인하면서 바꾸지 않아도 할 수 있게 만드는 게 disjoint-set structure의 union & find 연산이다.

그룹을 합칠 때는, 둘 중 어느 하나를 parent로 만드는 tree 구조를 이용한다. 즉, 하나의 tree에 있는 모든 노드는 하나의 그룹이 된다. 이걸 다시 쓰면, 어떤 노드의 root 노드와 다른 어떤 노드의 root가 같다면, 같은 그룹이다.

 

n개의 항목이 있다면 다음과 같이 n개 항목에 대한 배열(parent)을 잡아 각각의 항목의 index로 그룹명을 잡는다(rank 배열은 모두 1로 하고, union 연산에서 설명한다).

void makeset(int parent[], int rank[], int n)
{
	int i;
	
	for (i = 0; i < n; i++) {
		parent[i] = i;
		rank[i] = 1;
	}
}

 

Find 연산은 어떤 항목이 속한 그룹 번호, 즉, tree에서의 가장 상위 노드, root 노드의 번호를 반환한다. Optimization을 위해 어떤 항목이 find 연산 도중에 parent를 거쳐서 root까지 도달한다면, 그 항목과 그 parent모두를 해당 노드의 parent만 찾으면 root가 되도록 parent를 root로 바꾸어준다. 예를 들어, { A, B, C, D, E, F, G, H }가 { 0, 0, 2, 2, 0, 4, 3, 3 }과 같이 있을 때의 그림은 다음 그림의 왼쪽 그림과 같다. 이 때, F 항목에 대해 Find 연산을 하면 root 까지 가는 과정 중에 있는 노드들을 모두 parent가 root가 되도록 다음 그림의 오른쪽 그림과 같이 바꾼다. 이로써 parent 노드들 및 자신 중 어떤 노드가 어떤 그룹에 속하는지를 나중에 찾을 때(root까지 갈 때), 바로 찾아갈 수 있도록 한다.

 

int Find(int parent[], int x)
{
	if (x != parent[x])
		parent[x] = find(parent, parent[x]);

	return parent[x];
}

 

Union 연산은 두 항목의 root를 같게 만들어 두 항목이 속한 두 그룹을 하나의 그룹으로 합친다. 다시 쓰면, 자신들의 root를 찾아, 이 root 중 한 노드를 다른 root노드의 parent로 만들어 하나의 root로 만든다. 이 과정에서 각각의 항목이 find 연산을 하게 되는데, 이 때, 위에서와 같이 각각의 root까지 찾아가면서 거치는 모든 parent들도 모두 각각의 root를 바로 위 parent로 갖게 만들어 tree의 깊이를 낮춘다. Tree의 깊이가 깊어지면 자신의 그룹이 어떤 것인지를 아는데(root까지 찾아가는데) 시간이 오래 걸리므로, tree의 깊이를 깊게 하지 않기 위해서 find에서 깊이를 낮추는 것 외에 union 연산을 할 때, rank를 이용한다. 두 root 노드의 rank를 비교해 두 root 노드 중 rank가 높은 노드가 새로운 root가 된다. 또한, rank는 어떤 노드가 자신과 rank가 같은 다른 노드의 parent가 되면 1씩 증가한다. 따라서 rank 값은 자신의 sub tree가 최대로 깊이가 깊어졌을 때 rank + 1이 된다. find 연산을 거치면 깊이가 낮아지지만, rank가 증가한 후, root부터 어떤 leap node까지의 모든 노드가 한번도 find를 수행하지 않았다면, 최악의 경우 rank 깊이의 노드가 존재한다. 하지만, 서로 다른 rank의 두 root 가 합쳐진다 rank가 낮은 root노드의 tree는 항상 rank가 더 높은 tree의 sub tree가 된다. 이는 항상 최대 깊이를 rank 이하로 보장한다. 위의 예에서 F와 H의 그룹끼리 합친다고 하면 다음 그림과 같이 된다. 빨간색으로 표시한 값은 rank 값이다.

int Union(int parent[], int rank[], int x, int y)
{
	int rootX = find(parent, x);
	int rootY = find(parent, y);

	if (rootX == rootY)
		return 0;

	if (rank[rootX] < rank[rootY]) {
		parent[rootX] = rootY;
	} else if (rank[rootX] > rank[rootY]) {
		parent[rootY] = rootX;
	} else {
		parent[rootX] = rootY;
		rank[rootY]++;
	}
	
	return 1;
}

쓰고나니 주저리주저리 너무 길다.

[git] git tips 한국어판

​https://github.com/mingrammer/git-tips/blob/master/README.md

[RaspberryPi] minidlna-transcode 설치

라즈베리 파이로 DLNA 뷰어를 통해 동영상을 보려니 코덱 문제로 음성이 안나오는 것들이 있어서 minidlna-transcode로 트랜스코딩을 시도해 보았다. 결론만 말하면 실패. 컴퓨팅 파워 문제인지 툭툭 끊긴다.

원리는 설정된 특정 코덱을 사용하는 동영상은 보내기 전에 설정한 스크립트에서 ffmpeg를 통해 인코딩을 해서 쏘는 것으로 보인다. 설치하다보니 Debian에서 ffmpeg가 avconv로 이름이 바뀌었다는 것도 알게 됐다.

minidlna는 라즈베리안의 경우 현재 1.1.2버전을 apt-get으로 그냥 설치할 수 있다. 트랜스코딩없이 쓴다면 그냥 패키지 설치하는게 맘 편할 수 있겠다. 내가 시도한 방법은 소스 컴파일 설치이다.

  1. 소스 컴파일 설치

먼저 git으로 clone해온다.

$ git clone -b transcode https://bitbucket.org/stativ/readymedia-transcode.git

컴파일에 필요한 패키지를 설치한다.

$ sudo apt-get install libexif-dev libjpeg-dev libid3tag0-dev libflac-dev libvorbis-dev libsqlite3-dev libavformat-dev libavutil-dev libavcodec-dev  libmagickwand-dev autoconf autopoint gettext libav-tools libav-dev mpv

아래 패키지는 이름이 그럴듯해서 그냥 설치해봤는데 확실치 않다. mpv의 경우, mencoder가 또 저걸로 바뀌었대서 깔았는데, mencoder를 이용하진 않아서 잘 모르겠다.

$ sudo apt-get install libavcodec-extra libavcodec-extra-56 mpv

컴파일한다.

$ ./autogen.sh
$ ./configure
$ make

그럼 다음과 같은 컴파일 에러가 난다.

minidlnad-upnphttp.o: In function `SendResp_dlnafile':
/home/pi/src/readymedia-transcode/upnphttp.c:2041: warning: the use of `tmpnam' is dangerous, better use `mkstemp'

tmpnam()이 위험하니까 mkstemp()를 이용하라는 거니까 찾아서 바꿔준다.

$ vi upnphttp.c

char tmp[L_tmpnam];
mkdtemp(tmp);

다시 컴파일하면 잘 된다. 설치를 sudo make install로 해줘도 되는데(사실 이렇게 한 후에 checkinstall을 사용했다-_-), 패키지를 만들어 설치하면 나중에 제거가 편하다. checkinstall이 없으면 설치(sudo apt-get install checkinstall)한 후에 아래 명령을 준다.

$ sudo checkinstall

아래와 같은 게 나오면 ‘Y’

아래와 같은게 나오면 난 MiniDLNA Version 2015-06-18 Compiled June 18, 2017 라고 입력했다.

그럼 아래와 같은게 나오는데, 여기서 3번, 버전만 숫자로 시작해야해서 아래처럼 2015-06-18 로 입력했다.

3 - Version: [ transcode ]
3 - Version: [ 2015-06-18 ]

 

2. 설정

이제 소스 내에 있는 설정 파일을 /etc/ 아래로 복사해서 설정을 시작하자.

$ sudo cp minidlna.conf /etc/minidlna.conf

sudo를 매번 입력하기 귀찮으니 sudo -i 를 입력해서 root shell로 작업하자.

# vi /etc/minidlna.conf

설정 파일에서 아래 “media_dir”, “friendly_name”을 각자 환경에 맞게 적자. “media_dir”은 예상하듯이, 미디어가 있는 경로, “friendly_name”은 DLNA 기기에서 나타나는 이름이다.

media_dir=V,/mnt/NAS/Videos
friendly_name=DasomOLI DLNA

“audio_codecs”나 “video_codecs” 에 적혀있는 코덱에 해당하는 미디어 파일이 재생되면, 그 아래의 “transcode_audio_transcoder”혹은 “transcode_video_transcoder” 에 적혀 있는 스크립트를  실행한다. 원하는 코덱을 transcoding하고 싶다면 해당 코덱을 적어주자. 어떤 것을 적어야 하는지는 avconv -formats 라고 입력하면 찾아볼 수 있다.

transcode_audio_codecs=flac/vorbis/dts
transcode_audio_transcoder=/usr/local/share/minidlna/transcodescripts/transcode_audio
transcode_video_transcoder=/usr/local/share/minidlna/transcodescripts/transcode_video

transcoding 스크립트는 /usr/local/share/minidlna/transcodescripts/ 아래에 있다. 옵션을 변경하고 싶다면 해당 스크립트를 수정한다. 옵션을 변경하지 않더라도 수정을 해주어야 하는데 ffmpeg 대신 avconv를 사용해야 하기 때문이다.

# vi /usr/local/share/minidlna/transcodescripts/transcode_audio

#!/bin/sh

SOURCE=$1
STARTPOSITION=$2
DURATION=$3

#ffmpeg -ss $STARTPOSITION -t $DURATION -i "$SOURCE" -loglevel quiet -acodec pcm_s16le -f s16le -ar 44100 pipe:1
avconv -ss $STARTPOSITION -t $DURATION -i "$SOURCE" -loglevel quiet -acodec libmp3lame -f mp3 -ar 44100 -ab 224k pipe:1

# vi transcodescripts/transcode_video

#!/bin/sh

SOURCE=$1
STARTPOSITION=$2
DURATION=$3

avconv -ss $STARTPOSITION -t $DURATION -i "$SOURCE" -loglevel quiet -threads auto -async 2 -target pal-dvd pipe:1

# vi transcodescripts/transcode_video-hq

#!/bin/sh

SOURCE=$1
STARTPOSITION=$2
DURATION=$3

avconv -ss $STARTPOSITION -t $DURATION -i "$SOURCE" -loglevel quiet -threads auto -async 2 -vcodec mpeg2video -b:v 20000k -f mpegts pipe:1

위와 같이 ffmpeg가 적힌 곳에 avconv를 써주면 된다.

이제 실행하려면 다음과 같이 한다. -R 옵션은 DB update를 해주는 거니까 매번 하진 말자.

/usr/local/sbin/minidlnad -R -f /etc/minidlna.conf

 

3. 자동 실행

매번 위처럼 실행하긴 귀찮으니까, 부팅될 때마다 자동 실행 되도록 하자. /etc/init.d/안에 minidlna 파일을 새로 생성해서 아래와 같이 내용을 채운다. Process가 2개 되는 경우가 보여서 참고로 보았던 글에 있는 스크립트를 좀 수정했다.  잘안되면, 뭐 또 수정해야지..

# vi /etc/init.d/minidlna

#!/bin/bash
# Mini DLNA
### BEGIN INIT INFO
# Provides:          scriptname
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Start daemon at boot time
# Description:       Enable service provided by daemon.
### END INIT INFO

case "$1" in
'start')
        /usr/local/sbin/minidlnad -f /etc/minidlna.conf
        echo Started
        ;;
'stop')
        PIDS=`/bin/pidof minidlnad`
        if [ "${PIDS}" != "" ];
        then
                for PID in ${PIDS};
                do
                        kill -SIGTERM ${PID}
                done
        else
                echo Already Stopped
        fi
        ;;
'restart')
        PIDS=`/bin/pidof minidlnad`
        if [ "${PIDS}" != "" ];
        then
                for PID in ${PIDS};
                do
                        kill -SIGTERM ${PID}
                done
        fi
        /usr/local/sbin/minidlnad -f /etc/minidlna.conf
        echo Restarted
        ;;
'status')
        PID=`/bin/pidof minidlnad`
        if [ "${PID}" != "" ];
        then
                echo Running. Process ${PID}
        else
                echo Stopped
        fi
        ;;
'rescan')
        PIDS=`/bin/pidof minidlnad`
        if [ "${PIDS}" != "" ];
        then
                for PID in ${PIDS};
                do
                        kill -SIGTERM ${PID}
                done
        fi
        /usr/local/sbin/minidlnad -R -f /etc/minidlna.conf
        echo Rescanning
        ;;
*)
        echo "Usage: $0 { start | stop | restart | status | rescan }"
        ;;
esac
exit 0

위 파일에 실행 권한을 주자.

# chmod a+x /etc/init.d/minidlna

실행되도록 rc.d를 업데이트 하자.

# sudo update-rc.d minidlna defaults

 

참고:

https://www.htpcbeginner.com/install-minidlna-on-ubuntu-ultimate-guide/

[C/C++] Variadic function의 default argument promotion

C/C++에서 함수에 가변 인자를 사용할 때, 그 parameter의 타입을 컴파일러가 알 수 없기 때문에, 컴파일러가 좀 쉽게 알 수 있도록, int보다 작은 타입은 int 혹은 unsigned int로, float은 double로 promotion이 일어난다고 한다.

이를 무시하고, va_start(), va_args() 매크로를 사용하면, undefined behavior이다.

[RaspberryPi] NAS 설치 시 참고글

NAS를 어떻게 구축할까, OpenMediaVault를 쓸까, Raspbian 위에 그냥 만들까, 그러려면 뭘 깔아야 하나 찾다가 찾은 글.

http://www.spacek.xyz/mle/?p=357

뭘 설치했는지 꽤 잘 나와 있다. 좀 해보다 별로면 OpenMediaVault로 이미지 바꿔보지 뭐.

쓸만한 글을 찾을 때마다 이 글을 업데이트하겠다.

 

[QuickBuild] RESTful API로 빌드 요청

QuickBuild를 이용할 때, 굳이 사이트에 접속해서 빌드를 돌리지 않아도 RESTful API를 이용하면 빌드를 걸 수 있다.

http://wiki.pmease.com/display/QB61/Interact+with+Build+Requests#InteractwithBuildRequests-requestnewbuild 에 해당 내용이 정리되어 있다.

Basic authentication 시에 curl을 이용해서 요청하는 방법은 다음과 같다.

$ curl -X POST -u dasomoli:password -d@request.xml http://<Server>:8810/rest/build_requests

위의 예제에서 사용한 request.xml 은 다음의 형식을 가진다. <varlables> 안에 채울 내용은 해당 configuration에서 실행한 빌드의 variables(http://<Server>:8810/build/<Build id>/variables) 페이지에서 확인 가능하다.

<com.pmease.quickbuild.BuildRequest>
  <!-- This element tells QuickBuild in what configuration to trigger build. -->
  <configurationId>10</configurationId>

  <!-- This element tells whether or not to respect build condition of the configuration. 
       If this is set to true, and if the build condition evaluates to false, build will 
       not be triggered. -->
  <respectBuildCondition>false</respectBuildCondition>

  <!-- This element is optional and is used to specify variables for build triggering. If 
       specified, it will override the variable with the same name defined in configuration
       basic setting. -->
  <variables>
    <entry>
      <string>var_name1</string>
      <string>var_value1</string>
    </entry>
    <entry>
      <string>var_name2</string>
      <string>var_value2</string>
    </entry>
  </variables>

  <!-- This element is optional and is used to tell QuickBuild to request a build promotion. -->
  <promotionSource>

    <!-- This element is optional and is used to tell QuickBuild that the source build resides on another 
         QuickBuild server. -->
    <server>
      <url>http://another-qb-server:8810</url>
      <userName>admin</userName>
      <password>admin</password>
    </server>

    <!-- Identifier of the source build to promote from -->
    <buildId>697</buildId>

    <!-- This element is optional and used to specify files to promote -->
    <deliveries>
      <com.pmease.quickbuild.FileDelivery>
        <srcPath>artifacts/dir1</srcPath>
        <filePatterns>**/*.jar</filePatterns>
      </com.pmease.quickbuild.FileDelivery>
      <com.pmease.quickbuild.FileDelivery>
        <srcPath>artifacts/dir2</srcPath>
        <filePatterns>**/*.war</filePatterns>
      </com.pmease.quickbuild.FileDelivery>
    </deliveries>
  </promotionSource>

</com.pmease.quickbuild.BuildRequest>

Sample XML의 comment에서 확인할 수 있듯이 <promotionSource>내부는 생략 가능하다.