chinda_fall_desu’s diary

竹内豊の日記

ヒューマンコンピューターインターフェースをもっと知りたいなー

pythonでYoutube動画のタイトルと概要欄を取得

YouTube Data API の概要  |  Google Developers
Youtube Data APIを使えば、Youtubeから様々な情報を取得できる。
以下にその例を示した。


Youtubeの動画のidを使って、その動画のタイトルを得る。
ソースコード

import requests
id='z9WHIkedhz8'
apikey='(Youtube Data api のキー)'
url = 'https://www.googleapis.com/youtube/v3/videos?id='+id+'&key='+apikey+'&part=snippet,contentDetails,statistics,status'

response = requests.get(url)
# print(response.json())
print("https://www.youtube.com/watch?v="+response.json()['items'][0]['id'])
print(response.json()['items'][0]['snippet']['title'])

(実行結果)

> python .\title.py
https://www.youtube.com/watch?v=z9WHIkedhz8
【改名はまさかの…】結局誰が一番球技上手いの? 第1回球技王!【後編】


②同様にして概要欄の文章を取得することもできる。
ソースコード

print(response.json()['items'][0]['snippet']['description'])

(実行結果)

> python .\title.py
撮影協力:岡崎市
https://fc.okazaki-kanko.jp/news/94

今回の企画では「球技」とひとくくりにしていろいろなスポーツで遊びましたが、よく考えてみればなんか「球」を使う競技多いですよね。流行ってるんですか?

「スポーツなんだから球使うだろ」「球じゃないとくちゃくちゃになるだろ」とお思いかもしれませんが、本当にそうですか?ラグビーやアメフトは意味わかんない形のボール使ってますし、バドミントンやインディアカも意味わかんない羽根使ってます。でも別にスポーツとして成立してますよね?むしろその形状ならではの特徴的な動きが生まれていていい感じです。
:
:
:
ゆめまる→https://twitter.com/TO_yumemarucas
虫眼鏡→https://twitter.com/TO_ZAWAKUN


③チャンネルidを使って、そのチャンネルの動画のタイトルを得る。
ソースコード

import requests
channelid='UCutJqz56653xV2wwSvut_hQ'
apikey='(Youtube Data api のキー)'
url = 'https://www.googleapis.com/youtube/v3/search?key='+apikey+'&channelId='+channelid+'&part=snippet,id&order=date&maxResults=1'

response = requests.get(url)
# print(response.json())
print("https://www.youtube.com/watch?v="+response.json()['items'][0]['id']['videoId'])
print(response.json()['items'][0]['snippet']['title'])

urlのchannelIdでチャンネルを指定、maxResultsで出力する結果の数を指定。
(実行結果)

> python .\channel.py
https://www.youtube.com/watch?v=vn50D0NEpg4
【りょうやん】みんなの「お前おかしいよ!」っていう5対1を見つけ合おう!!!

そのチャンネルの最新の動画が表示される。


④maxResultsを変えて、タイトルの一覧を表示する。
ソースコード

import requests
channelid='UCutJqz56653xV2wwSvut_hQ'
apikey='(Youtube Data api のキー)'
url = 'https://www.googleapis.com/youtube/v3/search?key='+apikey+'&channelId='+channelid+'&part=snippet,id&order=date&maxResults=50'
num=1

response = requests.get(url)
for i in range(50):
    print(str(num)+'. '+response.json()['items'][i]['snippet']['title'])
    num+=1

(実行結果)

> python .\channel.py
1. 【りょうやん】みんなの「お前おかしいよ!」っていう5対1を見つけ合おう!!!
2. 【改名はまさかの…】結局誰が一番球技上手いの? 第1回球技王!【後編】
3. 【最下位は改名】結局誰が一番球技上手いの? 第1回球技王!【前編】
4. 【46道府県旅行の旅!滋賀県編】〜黄昏の湖、道化師の鎮魂歌〜
:
:
46. 【鍛冶屋】それは無理だろって素材だけで刃物作り対決!!!
47. 【バカ商品】東海オンエアショッピングへようこそ!!!!
48. 1日全力で努力したらどれだけの数のYouTuberとコラボできるの!?
49. 野球の道具を野球以外に使って新競技を考えてみよう!
50. 【猛火】1000円以内で自分の財布を「防火仕様」にしよう!


⑤51以上の動画のタイトルを表示する。
maxResultsは50までしか指定できないので、複数回リクエストを送る。

import requests
channelid='UCutJqz56653xV2wwSvut_hQ'
apikey='(Youtube Data api のキー)'
url = 'https://www.googleapis.com/youtube/v3/search?key='+apikey+'&channelId='+channelid+'&part=snippet,id&order=date&maxResults=50'
num=1

