原來 Python 3 整合原本 Python 2 零散的用戶端與伺服器模組, 在標準函式庫中提供 http 與 urllib 模組. 其中 http 模組負責用戶端與伺服端的 HTTP 協定操作 (包括 cookie 管理); 而 urllib 模組負責請求 (urllib.request) 與回應 (urllib.response) 之處理, 以及 URL 的拆解.
可惜 MicroPython 沒有實作 http 模組, 但有實作 urllib 模組處理請求的部分, 不過模組名稱是 urllib.ureqest 而非 urllib.request (不會 fall-back).
>>> import urllib
>>> import http
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: no module named 'http'
雖然 urllib.ureqest 實作的功能不多, 但也足夠使用了, 相關文件可參考 CPython 的 urllib.request 模組說明 :
# https://docs.python.org/3.5/library/urllib.request.html#module-urllib.request
# HOWTO Fetch Internet Resources Using The urllib Package
本系列測試紀錄前十篇參考 :
# MicroPython on ESP8266 (二) : 數值型別測試
# MicroPython on ESP8266 (三) : 序列型別測試
# MicroPython on ESP8266 (四) : 字典與集合型別測試
# MicroPython on ESP8266 (五) : WiFi 連線與 WebREPL 測試
# MicroPython on ESP8266 (六) : 檔案系統測試
# MicroPython on ESP8266 (七) : 時間日期測試
# MicroPython on ESP8266 (八) : GPIO 測試
# MicroPython on ESP8266 (九) : PIR 紅外線移動偵測
# MicroPython on ESP8266 (十) : socket 模組測試
MicroPython 文件參考 :
# MicroPython tutorial for ESP8266 (官方教學)
# http://docs.micropython.org/en/latest/micropython-esp8266.pdf
# http://docs.micropython.org/en/latest/pyboard/library/usocket.html#class-socket
# http://docs.micropython.org/en/v1.8.7/esp8266/library/usocket.html#module-usocket
# https://gist.github.com/xyb/9a4c8d7fba92e6e3761a (驅動程式)
使用 urllib.urequest 模組要先匯入 :
import urllib.urequest
或者取個別名以減少輸入時間 :
import urllib.urequest as u
這樣就可以呼叫模組的方法 urlopen() 來開啟遠端 URL 資源, 其 API 如下 :
urllib.urequest.urlopen(url[, data][, timeout])
其中可有可無的第二參數 data 是要傳送給伺服器的資料字串, 如果有傳送 data 的話就用 POST 方法提出請求; 否則就是用 GET 方法. urlopen() 方法會傳回一個 socket 物件, 這樣就可以用 socket() 物件的 read() 方法來讀取 HTTP 回應內容, 例如讀取 MicroPython 測試網頁 :
>>> import urllib.urequest as u
>>> s=u.urlopen('http://micropython.org/ks/test.html') #以 GET 方法提出請求
>>> type(s)
<class 'socket'> #傳回值是 socket 物件
>>> s
<socket state=3 timeout=-1 incoming=3fffa058 off=0>
>>> s.status #沒有實作 HTTP 回應狀態碼
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'socket' object has no attribute 'status'
>>> s.getheader('Content-Type') #沒有實作 getheader()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'socket' object has no attribute 'getheader'
>>> s.getheaders() #沒有實作 getheaders()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'socket' object has no attribute 'getheaders'
>>> r=s.read() #讀取 TCP 串流緩衝區 (傳回 bytes 型態)
>>> s<socket state=3 timeout=-1 incoming=0 off=0> #讀取過後 incoming 歸零
>>> type(r)
<class 'bytes'> #傳回 bytes 型態
>>> r #二進位 (byte) 資料
b'<!DOCTYPE html>\n<html lang="en">\n <head>\n <title>Test</title>\n </head>\n <body>\n <h1>Test</h1>\n It\'s working if you can read this!\n </body>\n</html>\n'
>>> r=s.read() #讀過之後緩衝區已清空, 再次 read() 無內容
>>> r
b''
上面 socket.read() 傳回的 bytes 串流可以用 decode('utf-8') 方法轉成 utf-8 編碼的字串, 然後再用字串的 split() 方法以 '\n' 為界拆分字串為串列型態, 這樣就可以用 for 迴圈印出 HTML 格式了 :
>>> import urllib.urequest as u
>>> s=u.urlopen('http://micropython.org/ks/test.html')
>>> r=s.read()
>>> r.decode("utf-8")
'<!DOCTYPE html>\n<html lang="en">\n <head>\n <title>Test</title>\n </head>\n <body>\n <h1>Test</h1>\n It\'s working if you can read this!\n </body>\n</html>\n'
>>> html=r.decode('utf-8').split('\n')
>>> html
['<!DOCTYPE html>', '<html lang="en">', ' <head>', ' <title>Test</title>', ' </head>', ' <body>', ' <h1>Test</h1>', " It's working if you can read this!", ' </body>', '</html>', '']
>>> for line in html:
... print(line)
...
...
...
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test</title>
</head>
<body>
<h1>Test</h1>
It's working if you can read this!
</body>
</html>
>>>
以上是 urllib.urequest 模組處理 GET 請求的方法, 不過由於 MicroPython 測試網頁只是單純的靜態網頁, 無法測試帶參數的 GET 請求. 測試含有參數的 GET 可以利用 httpbin.org 的主機所提供的服務, 其 GET 測試網址為 :
# http://httpbin.org/get
此網頁會回應客戶端所傳送的參數 (args) 與請求標頭資訊, 如果在瀏覽器輸入如下網址傳遞 a=1 與 b=2 參數, 伺服器回應如下 :
# http://httpbin.org/get?a=1&b=2
{ "args": { "a": "1", "b": "2" }, "headers": { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Encoding": "gzip, deflate, sdch", "Accept-Language": "zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4", "Cache-Control": "max-stale=0", "Connection": "close", "Cookie": "_gauges_unique_day=1; _gauges_unique_month=1; _gauges_unique_year=1; _gauges_unique=1", "Host": "httpbin.org", "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 OPR/45.0.2552.888", "X-Bluecoat-Via": "e850b88f17ae9572" }, "origin": "210.88.213.252", "url": "http://httpbin.org/get?a=1&b=2" }
而在 MicroPython 輸入如下指令 :
import urllib.urequest as u
url='http://httpbin.org/get?a=1&b=2'
s=u.urlopen(url)
r=s.read()
lines=r.decode('utf-8').split('\n')
for line in lines:
print(line)
結果如下 :
MicroPython v1.9.1-8-g7213e78d on 2017-06-12; ESP module with ESP8266
Type "help()" for more information.
>>> import urllib.urequest as u
>>> url='http://httpbin.org/get?a=1&b=2'
>>> s=u.urlopen(url)
>>> r=s.read()
>>> r
b'{\n "args": {\n "a": "1", \n "b": "2"\n }, \n "headers": {\n "Connection": "close", \n "Host": "httpbin.org"\n }, \n "origin": "223.138.76.144", \n "url": "http://httpbin.org/get?a=1&b=2"\n}\n'
>>> lines=r.decode('utf-8').split('\n') #格式化
>>> for line in lines:
... print(line)
...
...
...
{
"args": {
"a": "1",
"b": "2"
},
"headers": {
"Connection": "close",
"Host": "httpbin.org"
},
"origin": "223.138.76.144",
"url": "http://httpbin.org/get?a=1&b=2"
}
>>>
可見伺服器確實有收到所傳遞之參數.
測試 POST 請求可以用 httpbin.org 主機的 POST 測試網址 :
# http://httpbin.org/post
在 MicroPython 輸入下列指令, 當 urlopen() 含有第二參數時, 將會以 POST 方法提出請求. 注意, 這裡傳遞的參數須以 dict 形式的字串呈現, 不可直接用 dict 物件, 否則會產生 "TypeError: object with buffer protocol required" 的錯誤訊息.
import urllib.urequest as u
url='http://httpbin.org/post'
data="{'a':'1','b':'2'}"
s=u.urlopen(url,data)
r=s.read()
lines=r.decode('utf-8').split('\n')
for line in lines:
print(line)
結果如下 :
MicroPython v1.9.1-8-g7213e78d on 2017-06-12; ESP module with ESP8266
Type "help()" for more information.
>>> import urllib.urequest as u
>>> url='http://httpbin.org/post'
>>> data="{'a':'1','b':'2'}" #必須是字串
>>> s=u.urlopen(url,data)
>>> r=s.read()
>>> lines=r.decode('utf-8').split('\n')
>>> for line in lines:
... print(line)
...
...
...
{
"args": {},
"data": "{'a':'1','b':'2'}",
"files": {},
"form": {},
"headers": {
"Connection": "close",
"Content-Length": "17",
"Host": "httpbin.org"
},
"json": null,
"origin": "223.198.79.144",
"url": "http://httpbin.org/post"
}
可見所傳遞的參數是放在 data 屬性中, 而記錄 GET 參數的 args 為空.
參考 :
# Henry's HTTP Post Dumping Server
# http://posttestserver.com/post.php
# Testing multipart/form-data uploads with Post Test Server
# MICROPYTHON ON THE ESP8266: KICKING THE TIRES (socket 部分)
2 則留言 :
>>> upip.install('urllib')
Installing to: /lib/
Warning: pypi.org SSL certificate is not validated
Error installing 'urllib': Package not found, packages may be partially installed
您好!我在ESP8266上安装urllib库时提示如上错误信息,请问您遇到过吗?
MicroPython 已將 urllib 納入標準函式庫, 直接 import 即可, 不須 upip.
張貼留言