使用外部服务对网站进行屏幕截图

之前的配方使用 selenium、webdriver 和 PhantomJS 来创建屏幕截图。 这显然需要安装这些软件包。 如果您不想安装这些软件但仍想制作网站屏幕截图,那么您可以使用多种可以进行屏幕截图的网络服务之一。 在本教程中,我们将使用 www.screenshotapi.io 上的服务来创建屏幕截图。

准备工作

首先,前往 www.screenshotapi.io 并注册一个免费帐户:

image 2024 01 29 15 07 49 816

创建帐户后,继续获取 API 密钥。 这将需要针对他们的服务进行身份验证:

image 2024 01 29 15 08 11 881

怎么做

此示例的脚本是 04/09_screenshotapi.py。 运行一下,它会生成屏幕截图。 代码如下,在结构上与前面的菜谱非常相似:

from core.website_screenshot_with_screenshotapi import WebsiteScreenshotGenerator
from core.file_blob_writer import FileBlobWriter
from os.path import expanduser

# get the screenshot
image_bytes = WebsiteScreenshotGenerator("bd17a1e1-db43-4686-9f9b-b72b67a5535e")\
    .capture("http://espn.go.com", 500, 500).image_bytes

# save it to a file
FileBlobWriter(expanduser("~")).write("website_screenshot.png", image_bytes)

与上一配方的功能差异在于我们使用了不同的 WebsiteScreenshotGenerator 实现。 这个来自 core.website_screenshot_with_screenshotapi 模块。

运行时,控制台将输出以下内容:

Sending request: http://espn.go.com
{"status":"ready","key":"2e9a40b86c95f50ad3f70613798828a8","apiCreditsCost"
:1}
The image key is: 2e9a40b86c95f50ad3f70613798828a8
Trying to retrieve: https://api.screenshotapi.io/retrieve
Downloading image:
https://screenshotapi.s3.amazonaws.com/captures/2e9a40b86c95f50ad3f70613798
828a8.png
Saving screenshot to:
downloaded_screenshot.png2e9a40b86c95f50ad3f70613798828a8
Cropped the image to: 500 500
Attempting to write 209197 bytes to website_screenshot.png:
The write was successful

并为我们提供了以下图像:

image 2024 01 29 15 10 10 184

工作原理

以下是这个 WebsiteScreenshotGenerator 的代码:

class WebsiteScreenshotGenerator:
    def __init__(self, apikey):
        self._screenshot = None
        self._apikey = apikey

    def capture(self, url, width, height, crop=True):
        key = self.beginCapture(url, "{0}x{1}".format(width, height),
                                "true", "firefox", "true")

        print("The image key is: " + key)

        timeout = 30
        tCounter = 0
        tCountIncr = 3

        while True:
            result = self.tryRetrieve(key)
            if result["success"]:
                print("Saving screenshot to: downloaded_screenshot.png" + key)
                bytes = result["bytes"]
                self._screenshot = Image.open(io.BytesIO(bytes))

                if crop:
                    # crop the image
                    self._screenshot = self._screenshot.crop((0, 0, width, height))
                    print("Cropped the image to: {0} {1}".format(width, height))
                break

            tCounter += tCountIncr
            print("Screenshot not yet ready.. waiting for: " + str(tCountIncr) + " seconds.")
            time.sleep(tCountIncr)
            if tCounter > timeout:
                print("Timed out while downloading: " + key)
                break
        return self

    def beginCapture(self, url, viewport, fullpage, webdriver, javascript):
        serverUrl = "https://api.screenshotapi.io/capture"
        print('Sending request: ' + url)
        headers = {'apikey': self._apikey}
        params = {'url': urllib.parse.unquote(url).encode('utf8'),
              'viewport': viewport, 'fullpage': fullpage,
              'webdriver': webdriver, 'javascript': javascript}
        result = requests.post(serverUrl, data=params, headers=headers)
        print(result.text)
        json_results = json.loads(result.text)
        return json_results['key']

    def tryRetrieve(self, key):
        url = 'https://api.screenshotapi.io/retrieve'
        headers = {'apikey': self._apikey}
        params = {'key': key}
        print('Trying to retrieve: ' + url)
        result = requests.get(url, params=params, headers=headers)

        json_results = json.loads(result.text)
        if json_results["status"] == "ready":
            print('Downloading image: ' + json_results["imageUrl"])
            image_result = requests.get(json_results["imageUrl"])
            return {'success': True, 'bytes': image_result.content}
        else:
            return {'success': False}

    @property
    def image(self):
        return self._screenshot

    @property
    def image_bytes(self):
        bytesio = io.BytesIO()
        self._screenshot.save(bytesio, "PNG")
        bytesio.seek(0)
        return bytesio.getvalue()

Screenshotapi.io API 是一个 REST API。 有两个不同的端点:

第一个端点被调用并将 URL 和其他参数传递给其服务。 成功执行后,此 API 将返回一个密钥,可在另一个端点上使用该密钥来检索图像。 屏幕截图是异步执行的,我们需要使用从捕获端点返回的密钥不断调用检索 API。 屏幕截图完成后,此端点将返回就绪状态值。 该代码只是循环,直到设置、发生错误或代码超时。

当快照可用时,API 在检索响应中返回图像的 URL。 然后,代码检索该图像并根据接收到的数据构造一个 Pillow Image 对象。

还有更多

Screenshotapi.io API 有许多有用的参数。 其中一些允许您调整要使用的浏览器引擎(Firefox、Chrome 或 PhantomJS)、设备模拟以及是否在网页中执行 JavaScript。 有关这些选项和 API 的更多详细信息,请访问 http://docs.screenshotapi.io/rest-api/