response = requests.get(url)
for i in range(50):
    print(str(num)+'. '+response.json()['items'][i]['snippet']['title'])
    num+=1

for j in range(4):
    next=response.json()["nextPageToken"]
    nexturl=url+'&pageToken='+next
    response = requests.get(nexturl)
    for i in range(50):
        print(str(num)+'. '+response.json()['items'][i]['snippet']['title'])
        num+=1

(実行結果)

> python .\channel.py
1. 【りょうやん】みんなの「お前おかしいよ!」っていう5対1を見つけ合おう!!!
2. 【改名はまさかの…】結局誰が一番球技上手いの? 第1回球技王!【後編】
3. 【最下位は改名】結局誰が一番球技上手いの? 第1回球技王!【前編】
4. 【46道府県旅行の旅!滋賀県編】〜黄昏の湖、道化師の鎮魂歌〜
5. 【超難問】これなんの粉末でしょうクイズでブチギレ必至!!!
6. 【大ブランク】3回転んだら即終了スノボ!
7. 「こんな素材、編集でどうやって使うねん!!」→使います。
8. 【答えは実物で】第一回!現物大喜利!!!!!!
9. 【家庭事情×作品】自宅にある物だけでジオラマ作り対決!!!
10. 【失礼】プロフェッショナルのスゴ業をそんな事に使うな!!選手権
11. 【穴を制せ】負けたらピアスの対決!!穴王!!!
12. 【運×演技力】相談なしで5人の衣装を揃えて即興コントを演じきれ!
:
:
244. 【過去最悪】全味覚の世界最強を全て混ぜたら本当に死にかけた
245. 【5vs1クイズ対決】虫眼鏡ってさぁ、本当に頭いいの?
246. 【20種以上のフレーバー】俺たちがドクターペッパーを超えたドリンクを作ってやるよ!確かにそう意気込んでいた・・・     
247. 手作りバットで一番遠くにボール飛ばせた奴が勝ち対決!!
248. 【水溜りオンエア】おふざけなしのガチ文理ラップ対決!
249. 【サスペンス】リレー方式でドラマを作ったらまさかの結果に!?
250. 【二遊間】もう一度アライバコンビの超絶プレーが見たいんだ・・・【中日ドラゴンズ】


⑥実用例
chindafalldesu.hatenablog.com


参考
YouTube Data API の概要  |  Google Developers

YouTube Data api v3をPythonから使って特定のチャンネルの動画を取得する - Qiita


(間違い等あればコメントよろしくお願いいたします。)

pythonとParamikoでsshの通信を行う(sshサーバ作成)

paramikoモジュールを使ってpythonsshサーバを立ててみる。


(1)sshサーバを立てる (適宜分割して示す)

import socket
import threading
import paramiko

host_key = paramiko.RSAKey(filename='test_rsa.key')

簡単のため配布されているデモファイル 'test_rsa.key' を使う。
paramiko/test_rsa.key at master · paramiko/paramiko · GitHub

bind_host="0.0.0.0"
bind_port=50000
pw="password is 12345\n"

今回はpwの文字列を送ることにする。ポート番号50000を開く。

class Server(paramiko.ServerInterface):
    def __init__(self):
        self.event = threading.Event()

    def check_channel_request(self, kind, chanid):
        if kind == 'session':
            return paramiko.OPEN_SUCCEEDED
        return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
    def check_auth_password(self, username, password):
        if(username=='user') and (password=='pass'):
            return paramiko.AUTH_SUCCESSFUL
        return paramiko.AUTH_FAILED
    # def check_auth_publickey(self, username, key):
    #     if (username=="user") and (key ==(鍵)):
    #         return paramiko.AUTH_SUCCESSFUL
    #     return paramiko.AUTH_FAILED

これでサーバの動作を定義する。
パスワードを使って接続を確立する場合はcheck_auth_password()を使い、鍵を使う場合はcheck_auth_publickey()を使う。

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((bind_host, bind_port))
sock.listen(5)
client, addr = sock.accept()

ソケットを使って通信を確立する。

t = paramiko.Transport(client)
t.add_server_key(host_key)
server = Server()
t.start_server(server=server)

sshトランスポートをソケット通信にアタッチし、セッションを開始し、チャネルを生成する。

chan=t.accept(30)
chan.send("Hello\n".encode('utf-8'))
chan.send(pw.encode('utf-8'))

クライアントによって開かれた次のチャネルを使って文字列を送信する。

t.close()

セッションを終了し、すべてのチャネルを閉じる。


(2)接続を試す

