我创建了一个简单的脚本,用于实现谷歌的Chromecast墙纸图像作为我的Gnome桌面墙纸。

脚本从两个不同的源提取图像:

包含Chromecast图像集合的静态网页

一个动态的(javscript驱动的)网页,在谷歌设置的时间间隔内显示不同的墙纸。

我使用两种不同来源的原因是:

我已经有了一个只使用静态页面的工作脚本&我想挑战自己

当只使用动态页面时,加载该页面时的第一个图像总是相同的(或者来自一个非常小的子集,变化很小),我希望每次运行脚本时都能看到完全不同的墙纸。

我当前的代码如下:

#!/usr/bin/env python
"""This script runs in the background to download wallpapers used by Google's
chromecast devices and set them as a desktop wallpaper in Linux environments
using Gnome desktop. The wallpaper that is being set will change
automatically within a given interval."""

import io
import os
import subprocess
import time
from random import shuffle
from bs4 import BeautifulSoup
from PIL import Image
from pyvirtualdisplay import Display
import requests
from selenium import webdriver
from selenium.webdriver import DesiredCapabilities


def first_picture():
    """Retrieve a random image from a non-interactive website. This 
    will be the first image to which the Gnome desktop will be set. It 
    will (1) give the user visual feedback that the script is working 
    and (2) prevent the same image (or minor set of images) to be shown
    whenever the script is run."""

    request = requests.get('https://github.com/dconnolly/'\
                           'chromecast-backgrounds')
    content = request.content
    soup = BeautifulSoup(content, features="lxml")
    images = soup.find_all("img")
    sourcelist = []
    current_dir = os.path.dirname(os.path.realpath(__file__))

    for image in images:
        if "camo" in image["src"]:
            sourcelist.append(image["src"])

    # Implemented a try-except because pil_image sometimes raises an error:
    # raise IOError("cannot identify image file %r" % (filename if filename
    # else fp))
    # OSError: cannot identify image file <_io.BytesIO object at
    # 0x66d3bea6d3b0>

    try:

        shuffle(sourcelist)
        raw_image = requests.get(sourcelist[0])
        pil_image = Image.open(io.BytesIO(raw_image.content))

    except IOError:

        first_picture()

    temp_local_image_location = (current_dir + "/interactive_wallpaper."
                                 + pil_image.format)
    pil_image.save(temp_local_image_location)
    subprocess.Popen(["/usr/bin/gsettings", "set",
                      "org.gnome.desktop.background",
                      "picture-uri", "'" + temp_local_image_location + "'"],
                     stdout=subprocess.PIPE)
    time.sleep(40)


def change_desktop(image_source):
    """Tap into the dynamic webpage that is displaying a different
    wallpapers-like images at a given interval."""

    request = requests.get(image_source)
    image = Image.open(io.BytesIO(request.content))
    image_format = image.format
    current_dir = os.path.dirname(os.path.realpath(__file__))
    temp_local_image_location = (current_dir + "/interactive_wallpaper."
                                 + image_format)
    image.save(temp_local_image_location)
    subprocess.Popen(["/usr/bin/gsettings", "set",
                      "org.gnome.desktop.background",
                      "picture-uri", "'" + temp_local_image_location + "'"],
                     stdout=subprocess.PIPE)
    time.sleep(20)

def set_display_and_start_browser():
    """Start virtual display and selenium driver. This is needed
    for the selenium-initiated browser to grab the dynamically
    loaded images dfrom the webpage"""

    display = Display(visible=0, size=(1920, 1080))
    display.start()
    desired_capabilities = DesiredCapabilities.CHROME.copy()
    desired_capabilities['connection'] = "keep-alive"
    browser = webdriver.Chrome(desired_capabilities=desired_capabilities)
    url = "https://clients3.google.com/cast/chromecast/home/"
    browser.get(url)

    first_picture()

    while True:

        element = browser.find_element_by_id("picture-background")
        image_source = element.get_attribute("src")
        change_desktop(image_source)
        time.sleep(20)

