Pythonロボティクスコース レッスン 42
テーマ.11-1 ネットワーク通信やインターネットの仕組み
Studuino:bitをインターネットへ接続しよう!
チャプター1
このレッスンで学ぶこと
このレッスンではStuduino:bitをインターネットへ接続し、Webページから情報を取得する方法を学習します。インターネットが普及した経緯やその仕組みについても簡単に見ていきましょう。
チャプター2
インターネットの世界
インターネットは、世界中の情報端末が通信回線でつながった最大規模のネットワークです。インターネットを利用することで、いつでもどこでもすぐに自分の欲しい情報にアクセスすることができます。
最近は、次世代の通信技術「5G」が登場し、通信速度の高速化や同時接続可能な情報端末数の大幅な増加、遅延の改善によって、自動車や生活家電のようにこれまでインターネットとは関係がなかった様々なものがインターネットにつながる「IoT(Internet of Things)」の実現が期待されています。
2. 1 インターネットの普及に貢献したWebの開発
インターネットは、1990年代に欧州原子核研究機構(cern)に所属していた、コンピューター技術者のティム・バーナーズ=リーによって「World Wide Web(WWW)」(いわゆるWeb)というハイパーメディアシステムが開発されたことで急速に普及しました。ハイパーメディア(Hyper Media)はリンクによって相互に参照できる文書のことで、つまりはWebサイトです。
ティム・バーナーズ=リーは、WWWの中で、ハイパーメディアを記述するための様式として「HTML(Hyper Text Markup Language)」と、ハイパーメディアを転送するための方式である「HTTP(Hyper Text Transfer Protocol)」、それから世界中にあるWebサイトの中から一意に指定するための規約「URL(Uniform Resource Locator)」を開発しています。また、Webサイトを閲覧するための「WWWクライアント」(いわゆるWebブラウザ)を開発し、無償で公開しました。
1991年8月に彼によって世界で初めて公開されたWebサイトの復刻版が、今でも欧州原子核研究機構のWebサイト内で閲覧できます。
世界初のWebサイトの復刻ページ(http://info.cern.ch/hypertext/WWW/TheProject.html)
現在は、ティム・バーナーズ=リーを中心に組織されたWeb技術の標準化団体「W3C」によりWebの標準仕様が策定されています。
2. 2 インターネットの通信網
ここからは少し詳しくインターネットの仕組みを見ていきましょう。
インターネットの通信網は、「LAN(Local Area Network)」と「WAN(Wide Area Network)」の2つの分けられます。
LANは、家庭や企業、大学など限られた範囲で情報端末を相互に接続して構築されるネットワークです。これに対して、地理的に離れた場所をつなぐ広域なネットワークがWANです。
一般的な家庭では、回線事業者やプロバイダーが設置したルーターにコンピュータやスマートフォン、ゲーム機を接続しています。そして、それらの端末からインターネットにアクセスするときは、ルーターを通して外部のネットワークに接続します。
プロバイダーは、他のプロバイダーや規模の大きな企業や大学と通信回線をつないでいます。また、それだけでなく、インターネットエクスチェンジという施設と接続することで、他に接続しているすべてのプロバイダーと相互に接続できるようになっており、世界中のどこにでもつながる大規模なネットワークが構築されています。
2. 3 インターネットの階層構造
インターネット上でデータをやり取りする仕組みを理解する上で、アメリカの国防高等研究計画局(DARPA)から提唱されている「DARPAモデル」が参考になります。DARPAモデルでは、インターネットに必要な通信機能を、4つの階層構造に分割して説明しています。
【 DARPAモデル 】
- アプリケーション層
アプリケーションプログラムの間で、どのような形式や手順でデータをやり取りするのかを定めています。文字コードや画像フォーマット、データの暗号化などもこの層で扱います。 - トランスポート層
トランスポート層では、アプリケーションプログラム間でのデータ伝送を実現します。必要に応じて行われるデータの誤り検出や送信元へのデータの再送要求もこの層で扱います。 - インターネット層
インターネットに接続された複数の機器間でのデータの伝送を実現します。ネットワークの中から対象の機器を特定するために使用されるIPアドレスはインターネット層で扱います。 - ネットワークインタ―フェイス層
ネットワークインターフェイス層は、最もハードウェアに近い階層で、有線LANや無線LANを使用して通信するために、データを物理的な電気信号へ変換する役割があります。
DARPAモデルの他に、国際標準化機構(ISO)によって策定された「OSI参照モデル」があります。OSI参照モデルでは、コンピュータにとって必要な通信機能を7つの階層に分けて定義しています。
- アプリケーション層
ファイルの転送や、Webサイトのデータの取得など、具体的に行われる通信サービス。 - プレゼンテーション層
文字コードや暗号化など通信するデータの表現形式や変換方法。 - セッション層
通信の開始から終了までの手順についての取り決め。 - トランスポート層
エラーの訂正やデータの再送要求など、通信の信頼性を担保するための仕組み。 - ネットワーク層
ネットワーク上で目的の相手との通信するための経路を確保するための仕組み。 - データリンク層
ルーターなど直接近くに接続されている機器との間で信号の受け渡しを行うための仕組み。 - 物理層
ケーブルなど物理的な接続や電気信号への変換を行うための仕組み。
DARPAモデルとOSI参照モデルは、どちらも通信に必要な機能を階層別に定義したという意味では同じです。OSI参照モデルはより高位の抽象的な概念になっており、DARPAモデルは現在のインターネットの通信機能を実装する上で現実的な仕様を策定しているという違いがあります。
チャプター3
Studuino:bitをインターネットへ接続する
それでは実際に、Wi-Fiを使用してStuduino:bitをインターネットへ接続してみましょう。
3. 1 networkモジュールを使ったWi-Fiの利用
Wi-Fiを利用してインターネットへの接続を行うときは、network
モジュールのWLAN
※クラスを使用します。
※ 「WLAN」は「Wireless LAN(無線LAN)」を省略した表記です。
■ WLANクラスのオブジェクトの作成
WLAN
クラスはコンストラクタで動作モードを引数として受け取ります。この引数に「network.STA_IF
」を指定した場合、作成されたオブジェクトはステーション(別名:クライアント)として機能します。「network.AP_IF
」を指定した場合は、アクセスポイントとして機能します。
- ステーション:
network.STA_IF
無線LANの子機として動作するモード - アクセスポイント:
network.AP_IF
無線LANの親機として動作するモード
ここでは、教室や学校に設置されているWi-Fiのアクセスポイントに接続するため、ステーションとしてStuduio:bitを動作させましょう。
以下のコードで、ステーションとしてWLAN
オブジェクトを新たに作成します。
import network
wifi = network.WLAN(network.STA_IF)
■ ネットワークインタフェースの有効化/無効化
WLAN
クラスのほとんどのメソッドは、ネットワークインターフェースが有効化されていないと使用できません。有効化と無効化を行う場合、次のactive()
メソッドを実行します。
- ネットワークインターフェースを有効化するとき
active()
メソッドの引数に"up"
またはTrue
を渡して実行します。 - ネットワークインターフェースを無効化するとき
active()
メソッドの引数に"down"
またはFalse
を渡して実行します。
以下のコードを追加して、ネットワークインターフェースを有効化しましょう。
追加【4行目】
import network
wifi = network.WLAN(network.STA_IF)
wifi.active("up") # wifi.active(True)でも同様に有効化できる
■ アクセスポイントの一覧の取得
WLAN
クラスのscan()
メソッドを実行すると、利用可能なアクセスポイントの一覧を取得できます。このメソッドはアクセスポイントの情報を以下のタプルで返します。
(SSID、MACアドレス、チャンネル数、受信信号強度、認証モード、可視/非可視)
- SSID
Wi-Fiのアクセスポイントを識別するための名前。最大で32文字までの英数字で設定されています。 - MACアドレス
ネットワークに接続する機器に付けられた固有の識別番号。メーカーが製造する段階で「FF-FF-FF-FF-FF-FF」や「00:00:00:00:00:00」のように16進数を用いた表現で必ず付けられています。 - チャンネル数
最大でチャンネル数と同じ数の情報端末をアクセスポイントに接続できます。 - 受信信号強度
アクセスポイントから発信されている信号の受信強度。 - 認証モード
無線LANのデータ通信で利用する暗号化方式。 - 可視/非可視
可視設定の場合、他の機器からSSIDの検索ができますが、非可視設定の場合はできません。
実際に以下のコードを追加してプログラムを実行し、近くのアクセスポイントの一覧を表示しましょう。また、その中に教室で用意されているアクセスポイントがあることを確認しましょう。
追加【6行目~8行目】
import network
wifi = network.WLAN(network.STA_IF)
wifi.active("up")
aps = wifi.scan() # アクセスポイントの一覧を取得
for ap in aps: # 各アクセスポイントの情報を表示
print(ap)
(実行結果の例)
(b'MyAP', b'\x00\x00\x00\x00\a', 6, -53, 3, False) (b'xxxxxxxx', b'\x00\x00\x00\x00\b', 1, -91, 4, False) (b'yyyyyyyy', b'\x00\x00\x00\x00\c', 1, -91, 4, False) (b'zzzzzzzz', b'\x00\x00\x00\x00\d', 1, -92, 4, False) (b'oooooooo', b'\x00\x00\x00\x00\e', 1, -93, 3, False) (b'pppppppp', b'\x00\x00\x00\x00\f', 6, -94, 3, False) (b'qqqqqqqq', b'\x00\x00\x00\x00\g', 11, -94, 3, False)
■ アクセスポイントへの接続と通信の切断
アクセスポイントへ接続するときは、connect()
メソッドを実行します。このメソッドは、第1引数に「アクセスポイントの識別名(SSID)」を、第2引数に「パスワード(PASSWORD)」を取ります。
反対に、アクセスポイントとの通信を切断する場合はdisconnect()
メソッドを実行します。connect()
メソッドを一度実行すると、このdisconnect()
メソッドが実行されるか、もしくはネットワークインターフェースが無効化されるまで、繰り返し接続が試みられます。
以下のようにコードを変更して、教室や学校のアクセスポイントに接続しましょう。
変更【5行目】
※ アクセスポイントの一覧を表示するコードは削除します。
import network
wifi = network.WLAN(network.STA_IF)
wifi.active("up")
wifi.connect("SSID", "PASSWORD") # 利用するWi-FiのSSIDとパスワードを指定します
(接続に成功した場合のターミナルの表示例) ※↓表示されません。
I (15524) wifi: new:<6,0>, old:<1,0>, ap:<255,255>, sta:<6,0>, prof:1 I (16204) wifi: state: init -> auth (b0) I (16214) wifi: state: auth -> assoc (0) I (16224) wifi: state: assoc -> run (10) I (16244) wifi: connected with xxxxxx, channel 6, bssid = xx:xx:xx:xx:xx:xx I (16244) wifi: pm start, type: 1 I (16254) network: CONNECTED I (17014) event: sta ip: 172.20.10.4, mask: 255.255.255.240, gw: 172.20.10.1 I (17014) network: GOT_IP
(接続に失敗した場合のターミナルの表示例)
no AP found I (41584) wifi: new:<6,0>, old:<6,0>, ap:<255,255>, sta:<6,0>, prof:1 I (41584) wifi: state: init -> auth (b0) I (41584) wifi: state: auth -> assoc (0) I (41594) wifi: state: assoc -> run (10) I (41614) wifi: state: run -> init (2c0) I (41614) wifi: new:<6,0>, old:<6,0>, ap:<255,255>, sta:<6,0>, prof:1 I (41614) wifi: STA_DISCONNECTED, reason:2
■ 接続の有無の確認
アクセスポイントに接続できているかどうかは、isconnected()
メソッドで調べることができます。is_connected()
メソッドは接続できている場合はTrue
を返し、未接続の場合はFalse
を返します。
上で説明したように、connect()
メソッドはアクセスポイントに接続できるまで、裏側で繰り返し接続を試みます。しかし、必ずしもネットワークに接続する必要がないプログラムを実行する場合、これでは他の処理を妨げてしまうことになります。
そこで以下のコードでは、isconnected()
メソッドを利用して、connect()
メソッドを実行してから、10秒以上が経過してもアクセスポイントへ接続できなかったときに、途中で断念するようにしています。
追加・変更【2行目、6行目~20行目】
import network
import time # 10秒の経過を計測するためにtimeモジュールを追加
wifi = network.WLAN(network.STA_IF)
wifi.active("up")
if not wifi.isconnected(): # 未接続の場合のみ下記のコードを実行
wifi.connect("SSID", "PASSWORD") # アクセスポイントへの接続を試みる(教室用のSSID、PWを入力する)
timeout = 10 # 10秒のカウント用の変数。1秒おきに1ずつ減らす。
while timeout > 0:
print(".")
time.sleep_ms(1000) # 1秒待つ
timeout -= 1 # カウンターを1減らす
if wifi.isconnected(): # 接続できた場合は9行目のループを抜ける
print("Connected.")
break
else: # break文が実行されなかった(10秒が経過した)場合は以下のコードが実行される
print("Connection failed!")
wifi.disconnect() # 接続を断念する
else: # 既に接続されている場合は下記のコードを実行
print("Already connected.")
■ インターネット層レベルのネットワークに関するパラメータの取得
アクセスポイントへ接続すると、Studuino:bitには自動的に「IPアドレス」が割り当てられます。
IPは、Internet Protocolの略で、DARPAモデルのインターネット層にあたります。IPでは、ネットワーク上で互いに通信する相手を特定できるように、ネットワーク上の住所を表すIPアドレスが各情報端末に割り当てられます。
IPアドレスにはLAN(ローカルエリアネットワーク)内で割り当てられる「ローカルIPアドレス」と、WAN(ワイドエリアネットワーク)内で割り当てられる「グローバルIPアドレス」があります。
IPアドレスを含むインターネット層レベルのパラメータは、ifconfig()
メソッドで取得できます。このメソッドの返り値は以下のタプルです。
(IPアドレス、サブネットマスク、ゲートウェイ、DNSサーバー)
- IPアドレス
インターネット層にある情報端末を識別するために割り振られた識別番号。このIPアドレスは、ローカルIPアドレスです。 - サブネットマスク
ネットワークの範囲を定義するために使われる数値。サブネットマスクを使うことで、もともと同じネットワークにある情報端末をいくつかのグループに分割して管理することができます。 - ゲートウェイ
外部のネットワークとの通信の出入口となっているアクセスポイントのIPアドレス。 - DNSサーバー
DNSとして機能しているサーバーのIPアドレス。DNSは「Domain Name System」の略で、例えば「artec-kk.co.jp」のようなインターネット上のドメイン名とIPアドレスの関係を管理するために開発されたシステムです。本来はIPアドレスを指定して他の情報端末にアクセスする必要がありますが、それでは分かりにくいため、代わりにドメイン名が用いられています。
実際に以下のコードを追加して、各パラメータをターミナルに表示してみましょう。
追加【15行目、22行目】
import network
import time
wifi = network.WLAN(network.STA_IF)
wifi.active("up")
if not wifi.isconnected():
wifi.connect("ssid", "password")
timeout = 10
while timeout > 0:
print(".")
time.sleep_ms(1000)
timeout -= 1
if wifi.isconnected():
print("Connected.")
print(wifi.ifconfig()) # パラメータの表示
break
else:
print("Connection failed!")
wifi.disconnect()
else:
print("Already connected.")
print(wifi.ifconfig()) # パラメータの表示
(実行結果の例)
Connected. ('172.20.10.2', '255.255.255.240', '172.20.10.1', '172.20.10.1')
3. 2 アクセスポイントへの接続機能をモジュール化する
ここまでで確認したnetowork
モジュールを用いたWi-Fiのアクセスポイントへの接続機能を、独自のクラス「MyWifi
」を作成してまとめましょう。
【 MyWifiクラスのメソッド 】
メソッド名 | 処理内容 |
---|---|
__init__(self) | WLAN クラスのオブジェクトを作成し、ネットワークインタフェースを有効化する。 |
connect(self, ssid, password) | 指定されたSSIDとパスワードでアクセスポイントに接続する。接続に成功した場合はTrue を返し、IPアドレスなどのパラメータを表示する。接続に失敗した場合はFalse を返す。 |
disconnect(self) | アクセスポイントとの接続を解除する。 |
isconnected(self) | アクセスポイントと接続されているかどうかを返す。 |
get_aps(self) | 接続可能なアクセスポイントの一覧をターミナルに表示する。 |
■ MyWifiクラスの定義
新しくプログラムファイルを作成し、以下のコードを書きましょう。
【 MyWifi
クラスのコード 】 ※def connect のssid、pwはそのままに!
import network
import time
class MyWifi:
def __init__(self):
self.wifi = network.WLAN(network.STA_IF) # ステーションとしてWLANオブジェクトを作成
self.wifi.active("up") # ネットワークインターフェースを有効化
def connect(self, ssid, password):
if not self.wifi.isconnected(): # 未接続の場合のみ以下のコードを実行
self.wifi.connect(ssid, password) # アクセスポイントへの接続を試みる
timeout = 10
while timeout > 0: # 最大で10秒間接続まで待機する
print(".")
time.sleep(1)
timeout -= 1
if self.wifi.isconnected(): # 接続できた場合
print("Connected.")
print(self.wifi.ifconfig()) # IPアドレスを含むパラメーターを表示する
return True # 接続に成功したので「True」を返す
else: # 接続に失敗した場合
print("Connection failed!")
self.disconnect() # 接続の試みを止める
return False
else: # 接続済みの場合は、以下のコードを実行
print("Already connected.")
print(self.wifi.ifconfig())
return True
def disconnect(self): # アクセスポイントとの接続を解除する
self.wifi.disconnect()
print("Disconnected.")
def isconnected(self): # アクセスポイントと接続されているかどうかを返す
return self.wifi.isconnected()
def get_aps(self): # アクセスポイントの一覧を表示する
aps = self.wifi.scan()
for ap in aps:
print(ap)
■ クラスの動作確認とモジュール化
定義したクラスの動作を確認します。上のMyWifi
クラスを定義したプログラムを実行します。実行後にターミナルでMyWifi
クラスのインスタンスを作成し、SSIDとパスワードを指定してconnect()
メソッドを実行しましょう。
(プログラムの実行例) ※SSID、PWに情報を入れること
>>> wifi = MyWifi()
>>> wifi.connect("SSID","PASSWORD")
.
.
.
Connected.
('172.20.10.2', '255.255.255.240', '172.20.10.1', '172.20.10.1')
True
残りのメソッドも問題なく実行できることを確認した上で、このプログラムファイルをモジュール化します。
「mywifi」と名前を付けてパソコンにファイルを保存します。メニューから「ファイル」を選択して、Studuino:bit内に保存したファイルをコピーしましょう。
Studuino:bit内への保存方法の詳細は、レッスン20 チャプター3の「■ PCのファイルをStuduino:bitの内部に保存する」を参考にして下さい。
チャプター4
インターネットからWebページの情報を取得する
ここまでで、Wifiのアクセスポイントを経由して、インターネットに接続する方法を確認してきました。ここからは、実際にインターネット上のWebページから情報を取得する方法を見ていきます。
4. 1 インターネットを利用した通信の規約
インターネットを利用して、コンピュータ間の通信を成立させるためには、お互いが共通のルールに従ってデータの転送と受信ができる仕組みが必要です。コンピューターの世界では、このルールのことを「通信プロトコル(または通信規約)」といいます。
上で紹介した、インターネットの階層構造を定義した「DARPAモデル」では、各階層ごとに様々な通信プロトコルが存在しています。上位層の通信プロトコルは、下位層の通信プロトコルによって実装されています。最上位のアプリケーション層では次の通信プロトコルが使われています。
※ 他の階層については、テーマ.11-3で詳しく解説します。
【 DARPAモデルにおけるアプリケーション層での通信プロトコルの例 】
プロトコル名 | 正式名称 | 説明 |
---|---|---|
HTTP | Hyper Text Transfer Protocol | WebブラウザがWebサーバーと通信するときに使われる通信プロトコル。HTMLで書かれたWebページ内のコンテンツの送受信を行うことができます。 |
SMTP | Simple Mail Transfer Protocol | インターネットを利用して、電子メールを転送するときに使われる通信プロトコル。 |
POP3 | Post Office Protocol | 利用者がメールサーバーに保管されている自分宛の電子メールを取り出すときに使われる通信プロトコル。 |
FTP | File Transfer Protocol | クライアントとサーバーの間でファイルの転送を行うときに使われる通信プロトコル。 |
Telnet | Teletype network | インターネットを利用して、遠隔地にあるサーバーやルーターなどの情報端末を制御するときに使われる通信プロトコル。すべてのデータ通信は暗号化されていないテキスト(Plane text)で行われるため、安全性に問題があり、代わりとしてSSHが使われることも多い。 |
SSH | Secure Shell | 暗号化や認証の技術を利用して、遠隔地にある情報端末と安全に通信するためのプロトコル。 |
このレッスンでは、Webページの情報を取得するために、「HTTP」を利用します。
4. 2 HTTPについて
HTTPは、「HTML(HyperText Markup Language)」や「XML(Extensible Markup Language)」で書かれたWebページのデータを転送するために開発された通信プロトコルです。テキストデータ以外にも、画像や音声などWebページ内に埋め込まれた様々なコンテンツ(リソースといいます)を扱うことができます。
HTTPを用いた通信は、クライアント(Webブラウザ)からサーバーへ「リクエスト(request)」を送ることで開始されます。サーバーはクライアントから送られてきたリクエストに応じて、Webページのコンテンツを含む結果を返します。これを「レスポンス(response)」といいます。
クライアントから送るリクエストには、様々な情報が含まれています。その情報の中心にあるのが、次に説明する「URL」と「HTTPメソッド」です。
■ URLとは
URL(Uniform Resource Locator)とは、簡単に説明すると、インターネット上の情報がどこに存在しているのかを示した住所のことです。Webブラウザでは、上部に以下のような文字列でURLが表示されています。
http://www.artec-kk.co.jp/artecrobo2/pdf/jp/studuinobit_class_reference.pdf
※ 上のURLは、ArtecRobo2.0をPythonで制御するためのリファレンス(参照資料)の場所を表しています。
このURLに含まれる文字列は、それぞれ次の意味を表しています。
- スキーム:
http:
と呼ばれ、主に通信プロトコルの名前が使われることが多いです。この場合はHTTPを利用することを表しています。 - ホスト名/ドメイン名:
//www.artec-kk.co.jp
リクエストの送信先のサーバーを表す「ホスト名(www
)」と「ドメイン名(artec-kk.co.jp
)」です。 - パス:
/artecrobo2/pdf/jp/studuinobit_class_reference.pdf
取得するデータやWebページの名前(studuinobit_class_reference.pdf
)と、それがサーバー内のどのディレクトリ(/artecrobo2/pdf/jp/
)に存在しているかを表しています。これを「パス(Path)」といいます。
■ HTTPメソッドとは
HTTPメソッドは全部で9つあり、URLで指定したリソースに対して、どのような操作を行うのかを定めています。今は全てを覚える必要はありませんので、次の5つのメソッドを押さえておきましょう。
- GET
リソースを取得するメソッド。GETメソッドを実行することで、サーバーのリソースが変更されることはありません。 - HEAD
GETメソッドと同じレスポンスを求めますが、レスポンスの本体(リソース本体)は取得しません。 - POST
データを送信するメソッド。サーバーは受信したデータを元に、リソースを書き換えたり、新たなリソースを作成したりして、レスポンスを送ります。 - PUT
リソースをアップロードしたり、既に存在しているリソースを上書きするメソッド。 - DELETE
リソースを削除するメソッド。
■ HTTPのリクエストとレスポンスの内容
また、HTTPのリクエストやレスポンスは、内部で次のように情報が分かれています。
例えば、リクエストの先頭は、以下のようなメソッド名とURLから構成されています。最後の「HTTP/1.1
」はHTTPのバージョンを表していて、最新は「HTTP/2
」です。
GET https://www.artec-kk.co.jp/school/cl/textbooks/sample/ HTTP/1.1
その下のヘッダには、以下のようにクライアント自身の情報やサーバーへ要求する様々な情報が書かれています。
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Accept-Encoding: gzip, deflate, br Accept-Language: ja,en-US;q=0.9,en;q=0.8 Cache-Control: max-age=0 Connection: keep-alive Host: www.artec-kk.co.jp User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
|ヘッダの種類|意味|
|:—|:—|
|Accept|レスポンスとして受け入れ可能なリソースの種類|
|Accept-Encoding|Webブラウザでデコードが可能なデータのエンコーディングの種類|
|Accept-Language|Webブラウザが予期している言語の種類|
|Cache-Control|キャシュを制御するための情報。キャッシュは、一度アクセスしたサイトのデータをWebブラウザで一時的に保管し、次回以降に同じページへアクセスしたときの表示速度を上げる仕組みです。|
|Connection|サーバーとのコネクションを切断する場合は「close」を指定し、コネクションを維持する場合は「keep-alive」を指定。HTTP/1.1以降のデフォルトは「keep-alive」です。|
|Host|URLに含まれているホスト名やドメイン名。|
|User-Agent|リクエストを送信するWebブラウザやOSの種類とそのバージョン情報。|
上のヘッダ情報は一例で、他にも様々なヘッダが存在しています。サーバーはこれらのヘッダ情報を読み取り、必要な処理を行います。
そして、リクエストのボディには、サーバーにリソース(テキストデータや画像、音声などのファイル)を送信する場合のみ、その情報が書かれています。
レスポンスの情報については、実際にWebページへリクエストを送るプログラムを作成して、内容を確認していきましょう。
4. 3 HTTP通信の実践
MicroPythonでは、urequests
モジュールでHTTP通信を簡単に行うことができます。urequests
モジュールにはHTTPメソッドに対応した以下の関数が用意されています。
【 urequestsモジュールの関数 】
関数名 | 処理内容 |
---|---|
get(url, headers={}) | GETメソッドでリクエストを送る |
post(url, data, json, headers={}) | POSTメソッドでリクエストを送る |
put(url, data) | PUTメソッドでリクエストを送る |
delete(url) | DELETEメソッドでリクエストを送る |
head(url, headers={}) | HEADメソッドでリクエストを送る |
request(method, url, data=None, json=None, headers={}) | メソッド名を指定してリクエストを送る |
上記の関数はすべて、response
オブジェクトを返します。このresponse
オブジェクトにサーバーから送られてきたレスポンスの情報が格納されています。response
オブジェクトは、以下のプロパティとメソッドを持ちます。
【 responseオブジェクトのプロパティとメソッド 】
プロパティ名/メソッド名 | 内容 |
---|---|
status_code | HTTPステータスコード |
reason | HTTPステータスコードのリーズンフレーズ |
encoding | 文字エンコード |
content | デコード前のレスポンスデータ |
text | content をencoding に従ってデコードしたテキスト |
json() | content をJSONとして解釈し、辞書型のデータとして返すメソッド |
「ステータスコード」や「JSON」という言葉が初めて出てきましたが、これらについては、後の解説や次のレッスンで詳しく説明をしていきます。まずは、シンプルなWebページに対して、GETメソッドでリクエストを送り、返ってきたレスポンスを確認してみましょう。
■ GETメソッドでWebページのデータを取得する
GETメソッドで次のURLからWebページのデータを取得します。
https://www.artec-kk.co.jp/school/cl/textbooks/sample
このURLをWebブラウザで開くと、次のようなとてもシンプルなページが表示されます。
このWebページは、HTMLで書かれていて、元の文書は「ソース(Source)」と呼ばれます。ソースはWebブラウザ上の表示とは異なります。
※ よく誤解されがちですが、HTMLはプログミング言語ではありません。章・節・項のような階層構造と相互に参照可能なリンクを持つ文書を表すための言語です。
Webブラウザには、元のHTMLの文書を表示する機能が備わっています。例えばChromeの場合は、Webページ上で右クリックをし、表示された項目の中から「ページのソースを表示」を選択します。他のブラウザも手順は同じで、Edgeの場合は「ソースを表示」、Safariの場合はメニューバーの「開発」の中から「ページのソースを表示」を選択します。
上の手順で表示したソースが以下になります。
HTMLでは、文書内での役割や意味を「タグ」と呼ばれる表現で囲んで示します。例えば、5行目の<title></title>
というタグは、このWebページのタイトルを表しています。Googleなどの検索エンジンを利用したときに、検索結果の一覧に表示されるタイトルです。また、8行目の<h1></h1>
は大見出しを、9行目の<p></p>
は段落をそれぞれ表しています。
他にも、HTMLには表や画像、音声、動画など様々なコンテンツに対応したタグが用意されています。すべてのタグについて説明すると1冊の本になってしまいますので、この講座では取り扱いませんが、Webサイトの制作に興味がある人は書籍や他の講座で学習してみてください。
では、以下のサンプルコードを実行して、GETメソッドでこのWebページの情報を取得してみましょう。
【 サンプルコード 4-3-1 】
import urequests
from mywifi import MyWifi # レッスンの前半で作成したWi-Fi接続用のモジュール
url = "https://www.artec-kk.co.jp/school/cl/textbooks/sample/" # 対象のURL
wifi = MyWifi()
if wifi.connect("SSID", "PASSWORD"): # 使用するWi-FiのSSIDとパスワードを指定
response = urequests.get(url) # GETメソッドでリクエストを送る
print(response.status_code, response.reason, response.text, sep="\n") # sepで区切り文字を指定できます。ここでは改行文字を指定しています。
(実行結果の例)
Connected. ('172.20.10.2', '255.255.255.240', '172.20.10.1', '172.20.10.1') 200 b'OK' <!doctype html> <html> <head> <meta charset="utf-8"> <title>Python Robotics Course</title> </head> <body> <h1>Python Robotics Course</h1> <p>This is a sample website for learning HTTP methods.</p> </body> </html>
実行結果から、さきほど確認したものと同じHTMLのソースが取得できていることが分かります。
そして、ソースの上に表示した「200
」と「OK
」は、それぞれステータスコードとそのリーズンフレーズです。これらは、上で確認したレスポンス構造の先頭の行(ステータス行)に書かれています。
ステータスコードは、HTTPのリクエストが正常に処理されたのかを表す3桁の番号です。「200」は正常に完了したことを表しています。ステータスコードとリーズンフレーズはセットになっていて、ステータスコードが「200」のときのリーズンフレーズは「OK」です。
以下の表は、ステータスコードとリーズンフレーズの一例です。
【 HTTPのステータスコードとリーズンフレーズの一例 】
ステータスコード | リーズンフレーズ | 意味 |
---|---|---|
200 | OK | リクエストが成功したことを表します。 |
301 | Moved Permanently | リクエストされたURLが永久的に別のURLに変更されたことを表します。このときは、レスポンスで変更後のURLが送信されます。 |
304 | Not Modified | キャッシュを使用している場合に、前回のアクセスからレスポンスが変更されていないことを表します。そのため、クライアントはキャッシュに残っているリソースを使用してページを表示します。 |
404 | Not Found | サーバーがリクエストされたリソースを発見できない(リソースが存在しない)ことを表します。 |
405 | Method Not Allowed | リクエストされたメソッドがサーバーで許可されていないことを表します。ただし、GETやHEADに対して、このステータスコードが返されることはありません。 |
501 | Not Implemented | リクエストされたメソッドにサーバーが対応していないことを表します。ただし、GETやHEADに対して、このステータスコードが返されることはありません。 |
例えば、【 サンプルコード 4-3-1 】を変更し、GETメソッドに代わり、PUTメソッドでリクエストを送ってみましょう。
【 サンプルコード 4-3-2 】
変更【8行目】
import urequests
from mywifi import MyWifi
url = "https://www.artec-kk.co.jp/school/cl/textbooks/sample/"
wifi = MyWifi("SSID", "PASSWORD")if wifi.connect():
response = urequests.put(url) # PUTメソッドに変更
print(response.status_code, response.reason, response.text, sep="\n")
(実行結果の例)
Connected. ('172.20.10.2', '255.255.255.240', '172.20.10.1', '172.20.10.1') 405 b'Method Not Allowed' <HTML> <HEAD> <TITLE>405 Method Not Allowed</TITLE> </HEAD> <BODY> <H1>Method Not Allowed</H1> The HTTP verb used to access this page is not allowed. <HR> <ADDRESS> Web Server at artec-kk.co.jp </ADDRESS> </BODY> </HTML>
ステータスコードが「405」、リーズンフレーズが「Method Not Allowed」になっています。これは、サーバーでPUTメソッドによるリソースの上書きが禁止されているためです。このように、ステータスコードやリーズンフレーズを確認することによって、正常にリクエストが処理されなかった場合の理由が分かるようになっています。
チャプター5
インターネットから取得したデータを用いたLEDディスプレイの制御
サーバー上のWebページには、クライアントからのリクエストに対して常に同じレスポンスを返すものと、リクエストに含まれる情報によって異なるレスポンスを返すものがあります。
さきほどのサンプルコードで利用したWebページは前者で、以下のWebページは後者です。
https://www.artec-kk.co.jp/school/cl/textbooks/sample/getrgb.php
このWebページは、リクエストで送られてきた色名の情報によって、対応するRGB値をレスポンスとして返します。実際に、次の2つのURLを開き、表示内容が異なることを確かめてみましょう。
https://www.artec-kk.co.jp/school/cl/textbooks/sample/getrgb.php?color=red
https://www.artec-kk.co.jp/school/cl/textbooks/sample/getrgb.php?color=green
上の2つのURLは、どちらも元のURLの末尾に「?」が追加されています。その後ろに続く「color=red
」や「color=green
」を「クエリパラメータ」といい、GETメソッドを利用してサーバーに情報を送るときに使われます。
5. 1 クエリパラメータをURLに追加する
クエリパラメータは、URLの末尾に「?」を付け、その後ろに「パラメータ(変数)の名前=パラメータ(変数)の値」の形式で指定します。複数のパラメータを送りたい場合は、以下のように「&
」でつなぎます。
http://www.****.com/***/***.php?パラメータ名=値&パラメータ名=値&...
上で紹介したWebページは、次のパラメータが送られてくると、それに応じたレスポンスを返すようになっていました。つまり、このWebページは色名からRGB値を検索できる機能を提供していると言えます。
【 対応しているクエリパラメータ 】
クエリパラメータ | URL | レスポンス |
---|---|---|
color=red | https://www.artec-kk.co.jp/school/cl/textbooks/sample/getrgb.php?color=red | (31, 0, 0) |
color=green | https://www.artec-kk.co.jp/school/cl/textbooks/sample/getrgb.php?color=green | (0, 31, 0) |
color=blue | https://www.artec-kk.co.jp/school/cl/textbooks/sample/getrgb.php?color=blue | (0, 0, 31) |
color=yellow | https://www.artec-kk.co.jp/school/cl/textbooks/sample/getrgb.php?color=yellow | (31, 31, 0) |
color=cyan | https://www.artec-kk.co.jp/school/cl/textbooks/sample/getrgb.php?color=cyan | (0, 31, 31) |
color=magenta | https://www.artec-kk.co.jp/school/cl/textbooks/sample/getrgb.php?color=magenta | (31, 0, 31) |
color=white | https://www.artec-kk.co.jp/school/cl/textbooks/sample/getrgb.php?color=white | (31, 31, 31) |
該当なし | https://www.artec-kk.co.jp/school/cl/textbooks/sample/getrgb.php?color=orange | Not Found |
それでは、このWebページを利用して、ユーザーがターミナルに入力した色名からRGB値を取得し、Studuino:bitのLEDディスプレイを点灯するプログラムを作成してみましょう。
5. 2 Webページで色名に対応するRGB値を調べてLEDディスプレイを点灯するプログラムの作成
以下の処理を順番に行うlight_up()
という関数をつくり、この関数をターミナルから呼び出して実行します。
- Wi-Fiへの接続
- ユーザーからの入力の受け付け
- Webページからの情報の取得
- 取得した情報の安全性の確認
- LEDディスプレイの点灯
順番にコードを書いていきましょう。
■ Wi-Fiへの接続
light_up()
関数が呼び出されたとき、Wi-Fiに未接続の場合は接続を試みます。接続に失敗した場合は続きの処理が実行できないため、return
文を実行して処理を終えるようにしましょう。
from mywifi import MyWifi # レッスンで作成したモジュールから独自クラスをインポート
SSID = "SSID" # 使用するWi-FiのSSID
PASSWORD = "PASSWORD" # 使用するWi-FiのPASSWORD
wifi = MyWifi() # 独自クラスのオブジェクトの作成
def light_up(): # ターミナルから実行する関数
if not wifi.isconnected(): # Wi-Fiに未接続の場合のみ接続処理を実行する
if not wifi.connect(SSID, PASSWORD): # Wi-Fiへの接続。
return # 接続に失敗した場合はここで関数の処理を終える
■ ユーザーからの入力の受け付け
次に、input()
関数でユーザーから色名の入力を受け付けます。以下のコードを追加しましょう。
追加【11行目】
def light_up():
if not wifi.isconnected(): # Wi-Fiに未接続の場合のみ接続処理を実行する
if not wifi.connect(SSID, PASSWORD):
return
colorname = input("Enter color name. >>>") # 色名の入力を求める
■ Webページからの情報の取得
ユーザーが入力した色名をパラメータの値としてURLを作成し、GETメソッドでサーバーにリクエストを送ります。レスポンスのステータスコードを確認し、成功しなかった場合は、ステータスコードとリーズンフレーズを表示して、ここで関数の処理を終えます。
追加【1行目、7行目、14~17行目】
import urequests # HTTP用のモジュールのインポート
from mywifi import MyWifi
SSID = "SSID"
PASSWORD = "PASSWORD"
wifi = MyWifi()
url_base = "https://www.artec-kk.co.jp/school/cl/textbooks/sample/getrgb.php?color=" # リクエストを送るWebページのURLのベース
def light_up():
if not wifi.isconnected():
if not wifi.connect(SSID, PASSWORD):
return
colorname = input("Enter color name. >>>")
response = urequests.get(url_base + colorname) # リクエストをGETメソッドで送る
if not response.status_code != "200": # リクエストが成功した場合のステータスコードは「200」
print(response.status_code, response.reason) # リクエストが失敗した場合はステータスコードとリーズンフレーズを表示
return # ここで関数の処理を終える
■ 取得した情報の安全性の確認
リクエストが成功した場合、サーバーからは以下のタプル形式の文字列をボディにもつレスポンスが返されます。
(31,31,31)
そこで、これをeval()
関数で式として評価し、タプル型のオブジェクトを得ることができます。
しかし、インターネットから取得した情報の中には、悪意のあるコードが埋め込まれている場合もあります。(※もちろんレッスンで紹介しているWebページにはそのようなコードは埋め込まれていません。)eval()
関数は、文字列をそのままPythonで書かれた式として解釈して評価するため、悪意のあるコードによってプログラムが不具合を起こす可能性もあります。
※ 文字列を式ではなく、プログラム文として解釈して実行するexec()
関数を使用する場合は、さらに危険性が高く、ウィルスが埋め込まれたり、個人情報を盗み取られたりする可能性があります。
そこで、情報の安全性を担保するために、事前に正規表現を利用したマッチングを行い、求めているフォーマットに適合しているかどうかをチェックする対策が取られることがあります。ここでも、この方法を採用してみます。
このプログラムでは、「3つの2桁までの数値を要素にもつタプル型文字列」をレスポンスとして期待しています。このパターンにマッチする正規表現は以下になります。
「(
」や「)
」の丸括弧は、正規表現でグループを表すという特別な意味をもつため、そのまま文字として解釈させるためには、直前にエスケープ文字である「\
」を付ける必要があります。
それでは、上記のことを踏まえた上で次のコードを追加しましょう。
追加【2行目、9・10行目、21~25行目】
import urequests
import ure # 正規表現を扱うためのモジュールのインポート
from mywifi import MyWifi
SSID = "SSID"
PASSWORD = "PASSWORD"
wifi = MyWifi()
url_base = "https://www.artec-kk.co.jp/school/cl/textbooks/sample/getrgb.php?color="
pattern_rgb = "^\(([1-9]\d|\d),([1-9]\d|\d),([1-9]\d|\d)\)$" # 3つの2桁までの数値を要素にもつタプル型文字列の正規表現
regex_rgb = ure.compile(pattern_rgb) # 正規表現のオブジェクト
def light_up():
if not wifi.isconnected():
if not wifi.connect(SSID, PASSWORD):
return
colorname = input("Enter color name. >>>")
response = urequests.get(url_base + colorname)
if not response.status_code != "200":
print(response.status_code, response.reason)
return
match_obj = regex_rgb.match(response.text) # マッチングの実行
if match_obj: # マッチした場合
rgb = eval(response.text) # eval()関数で評価してタプルに変換
else: # マッチしなかった場合
print(response.text) # 文字列をそのままターミナルに表示して内容を確認できるようにする
■ LEDディスプレイの点灯
最後に取得したRGB値でLEDディスプレイを点灯します。以下のコードを追加しましょう。これでプログラムの完成です。
【 サンプルコード 5-2-1 】
追加【3行目、11・12行目、26行目】
import urequests
import ure
from pystubit.board import display, Image # ディスプレイ制御用オブジェクトとイメージのクラスのインポート
from mywifi import MyWifi
SSID = "SSID"
PASSWORD = "PASSWORD"
wifi = MyWifi()
url_base = "https://www.artec-kk.co.jp/school/cl/textbooks/sample/getrgb.php?color="
pattern_rgb = "^\(([1-9]\d|\d),([1-9]\d|\d),([1-9]\d|\d)\)$"
regex_rgb = ure.compile(pattern_rgb)
img = Image("11111:11111:11111:11111:11111") # ディスプレイに表示するイメージオブジェクトの作成
def light_up():
if not wifi.isconnected():
if not wifi.connect(SSID, PASSWORD):
return
colorname = input("Enter color name. >>>")
response = urequests.get(url_base + colorname)
if not response.status_code != "200":
print(response.status_code, response.reason)
return
match_obj = regex_rgb.match(response.text)
if match_obj:
rgb = eval(response.text)
display.show(img, color=rgb) # LEDディスプレイの点灯
else:
print(response.text)
5. 3 プログラムの動作確認
利用するWebページは以下の色名に対応しています。
red、green、blue、yellow、cyan、magenta、white
完成したプログラムを実行して、上記の色名をターミナルから入力し、動作を確認しましょう。
(プログラムの実行例)
>>> light_up() . . . Connected. ('172.20.10.2', '255.255.255.240', '172.20.10.1', '172.20.10.1') Enter color name. >>>red
チャプター6
課題:インターネットから取得したデータを用いたブザーの制御
この課題用に、イギリスの作曲家エドワード・エルガーが作曲した「威風堂々(Pomp and Circumstance)」の楽譜の中から数小節を選び、その音を上から順番に表へまとめたWebページを用意しています。
【 WebページのURL 】
https://www.artec-kk.co.jp/school/cl/textbooks/sample/melody.html
【 Webブラウザの画面 】
そして、このページのHTMLのソースは次のようになっています。
【 Webページのソース 】
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Pomp and Circumstance</title>
</head>
<style>
table{
border-collapse: collapse;
border:1px solid;
}
th,td{
border:1px solid;
padding:0.5em 0.3em;
}
th{
background-color: #eee;
}
</style>
<body>
<h1>Pomp and Circumstance</h1>
<table>
<thead>
<tr>
<th>tone(MIDI)</th><th>duration</th>
</tr>
<thead>
<tbody>
<tr><td>72</td><td>1200</td></tr>
<tr><td>71</td><td>300</td></tr>
<tr><td>72</td><td>300</td></tr>
<tr><td>74</td><td>600</td></tr>
<tr><td>69</td><td>1200</td></tr>
<tr><td>67</td><td>1200</td></tr>
<tr><td>65</td><td>1200</td></tr>
<tr><td>64</td><td>300</td></tr>
<tr><td>65</td><td>300</td></tr>
<tr><td>67</td><td>600</td></tr>
<tr><td>62</td><td>2400</td></tr>
<tr><td>64</td><td>1200</td></tr>
<tr><td>66</td><td>300</td></tr>
<tr><td>67</td><td>600</td></tr>
<tr><td>69</td><td>300</td></tr>
<tr><td>74</td><td>1200</td></tr>
<tr><td>67</td><td>1200</td></tr>
<tr><td>72</td><td>1200</td></tr>
<tr><td>72</td><td>300</td></tr>
<tr><td>71</td><td>600</td></tr>
<tr><td>69</td><td>300</td></tr>
<tr><td>67</td><td>2400</td></tr>
</tbody>
</table>
</body>
</html>
24行目から54行目の<table></table>
タグで囲まれた文書が表にまとめられた音の情報です。GETメソッドで取得したこのソースから、正規表現や文字列のメソッドを使って音の情報だけを取り出し、ブザーから再生するプログラムを次のヒントを踏まえて自分で作成してみましょう。
6. 1 HTMLで書かれたソースから必要な情報だけを取り出す方法
上のソースを詳しくみていくと、次のかたまりが1つの音を表していることが分かります。
<tr><td>72</td><td>1200</td></tr>
<tr></tr>
タグは表の1行を、<td></td>
タグは表の1セルをそれぞれ表しています。
そこで、次の正規表現と正規表現オブジェクトのsearch()
メソッドを用いてセル単位で検索を行います。
"<td>\\d+</td>"
search()
メソッドでマッチした文字列は、返り値のマッチオブジェクトのgroup()
メソッドで取得できます。この2つのメソッドと文字列オブジェクトのreplace()
メソッドを次のように組み合わせることで、元のソースからマッチする文字列を取り出すことができます。
【 組み合せの例 】
import ure # 正規表現のモジュール
regexobj = ure.compile("<td>\\d+</td>") # 正規表現オブジェクト
# HTMLのソースの例
source = """
<tr><td>72</td><td>1200</td></tr>
<tr><td>71</td><td>300</td></tr>
"""
# マッチする文字列を順番にターミナルに表示する
while True:
matchobj = regexobj.search(source) # マッチする文字列を検索
if matchobj:
_str = matchobj.group(0) # マッチした文字列を取得
# 次回の検索の対象外にするために元のソースからマッチした文字列を削除する
source = source.replace(_str, "", 1) # 1番目のみ置換
print(_str) # マッチした文字列をターミナルに表示
else:
break # マッチする文字列がなければループを終える
(実行結果)
<td>72</td> <td>1200</td> <td>71</td> <td>300</td>
以上をヒントにして、できるだけ自分で考えてプログラムを作成しましょう。
6. 2 サンプルプログラムの作成手順
Webページのリソースを取得するところまでの手順は、【 サンプルコード 5-2-1 】と同じです。以下のサンプルプログラムでは、play_melody()
と名前を付けた関数に処理をまとめています。
import urequests
from mywifi import MyWifi # 自作モジュールのインポート
SSID = "SSID" # 使用するWi-FiのSSID
PASSWORD = "PASSWORD" # 使用するWi-Fiのパスワード
wifi = MyWifi() # 独自クラスのオブジェクトを作成
url = "https://www.artec-kk.co.jp/school/cl/textbooks/sample/melody.html" # 情報の取得先のURL
def play_melody(): # Webページから取得した曲の情報をもとにブザーを制御する関数
if not wifi.isconnected(): # Wi-Fiに未接続の場合は接続を試みる
if not wifi.connect(SSID, PASSWORD):
return # 接続失敗の場合はここで処理を終える
response = urequests.get(url) # GETメソッドでリクエストを送る
if not response.status_code != "200": # リクエストに失敗した場合
print(response.status_code, response.reason) # ステータスコードどリーズンコードを表示
return # ここで処理を終える
上で確認した方法を応用して、取得したソースから音の情報だけを正規表現を利用して取り出します。ここで気を付けなければならないのは、「音の高さ」と「音の長さ」の2つの情報を区別しなければならないということです。
<tr><td>72</td><td>1200</td></tr>
左の<td>72</td>
が音の高さで、右の<td>1200</td>
が音の長さを表しています。
しかし、以下の正規表現で検索するだけでは、2つを区別できません。
"<td>\\d+</td>"
そこで、検索回数をカウントし、その回数が奇数の場合はマッチした文字列が音の高さを表していて、偶数の場合は音の長さを表しているということで区別してみます。そして、取り出した音の情報は順番にリストに格納します。
この方法を実践したプログラムが以下になります。
追加【2行目、9行目、19~35行目】
import urequests
import ure # 正規表現用のモジュール
from mywifi import MyWifi
SSID = "SSID"
PASSWORD = "PASSWORD"
wifi = MyWifi()
url = "https://www.artec-kk.co.jp/school/cl/textbooks/sample/melody.html"
regex_td = ure.compile("<td>\\d+</td>") # 対象の文字列を検索するための正規表現オブジェクトの作成
def play_melody():
if not wifi.isconnected():
if not wifi.connect(SSID, PASSWORD):
return
response = urequests.get(url)
if not response.status_code != "200":
print(response.status_code, response.reason)
return
source = response.text # ソースを取得
melody = [] # (音の高さ,音の長さ)のタプルを要素として格納するための曲のリスト
count = 0 # 検索回数のカウンタ
while True: # マッチする文字列がなくなるまで繰り返し検索を実行
count += 1 # カウンタを1増やす
matchobj = regex_td.search(source) # 検索の実行
if matchobj: # マッチする文字列があった場合
_str = matchobj.group(0) # マッチした文字列を取得
source = source.replace(_str, "", 1) # マッチした文字列を元のソースから削除して、次回の検索対象から外す
num = _str.replace("<td>", "").replace("</td>", "") # 数字だけを取り出す
if count % 2 == 1: # 検索回数が奇数の場合
tone = num # 数字が表すのは音の高さ(MIDI番号)
else: # 検索回数が偶数の場合
duration = int(num) # 数字が表すのは音の長さであるため文字列から整数に変換
melody.append((tone, duration)) # 曲のリストに音の情報を追加
else: # マッチする文字列がなかった場合
break # 検索のループを抜ける
ここまでで、Webページから音の情報を抽出してリストに格納できたので、最後にブザーから順番に鳴らすようにしてプログラムの完成です。
【 サンプルコード 6-2-1 】
追加【3行目、37・38行目】
import urequests
import ure
from pystubit.board import buzzer # ブザーの制御用オブジェクト
from mywifi import MyWifi
SSID = "SSID"
PASSWORD = "PASSWORD"
wifi = MyWifi()
url = "https://www.artec-kk.co.jp/school/cl/textbooks/sample/melody.html"
regex_td = ure.compile("<td>\\d+</td>")
def play_melody():
if not wifi.isconnected():
if not wifi.connect(SSID, PASSWORD):
return
response = urequests.get(url)
if not response.status_code != "200":
print(response.status_code, response.reason)
return
source = response.text
melody = []
count = 0
while True:
count += 1
matchobj = regex_td.search(source)
if matchobj:
_str = matchobj.group(0)
source = source.replace(_str, "", 1)
num = _str.replace("<td>", "").replace("</td>", "")
if count % 2 == 1:
tone = num
else:
duration = int(num)
melody.append((tone, duration))
else:
break
for note in melody: # リストから順番に音の情報を取り出す
buzzer.on(note[0], duration=note[1]) #ブザーからその音を鳴らす
完成したプログラムを実行して、関数名をターミナルから入力し、動作を確認しましょう。
(プログラムの実行例)
>>> play_melody()
チャプター7
おわりに
7. 1 このレッスンのまとめ
このレッスンでは、インターネットの仕組みとWebの歴史、それからWebブラウザが行っているHTTP通信について新たに学習しました。
また、MicroPythonのnetwork
モジュールを使って、Studuino:bitをWi-Fiへ接続する方法を確認し、その機能をまとめたモジュールを自作しました。さらに、インターネットにつながったStuduino:bitから、urequests
モジュールを用いてHTTPのGETメソッドでリクエストを送り、シンプルなWebページの情報を取得する方法も見てきました。
そしてレッスンの後半では、学習したことの応用として、インターネットから取得した情報からLEDディスプレイやブザーを制御するプログラムを作成しました。
このレッスンの内容は、次回以降のレッスンを学ぶ上での基礎になりますので、時間のあるときに改めて振り返りを行ってください。
7. 2 次のレッスンについて
次回のレッスンでは、HTTPの「POSTメソッド」の実践と「JSON」というデータ形式について新たに学習します。そして、世界気象機関のWebサイトで公開されている世界の各都市の気象予報データを取得するプログラムを作成します。