> ssh -p 50000 user@---------
The authenticity of host '[-------]:50000 ([--------]:50000)' can't be established.
RSA key fingerprint is ---------
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[-------]:50000' (RSA) to the list of known hosts.     
user@-------'s password: (パスワードを入力)
Hello
     password is 12345
                      Connection reset by ------- port 50000


下記のデモファイルを参考にした。
paramiko/demos at master · paramiko/paramiko · GitHub



(間違い等あればコメントよろしくお願いいたします。)

pythonとtelnetで通信を行う

telnetsshのどちらもほかのコンピュータと接続するための仕組みであるが、telnetは通信を暗号化しないためその内容を覗き見られる危険性がある。
サーバを自分で作って、wiresharkで通信を覗いてみる。


(1)telnetで通信を行う
・サーバ

import socket
import subprocess

bind_host="0.0.0.0"
bind_port=50000
pw="password is 12345\n"

server=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((bind_host, bind_port))
server.listen(5)
print("host: "+bind_host)
print("port: "+str(bind_port))

while True:
    client, addr=server.accept()
    print("from:"+ addr[0]+" "+str(addr[1]))
    while True:
        print("waiting for his response...")
        rec=client.recv(1024)
        client.send(pw.encode("utf-8"))

        if len(rec)==0:
            client.close()
            break

・クライアント

telnet 192.168.129.17 50000
Trying 192.168.129.17...
Connected to 192.168.129.17.
Escape character is '^]'.
Connection closed by foreign host.
hi
password is 12345

クライアントはサーバに接続してメッセージを送るとサーバから "password is 12345" が送られる。
だがこの内容は覗き見られる可能性がある。



(2)Wiresharkで内容を覗く
Wiresharkで上記のサーバとクライアント間の通信を覗くと下記が得られた。
・クライアント側が送った情報
f:id:chinda_fall_desu:20200128224826p:plain
・サーバ側が送った情報
f:id:chinda_fall_desu:20200128224955p:plain


次回はsshで同様のことを行う。


(間違い等あればコメントよろしくお願いいたします。)

ksnctfを使ってHTTP通信について学ぶ④(python・ksnctf-9)

前回、pythonでHTTPリクエストメッセージを作り、HTTPレスポンスメッセージを表示させた。
これを用いて、ksnctfの問9を解いてみる。
ksnctf - 9 Digest is secure!


認証を行ってflag.htmlにアクセスすればいいことまではわかっている。
Authorizationヘッダを付けたHTTPリクエストメッセージをつくって、HTTPレスポンスメッセージを表示させてみる。


①Authorizationヘッダを作る
Authorizationヘッダを作るためにはnonceが必要である。リクエストメッセージを送り、nonceを得る。
ハッシュの作成にはhashlibライブラリを使う。

import requests
import hashlib

url="http://ctfq.sweetduet.info:10080/~q9/flag.html"

nonce = requests.get(url).headers["WWW-Authenticate"].split(" ")[2][7:-2]
a1="c627e19450db746b739f41b64097d449"
nc="00000001"
cnonce="9691c249745d94fc"
qop="auth"
a2=hashlib.md5(b"GET:/~q9/flag.html").hexdigest()

res=hashlib.md5((a1+":"+nonce+":"+nc+":"+cnonce+":"+qop+":"+a2).encode('utf-8')).hexdigest()

key='Digest username="q9", realm="secret", nonce="'+nonce+'", uri="/~q9/flag.html", algorithm=MD5, response="'+res+'", qop=auth, nc=00000001, cnonce="9691c249745d94fc"'


②HTTPリクエストメッセージを送る。
前回のコードに上記の認証を加える。

headers={
'Host': 'ctfq.sweetduet.info:10080',
'Connection':'keep-alive',
'Authorization': key,
'User-Agent': 'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.162 Safari/535.19',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Encoding': 'gzip,deflate,sdch',
'Accept-Language': 'ja,en-US;q=0.8,en;q=0.6',
'Accept-Charset': 'Shift_JIS,utf-8;q=0.7,*;q=0.3',
}

r=requests.get(url, headers=headers)


③HTTPレスポンスメッセージを得る。

print(r.status_code)
print(r.headers)
print(r.text)

(実行結果)

> python .\sample.py
200
{'Date': 'Tue, 21 Jan 2020 09:56:55 GMT', 'Server': 'Apache/2.2.15 (CentOS)', 'Authentication-Info': 'rspauth="ed70106349d8d9e632ae77641a40a1b7", cnonce="9691c249745d94fc", nc=00000001, qop=auth', 'Last-Modified': 'Sat, 26 May 2012 12:29:31 GMT', 'ETag': '"422e3-90-4c0efa30418d3"', 'Accept-Ranges': 'bytes', 'Content-Length': '144', 'Connection': 'close', 'Content-Type': 'text/html; charset=UTF-8'}
<!DOCTYPE html>
  <head>
    <meta charset="utf-8">
    <title>Q9</title>
  </head>
  <body>
    <p>(フラグ)</p>
  </body>
