Take screenshot in Python on Mac OS X

2019-01-23 08:50发布

问题:

ImageGrab from PIL would have been ideal. I'm looking for similar functionality, specifically the ability to define the screenshot's bounding box. I've been looking for a library to do so on Mac OS X but haven't had any luck. I also wasn't able to find any sample code to do it (maybe pyobjc?).

回答1:

While not exactly what you want, in a pinch you might just use:

os.system("screencapture screen.png")

Then open that image with the Image module. I'm sure a better solution exists though.



回答2:

Here's how to capture and save a screenshot with PyObjC, based on my answer here

You can capture the entire screen, or specify a region to capture. If you don't need to do that, I'd recommend just calling the screencapture command (more features, more robust, and quicker - the initial PyObjC import alone can take around a second)

import Quartz
import LaunchServices
from Cocoa import NSURL
import Quartz.CoreGraphics as CG


def screenshot(path, region = None):
    """region should be a CGRect, something like:

    >>> import Quartz.CoreGraphics as CG
    >>> region = CG.CGRectMake(0, 0, 100, 100)
    >>> sp = ScreenPixel()
    >>> sp.capture(region=region)

    The default region is CG.CGRectInfinite (captures the full screen)
    """

    if region is None:
        region = CG.CGRectInfinite

    # Create screenshot as CGImage
    image = CG.CGWindowListCreateImage(
        region,
        CG.kCGWindowListOptionOnScreenOnly,
        CG.kCGNullWindowID,
        CG.kCGWindowImageDefault)

    dpi = 72 # FIXME: Should query this from somewhere, e.g for retina displays

    url = NSURL.fileURLWithPath_(path)

    dest = Quartz.CGImageDestinationCreateWithURL(
        url,
        LaunchServices.kUTTypePNG, # file type
        1, # 1 image in file
        None
        )

    properties = {
        Quartz.kCGImagePropertyDPIWidth: dpi,
        Quartz.kCGImagePropertyDPIHeight: dpi,
        }

    # Add the image to the destination, characterizing the image with
    # the properties dictionary.
    Quartz.CGImageDestinationAddImage(dest, image, properties)

    # When all the images (only 1 in this example) are added to the destination, 
    # finalize the CGImageDestination object. 
    Quartz.CGImageDestinationFinalize(dest)


if __name__ == '__main__':
    # Capture full screen
    screenshot("/tmp/testscreenshot_full.png")

    # Capture region (100x100 box from top-left)
    region = CG.CGRectMake(0, 0, 100, 100)
    screenshot("/tmp/testscreenshot_partial.png", region=region)


回答3:

While I do understand that this thread is close to five years old now, I'm answering this in the hope that it helps people in future.

Here's what worked for me, based on an answer in this thread (credit goes to ponty ) : Take a screenshot via a python script. [Linux]

https://github.com/ponty/pyscreenshot

Install:

easy_install pyscreenshot

Example:

import pyscreenshot

# fullscreen
screenshot=pyscreenshot.grab()
screenshot.show()

# part of the screen
screenshot=pyscreenshot.grab(bbox=(10,10,500,500))
screenshot.show()

# save to file
pyscreenshot.grab_to_file('screenshot.png')


回答4:

Pillow has since added ImageGrab support for macOS!

However it's not in v2.9 (as of right now the latest) so I just added this file to my local module.

The code is as below:

#
# The Python Imaging Library
# $Id$
#
# screen grabber (macOS and Windows only)
#
# History:
# 2001-04-26 fl  created
# 2001-09-17 fl  use builtin driver, if present
# 2002-11-19 fl  added grabclipboard support
#
# Copyright (c) 2001-2002 by Secret Labs AB
# Copyright (c) 2001-2002 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#

from . import Image

import sys
if sys.platform not in ["win32", "darwin"]:
    raise ImportError("ImageGrab is macOS and Windows only")

if sys.platform == "win32":
    grabber = Image.core.grabscreen
elif sys.platform == "darwin":
    import os
    import tempfile
    import subprocess


def grab(bbox=None):
    if sys.platform == "darwin":
        fh, filepath = tempfile.mkstemp('.png')
        os.close(fh)
        subprocess.call(['screencapture', '-x', filepath])
        im = Image.open(filepath)
        im.load()
        os.unlink(filepath)
    else:
        size, data = grabber()
        im = Image.frombytes(
            "RGB", size, data,
            # RGB, 32-bit line padding, origin lower left corner
            "raw", "BGR", (size[0]*3 + 3) & -4, -1
            )
    if bbox:
        im = im.crop(bbox)
    return im


def grabclipboard():
    if sys.platform == "darwin":
        fh, filepath = tempfile.mkstemp('.jpg')
        os.close(fh)
        commands = [
            "set theFile to (open for access POSIX file \""+filepath+"\" with write permission)",
            "try",
                "write (the clipboard as JPEG picture) to theFile",
            "end try",
            "close access theFile"
        ]
        script = ["osascript"]
        for command in commands:
            script += ["-e", command]
        subprocess.call(script)

        im = None
        if os.stat(filepath).st_size != 0:
            im = Image.open(filepath)
            im.load()
        os.unlink(filepath)
        return im
    else:
        debug = 0  # temporary interface
        data = Image.core.grabclipboard(debug)
        if isinstance(data, bytes):
            from . import BmpImagePlugin
            import io
            return BmpImagePlugin.DibImageFile(io.BytesIO(data))
        return data


回答5:

I found that using webkit2png was the most convenient solution for me on OS X.

brew install webkit2png
webkit2png http://stackoverflow.com


回答6:

from subprocess import call
import time
from time import gmtime, strftime

# Take screenshot every 10 seconds and store in the folder where the 
# code file is present on disk. To stop the script press Cmd+Z/C
def take_screen_shot():
    # save screen shots where
    call(["screencapture", "Screenshot" + strftime("%Y-%m-%d %H:%M:%S", gmtime()) + ".jpg"])



def build_screen_shot_base():
    while True:
        take_screen_shot()
        time.sleep(10)


build_screen_shot_base()