任何提示、见解、评论、意见等,欢迎垂询!

想知道你在想什么。

最佳回复
重组和更正

我们先从 first_picture 功能问题。
我至少把它改名为 get_first_picture .

BeautifulSoup.find_all 允许使用正则表达式查找图像标记 src 满足某些条件的属性。因此,条件:

sourcelist = []
...
    if "camo" in image["src"]:
        ...

可以消除和过滤图像 images = soup.find_all("img", src=re.compile(r'.*camo.*')) .

这个 except 阻止:

except IOError:
    first_picture()

不提供任何"退出"条件。当控制流进入这个 except 阻塞并调用当前函数 first_picture() 同样-这并不意味着下面的所有后续语句(来自上一个上下文)都不会被执行。
您需要将该块之后的所有语句括起来 else: 块,使它们在成功打开或放置图像时执行 return 下面的语句 first_picture() 打电话来。

但是,应该事先注意的是 get_first_picture 以及 change_desktop 函数共享包含以下操作集的相同公共行为:

从远程资源中提取图像

保存图像

单独设置桌面背景 subprocess

时间延迟也有一个共同的量- 40 (在你的 set_display_and_start_browser 功能 time.sleep(20) + time.sleep(20) ,间接添加) 这无疑需要提取函数技术-公共/重复的行为被提取到一个单独的函数中,比如 set_desktop_background :

def set_desktop_background(image_source):
    """Tap into the dynamic webpage that is displaying a different
    wallpapers-like images at a given interval."""

    request = requests.get(image_source)
    image = Image.open(io.BytesIO(request.content))
    image_format = image.format
    current_dir = os.path.dirname(os.path.realpath(__file__))
    temp_local_image_location = f'{current_dir}/interactive_wallpaper.{image_format}'
    image.save(temp_local_image_location)
    subprocess.Popen(["/usr/bin/gsettings", "set",
                      "org.gnome.desktop.background",
                      "picture-uri", f"'{temp_local_image_location}'"],
                      stdout=subprocess.PIPE)
    time.sleep(40)

现在 change_desktop 为了 set_desktop_background 功能。

至于你提到的周期性错误 OSError: cannot identify image file ... -我相信这是可以单独修复的,并且与某些特定的图像类型或被截断的图像或无法保存图像有关;您可以在网上找到各种修复方法。

这个 get_first_picture 函数现在缩短为:

def get_first_picture():
    """Retrieve a random image from a non-interactive website. This 
    will be the first image to which the Gnome desktop will be set. It 
    will (1) give the user visual feedback that the script is working 
    and (2) prevent the same image (or minor set of images) to be shown
    whenever the script is run."""

    request = requests.get('https://github.com/dconnolly/chromecast-backgrounds')        
    soup = BeautifulSoup(request.content, features="lxml")
    images = soup.find_all("img", src=re.compile(r'.*camo.*'))
    sourcelist = [image["src"] for image in images]

    shuffle(sourcelist)
    set_desktop_background(sourcelist[0])

set_display_and_start_browser 对于函数命名来说是一个糟糕的模式,因为它指向了过度的责任(函数做得太多)。
最好将其拆分为单独的函数(考虑到以上所有优化):

start_display 功能:

def start_display():
    display = Display(visible=0, size=(1920, 1080))
    display.start()

start_browser 功能:

def start_browser():
    """Start selenium driver. This is needed
       for the selenium-initiated browser to grab the dynamically
       loaded images from the webpage.
    """
    desired_capabilities = DesiredCapabilities.CHROME.copy()
    desired_capabilities['connection'] = "keep-alive"
    browser = webdriver.Chrome(desired_capabilities=desired_capabilities)
    url = "https://clients3.google.com/cast/chromecast/home/"
    browser.get(url)
    return browser

load_images_to_desktop 职能(作为主要主管职能):

def load_images_to_desktop():
    start_display()
    browser = start_browser()
    get_first_picture()

    while True:
        element = browser.find_element_by_id("picture-background")
        image_source = element.get_attribute("src")
        set_desktop_background(image_source)