</html>


下記のドキュメントを参考にした。
Requests: 人間のためのHTTP — requests-docs-ja 1.0.4 documentation


(間違い等あればコメントよろしくお願いいたします。)

ksnctfを使ってHTTP通信について学ぶ③(python・ksnctf-8)

HTTP通信をpythonで行う方法を学び、それを使って次回ksnctfの問9を解く。
ksnctf - 9 Digest is secure!


①HTTPリクエストメッセージをPythonで作る
・下記はksnctfの問8のHTTPリクエストメッセージ

GET /~q8/ HTTP/1.1
Host: ctfq.sweetduet.info:10080
Connection: keep-alive
Authorization: Basic cTg6RkxBR181dXg3eksyTktTSDhmU0dB
User-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.162 Safari/535.19
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate,sdch
Accept-Language: ja,en-US;q=0.8,en;q=0.6
Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.3


・Requestsライブラリを使って、Pythonで同じメッセージを作ってみる

import requests
url="http://ctfq.sweetduet.info:10080/~q8/"

headers={
'Host': 'ctfq.sweetduet.info:10080',
'Connection':'keep-alive',
'Authorization': 'Basic cTg6RkxBR181dXg3eksyTktTSDhmU0dB',
'User-Agent': 'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.162 Safari/535.19',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Encoding': 'gzip,deflate,sdch',
'Accept-Language': 'ja,en-US;q=0.8,en;q=0.6',
'Accept-Charset': 'Shift_JIS,utf-8;q=0.7,*;q=0.3',
}

r=requests.get(url, headers=headers)


②HTTPレスポンスメッセージをPythonで表示してみる
・下記はksnctfの問8のHTTPレスポンスメッセージ

HTTP/1.1 200 OK
Date: Sat, 26 May 2012 20:54:05 GMT
Server: Apache/2.2.15 (CentOS)
Last-Modified: Sat, 26 May 2012 12:24:46 GMT
ETag: "422da-b8-4c0ef920b3f8e"
Accept-Ranges: bytes
Content-Length: 184
Connection: close
Content-Type: text/html; charset=UTF-8

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Q8</title>
  </head>
  <body>
    <p>Congratulations!</p>
    <p>The flag is q8's password.</p>
  </body>
</html>


pythonでメッセージを表示させてみる
ソースコード

print(r.status_code)
print(r.headers)
print(r.text)

(実行結果)

> python .\sample.py
200
{'Date': 'Tue, 21 Jan 2020 08:38:22 GMT', 'Server': 'Apache/2.2.15 (CentOS)', 'Last-Modified': 'Sat, 26 May 2012 12:24:46 GMT', 'ETag': '"422da-b8-4c0ef920b3f8e"', 'Accept-Ranges': 'bytes', 'Content-Length': '184', 'Connection': 'close', 'Content-Type': 'text/html; charset=UTF-8'}
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Q8</title>
  </head>
  <body>
    <p>Congratulations!</p>
    <p>The flag is q8's password.</p>
  </body>
</html>

下記のドキュメントを参考にした。
Requests: 人間のためのHTTP — requests-docs-ja 1.0.4 documentation


(間違い等あればコメントよろしくお願いいたします。)

pythonでsocketを使って通信を行う③ (コマンドの実行)

今回は前回までのコードを再び改良し、コマンドを実行できるようにする。


準備
コマンドの実行にはsubprocessモジュールを用いる。
下記のようにして、コマンドを実行できる。

import subprocess
com=subprocess.run("dir", stdout=subprocess.PIPE,stderr=subprocess.PIPE, shell=True)
print(com.stdout.decode('cp932'))
print(com.stderr.decode('cp932'))
> python .\30.py
 ドライブ C のボリューム ラベルは Windows です
 ボリューム シリアル番号は 40BE-398B です

 C:\-----------------\02 のディレクトリ

2020/01/12  21:27    <DIR>          .
2020/01/12  21:27    <DIR>          ..
2020/01/12  21:28               549 30.py
2020/01/12  21:27                24 sample.txt
               2 個のファイル                 573 バイト
               2 個のディレクトリ   5,052,829,696 バイトの空き領域


上記のモジュールを使って前回までのコードを改良する。
ソースコード
・サーバ側

import socket
import subprocess

bind_host="0.0.0.0"
bind_port=50000

