Tcl 4 Web: 用 http 获取网站内容
Tcl 自带有一个 http package 实现了 HTTP 客户端协议。可以用于访问网页,抓取数据等操作。
客户端的 HTTP
客户端的 HTTP 协议,可以简单理解为
- 向服务器发送一个请求
- 从服务器接收数据
同步 http::geturl
package require http
set token [http::geturl http://www.bing.com/]
# 同步请求,阻塞并等待数据返回
http::status $token ;# ok
http::code $token ;# HTTP/1.1 200 OK
http::ncode $token ;# 200
http::size $token ;# 119637
http::meta $token ;# header dict
http::data $token ;# body content
异步 http::geturl
proc http_done {token} {
upvar #0 $token state
puts $state(status)
puts $state(meta) ;# header
puts $state(body) ;# body
puts $state(type) ;# mime type from Content-Type
puts $state(charset) ;# charset from Content-Type
}
set token [ http::geturl http://www.bing.com/ -command http_done ]
# 异步请求。通过 -command 指定回调函数,或者通过 http::wait 等待数据返回
http::wait $token ;# 等待响应
GET and POST
# GET
http::geturl http://www.site.com/page?param=value¶m2=value2
# POST
http::geturl http://www.site.com/page -query param=value¶m2=value2
http::geturl http://www.site.com/page \
-query [::http::formatQuery param value param2 value2]
# HEAD
http::geturl http://www.site.com/page -validate 1
# PUT
http::geturl http://www.site.com/page -method PUT
数据的读取和保存
默认情况下,不管是请求的返回数据,还是POST的请求数据,都是通过变量直接传递的。
如果需要把数据写入文件——比如下载图片的时候,则可以借助 -channel
实现。
set fout [open image.png "wb"]
http::geturl http://www.site.com/image.png -channel $fout
close $fout
类似的,请求数据——比如上传文件,可以借助 -querychannel
实现。
set fp [open data.json "rb"]
http::geturl http://www.site.com/image.png \
-type text/json \
-querychannel $fp
close $fp
Track Progress
如果需要跟踪数据上传(POST)或者下载时的进度,可以指定每次读或写的数据块的大小,和响应的回调函数。
proc http_write_progress {token total current} {
upvar #0 $token state
}
proc http_read_progress {token total current} {
upvar #0 $token state
}
http::geturl http://www.site.com/page \
-querychannel $fp \
-queryblocksize 1024 -queryprogress http_write_progress \
-blocksize 1024 -progress http_read_progress
定制 Header
http::config -useragent "Chrome"
http::config -accept "text/json"
http::geturl http://www.site.com/page \
-headers [list \
Content-Type text/json \
X-Remote 1 \
]
用长连接 Keep-Alive 提高效率
HTTP/1.1 协议支持 Keep-Alive 连接以提高传输效率。
-keepalive
选项用于这个目的,默认是关闭的。
http::geturl http://www.site.com/page -keepalive 1
超时处理 timeout
涉及网络的东西,总有通讯失败的可能。通过 -timeout
选项指定时间。
http::geturl http://www.site.com/page -timeout 6000 ;# 单位是 ms
并行访问
同时发起多个请求——比如同时下载多个图片,可以减少总的数据获取时间。
下面是一个简单的并行访问的例子。实践中虽然代码会更复杂,但基本框架大致如此。
set http_jobs [dict create]
set http_all_done 0
proc http_done {token} {
upvar #0 $token state
puts [list $state(status) $state(body)]
dict unset ::http_jobs $token ;# 通过删除对应条目标记为完成
if {[dict size $http_jobs]==0} {
set ::http_all_done 1 ;# 触发全部完成的事件(Event)
}
}
foreach url $url_list {
set token [http::geturl $url -command http_done]
dict set ::http_jobs $token 1
}
vwait http_all_done
在这个基础上逐步扩展,可以写出一个简单的爬虫。