今天想给wayback machine bot增加一个截图的功能,难度不大,用selenium打开浏览器,截图,再发给用户就可以了。
网上随便就可以找到教程,比如说这样
package main import ( "fmt" log "github.com/sirupsen/logrus" "github.com/tebeka/selenium" "io/ioutil" ) const ( chromeDriverPath = "/usr/local/bin/chromedriver" port = 9515 ) func main() { var opts []selenium.ServiceOption selenium.SetDebug(false) service, err := selenium.NewChromeDriverService(chromeDriverPath, port, opts...) if err != nil { panic(err) // panic is used only as an example and is not otherwise recommended. } defer service.Stop() caps := selenium.Capabilities{"browserName": "chrome"} wd, err := selenium.NewRemote(caps, fmt.Sprintf("http://localhost:%d/wd/hub", port)) if err != nil { panic(err) } defer wd.Quit() if err := wd.Get("https://github.com/tgbot-collection/archiver"); err != nil { panic(err) } screenshot, err := wd.Screenshot() if err != nil { log.Errorln(err) } ioutil.WriteFile("screenshot.png", screenshot, 0644) }
但是问题来了,这里的截屏只有当前窗口,怎么把整个页面都截屏了呢?
整页截屏
方法还是有的,一个简单的办法就是获取到网页的宽高,然后把浏览器的窗口设置成这么大,然后再截图就好了!需要注意的是只有headless模式可以任意设置窗口大小,否则最大高度不能超过你的显示器分辨率
设置headless只需要加个参数就好了,如下
caps := selenium.Capabilities{"browserName": "chrome"} chromeCaps := chrome.Capabilities{ Path: "", Args: []string{ "--headless", }, } caps.AddChrome(chromeCaps) wd, err := selenium.NewRemote(caps, fmt.Sprintf("http://localhost:%d/wd/hub", port))
获取网课宽高很容易,偷懒的话只要获取网页的高就可以了,宽我们可以固定成固定的像素,比如1920.
在chrome的控制台里,输入这么一串神秘代码可获得网页的高
document.body.parentNode.scrollHeight
在selenium中使用ExecuteScript
就可以执行JavaScript代码,需要注意返回值是interface,所以要类型断言成float,然后再转成int
height, _ := wd.ExecuteScript("return document.body.parentNode.scrollHeight", nil) var realHeight = int(height.(float64))
然后我们需要设置窗口大小
wd.ResizeWindow("", 1920, realHeight)
运行一下看看,果然正确且完美
在服务器中运行
必然这个东西是要在某台服务器中跑起来的,也没什么不同的,下载自己的webdriver,安装浏览器,照样headless启动就好了。应该不会遇到大问题
在容器中运行
理论上来说,在容器中运行和在某个Linux发行版运行是没什么太大差别的。无非就是找到对应的webdriver。比如我经常用的alpine恰巧提供了chrome和chrome-webdriver,并且有amd64和arm64(aarch64)的预编译包
直接运行 apk add chromium-chromedriver
浏览器和webdriver会一起安装好
要注意一件事情,如果你是root用户运行的,那么要加上--no-sandbox
参数
一运行却发现结果不如人意,总是写入0大小的文件,或者报错
强行加一行sleep,会有比较大的概率会避免这个问题,但是这不是个办法嘛。
if err := wd.Get("https://www.baidu.com"); err != nil { panic(err) } time.Sleep(10 * time.Second)
后来搜了一下,是Chrome和 container的兼容性问题,Chrome需要比较大的/dev/shm
,然而容器中的/dev/shm
往往都比较小,对于Chrome这种内大户来说不太行。
解决方法要么就增大容器里的/dev/shm
,可以通过run时额外参数--shm-size=256m
也可以直接把宿主机的共享进去
docker run --rm -v /dev/shm:/dev/shm -it alpine sh
也可以禁用Chrome的shm功能,加一个参数
"--disable-dev-shm-usage",
还有一种办法,换Firefox也许也有救。
中文乱码
alpine里没带中文字体,所以中文会乱码。可以用alpine自带的一个,这样安装就好,也可以自己偷字体,然后放到/usr/share/fonts/
就好了
apk add wqy-zenhei --update-cache --repository https://nl.alpinelinux.org/alpine/edge/testing
参考资料
https://stackoverflow.com/a/53970825/10264400
https://github.com/tgbot-collection/archiver/blob/master/screenshot.go