server=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((bind_host, bind_port))
server.listen(5)
print("host: "+bind_host)
print("port: "+str(bind_port))

while True:
    client, addr=server.accept()
    print("from:"+ addr[0]+" "+str(addr[1]))
    while True:
        print("waiting for his response...")
        rec=client.recv(1024)
        com=subprocess.run(rec.decode('utf-8'), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
        # print(rec.decode('utf-8'))
        client.send(com.stdout)
        client.send(com.stderr)

        if len(rec)==0:
            client.close()
            break

・クライアント側

import socket
target_host="(サーバ側のIPアドレス)"
target_port=50000
client=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((target_host, target_port))
while True:
    com=input("command: ")
    client.send(com.encode('utf-8'))
    rec=client.recv(4096)
    print(rec.decode('cp932'))


実行結果
・サーバ側

> python .\server.py
host: 0.0.0.0
port: 50000
from:(クライアント側のIPアドレス)(クライアント側のポート番号)
waiting for his response...
waiting for his response...
waiting for his response...
waiting for his response...
waiting for his response...
waiting for his response...

・クライアント側

> python .\client.py
command: dir
 ドライブ C のボリューム ラベルは Windows です
 ボリューム シリアル番号は 40BE-398B です

 C:\---------------------\01 のディレクトリ

2020/01/12  21:34    <DIR>          .
2020/01/12  21:34    <DIR>          ..
2020/01/12  21:16                24 02.txt
2020/01/12  21:16                24 sample.txt
2020/01/12  21:14               724 server.py
               3 個のファイル                 772 バイト
               2 個のディレクトリ   5,051,006,976 バイトの空き領域

command: type sample.txt
hello
i'm from Canada

command: copy sample.txt copy.txt
        1 個のファイルをコピーしました。

command: dir
 ドライブ C のボリューム ラベルは Windows です
 ボリューム シリアル番号は 40BE-398B です

 C:\---------------------\01 のディレクトリ

2020/01/12  21:34    <DIR>          .
2020/01/12  21:34    <DIR>          ..
2020/01/12  21:16                24 02.txt
2020/01/12  21:16                24 copy.txt
2020/01/12  21:16                24 sample.txt
2020/01/12  21:14               724 server.py
               4 個のファイル                 796 バイト
               2 個のディレクトリ   5,049,970,688 バイトの空き領域

command: type copy.txt
hello
i'm from Canada

command:


解説
・クライアント側からサーバ側にコマンドを送信し、サーバ側で実行されたコマンドの実行結果をクライアント側に返している。
・クライアント側はこれによってサーバ側のディレクトリの中身、ファイルの中身を見ることができる。


(間違い等あればコメントよろしくお願いいたします。)

pythonでsocketを使って通信を行う② (ファイルの送受信)

今回は前回作ったコードを改良して、テキストファイルを送信できるようにする。

ソースコード
・サーバ側

import socket

bind_host="0.0.0.0"
bind_port=50000

server=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((bind_host, bind_port))
server.listen(5)
print("host: "+bind_host)
print("port: "+str(bind_port))

while True:
    client, addr=server.accept()
    print("from:"+ addr[0]+" "+str(addr[1]))
    while True:
        print("waiting for his response...")
        rec=client.recv(1024)
        fd=open("get.txt", "wb")
        fd.write(rec)
        fd.close()
        print(rec.decode('utf-8'))
        if len(rec)==0:
            client.close()
            break

・クライアント側

import socket
target_host="(サーバ側のIPアドレス)"
target_port=50000
client=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((target_host, target_port))
while True:
    while True:
        file=input("textfile: ")
        try:
            fd=open(file, "rb")
        except FileNotFoundError:
            print("can't open this file")
            print("please try it again")
            print("")
        else:
            text=fd.read()
            fd.close()
            break
    client.send(text)
    print("succeed")
    print("")

・送るテキストファイル (send.txt)

hello!
my name is Alex


実行結果
・サーバ側

> python .\server.py
host: 0.0.0.0
port: 50000
from:(クライアント側のIPアドレス)(クライアント側のポート番号)
waiting for his response...
hello!
my name is Alex

waiting for his response...

・クライアント側

> python .\client.py
textfile: fdsa
can't open this file
please try it again

textfile: send.txt
succeed

textfile:

・得られたファイル (get.txt)

hello!
my name is Alex


解説
1.サーバ側はクライアント側からの接続を待ち、接続後はデータの送信を待つ。
2. クライアント側はテキストファイル名を入力する。


ファイアウォールの設定等が原因で送れないことがあるので注意。


(間違い等あればコメントよろしくお願いいたします。)