跳转到主要内容

PyiCloud是一个模块,允许Python开发者与iCloud网络服务交互。

项目描述

Check out our test status at https://travis-ci.org/picklepete/pyicloud Library version Supported versions Downloads Requirements Status Formated with Black Join the chat at https://gitter.im/picklepete/pyicloud

PyiCloud是一个模块,允许Python开发者与iCloud网络服务交互。它由出色的requests HTTP库提供支持。

在其核心,PyiCloud使用您的用户名和密码连接到iCloud,然后通过它们的API执行日历和iPhone查询。

身份验证

不使用已保存密码的身份验证非常简单,只需将您的用户名和密码传递给PyiCloudService类即可

from pyicloud import PyiCloudService
api = PyiCloudService('jappleseed@apple.com', 'password')

如果用户名/密码组合无效,将抛出PyiCloudFailedLoginException异常。

您还可以使用命令行工具将您的密码存储在系统密钥链中

$ icloud --username=jappleseed@apple.com
ICloud Password for jappleseed@apple.com:
Save password in keyring? (y/N)

如果您已在密钥链中存储了密码,则在与命令行工具交互或为存储密码的用户实例化PyiCloudService类时,您不需要提供密码。

api = PyiCloudService('jappleseed@apple.com')

如果您想从系统密钥链中删除存储的密码,可以使用--delete-from-keyring命令行选项清除存储的密码

$ icloud --username=jappleseed@apple.com --delete-from-keyring

注意:身份验证将在苹果设定的间隔后过期,届时您将需要重新进行身份验证。此间隔目前为两个月。

两步验证和双因素身份验证(2SA/2FA)

如果您已为账户启用了双因素身份验证(2FA)或两步身份验证(2SA),您将需要进行一些额外的工作

if api.requires_2fa:
    print("Two-factor authentication required.")
    code = input("Enter the code you received of one of your approved devices: ")
    result = api.validate_2fa_code(code)
    print("Code validation result: %s" % result)

    if not result:
        print("Failed to verify security code")
        sys.exit(1)

    if not api.is_trusted_session:
        print("Session is not trusted. Requesting trust...")
        result = api.trust_session()
        print("Session trust result %s" % result)

        if not result:
            print("Failed to request trust. You will likely be prompted for the code again in the coming weeks")
elif api.requires_2sa:
    import click
    print("Two-step authentication required. Your trusted devices are:")

    devices = api.trusted_devices
    for i, device in enumerate(devices):
        print(
            "  %s: %s" % (i, device.get('deviceName',
            "SMS to %s" % device.get('phoneNumber')))
        )

    device = click.prompt('Which device would you like to use?', default=0)
    device = devices[device]
    if not api.send_verification_code(device):
        print("Failed to send verification code")
        sys.exit(1)

    code = click.prompt('Please enter validation code')
    if not api.validate_verification_code(device, code):
        print("Failed to verify verification code")
        sys.exit(1)

设备

您可以使用devices属性列出与您的账户关联的设备

>>> api.devices
{
'i9vbKRGIcLYqJnXMd1b257kUWnoyEBcEh6yM+IfmiMLh7BmOpALS+w==': <AppleDevice(iPhone 4S: Johnny Appleseed's iPhone)>,
'reGYDh9XwqNWTGIhNBuEwP1ds0F/Lg5t/fxNbI4V939hhXawByErk+HYVNSUzmWV': <AppleDevice(MacBook Air 11": Johnny Appleseed's MacBook Air)>
}

并且您可以访问单个设备,无论是通过其索引还是其ID

>>> api.devices[0]
<AppleDevice(iPhone 4S: Johnny Appleseed's iPhone)>
>>> api.devices['i9vbKRGIcLYqJnXMd1b257kUWnoyEBcEh6yM+IfmiMLh7BmOpALS+w==']
<AppleDevice(iPhone 4S: Johnny Appleseed's iPhone)>

或者,如果您只有一个关联的苹果设备,您可以直接使用iphone属性来访问与您的账户关联的第一个设备

>>> api.iphone
<AppleDevice(iPhone 4S: Johnny Appleseed's iPhone)>

注意:与您的账户关联的第一个设备不一定是您的iPhone。

查找我的iPhone

成功进行身份验证后,您就可以开始查询您的数据了!

位置

返回设备最后已知的位置。必须已安装并初始化查找我的iPhone应用。

>>> api.iphone.location()
{'timeStamp': 1357753796553, 'locationFinished': True, 'longitude': -0.14189, 'positionType': 'GPS', 'locationType': None, 'latitude': 51.501364, 'isOld': False, 'horizontalAccuracy': 5.0}

状态

查找我的iPhone响应相当庞大,因此出于简便起见,此方法将返回属性子集。

>>> api.iphone.status()
{'deviceDisplayName': 'iPhone 5', 'deviceStatus': '200', 'batteryLevel': 0.6166913, 'name': "Peter's iPhone"}

如果您希望请求更多属性,您可以传递一个属性名称列表。

播放声音

向设备发送请求以播放声音,如果您想传递自定义消息,可以通过更改主题arg来实现。

api.iphone.play_sound()

几分钟后,设备将播放铃声,显示默认通知(“查找我的iPhone警报”)并向您发送确认电子邮件。

丢失模式

丢失模式与“播放声音”功能略有不同,因为它允许拾取电话的人在不输入密码的情况下拨打特定的电话号码。就像“播放声音”一样,您可以传递自定义消息,设备将显示该消息,如果未覆盖,将使用默认的自定义消息“此iPhone已丢失。请给我打电话。”。

phone_number = '555-373-383'
message = 'Thief! Return my phone immediately.'
api.iphone.lost_device(phone_number, message)

日历

当前的日历网络服务仅支持获取事件。

事件

返回本月的事件

api.calendar.events()

或者,在特定的日期范围内

from_dt = datetime(2012, 1, 1)
to_dt = datetime(2012, 1, 31)
api.calendar.events(from_dt, to_dt)

或者,您可以这样获取单个事件的详细信息

api.calendar.get_event_detail('CALENDAR', 'EVENT_ID')

联系人

您可以通过 contacts 属性访问您的iCloud联系人/通讯录

>>> for c in api.contacts.all():
>>> print(c.get('firstName'), c.get('phones'))
John [{'field': '+1 555-55-5555-5', 'label': 'MOBILE'}]

注意:这些联系人不包括例如从Facebook等处联合的联系人,只包括存储在iCloud中的联系人。

文件存储(Ubiquity)

您可以通过 files 属性的 dir 方法访问您iCloud账户中存储的文档

>>> api.files.dir()
['.do-not-delete',
 '.localized',
 'com~apple~Notes',
 'com~apple~Preview',
 'com~apple~mail',
 'com~apple~shoebox',
 'com~apple~system~spotlight'
]

您可以使用文件名作为索引来访问子项及其子项的子项

>>> api.files['com~apple~Notes']
<Folder: 'com~apple~Notes'>
>>> api.files['com~apple~Notes'].type
'folder'
>>> api.files['com~apple~Notes'].dir()
['Documents']
>>> api.files['com~apple~Notes']['Documents'].dir()
['Some Document']
>>> api.files['com~apple~Notes']['Documents']['Some Document'].name
'Some Document'
>>> api.files['com~apple~Notes']['Documents']['Some Document'].modified
datetime.datetime(2012, 9, 13, 2, 26, 17)
>>> api.files['com~apple~Notes']['Documents']['Some Document'].size
1308134
>>> api.files['com~apple~Notes']['Documents']['Some Document'].type
'file'

当您需要下载文件时,open 方法将返回一个响应对象,您可以从该对象中读取 content

>>> api.files['com~apple~Notes']['Documents']['Some Document'].open().content
'Hello, these are the file contents'

注意:上面 open 方法返回的对象是一个 response object,而 open 方法可以接受您在请求中使用 requests 的任何参数。

例如,如果您知道您要打开的文件具有JSON内容

>>> api.files['com~apple~Notes']['Documents']['information.json'].open().json()
{'How much we love you': 'lots'}
>>> api.files['com~apple~Notes']['Documents']['information.json'].open().json()['How much we love you']
'lots'

或者,如果您正在下载一个非常大的文件,您可能想使用 stream 关键字参数,并直接从原始响应对象中读取

>>> download = api.files['com~apple~Notes']['Documents']['big_file.zip'].open(stream=True)
>>> with open('downloaded_file.zip', 'wb') as opened_file:
        opened_file.write(download.raw.read())

文件存储(iCloud Drive)

您可以通过与上一节中描述的Ubiquity API相同的API访问您的iCloud Drive,但它的根目录在 `api.drive`

>>> api.drive.dir()
['Holiday Photos', 'Work Files']
>>> api.drive['Holiday Photos']['2013']['Sicily'].dir()
['DSC08116.JPG', 'DSC08117.JPG']

>>> drive_file = api.drive['Holiday Photos']['2013']['Sicily']['DSC08116.JPG']
>>> drive_file.name
'DSC08116.JPG'
>>> drive_file.date_modified
datetime.datetime(2013, 3, 21, 12, 28, 12) # NB this is UTC
>>> drive_file.size
2021698
>>> drive_file.type
'file'

open 方法将返回一个响应对象,您可以从该对象中读取文件的内容

from shutil import copyfileobj
with drive_file.open(stream=True) as response:
    with open(drive_file.name, 'wb') as file_out:
        copyfileobj(response.raw, file_out)

要交互文件和目录,对于文件或文件夹,有 mkdirrenamedelete 函数可用

api.drive['Holiday Photos'].mkdir('2020')
api.drive['Holiday Photos']['2020'].rename('2020_copy')
api.drive['Holiday Photos']['2020_copy'].delete()

可以使用 upload 方法将文件对象发送到iCloud Drive

with open('Vacation.jpeg', 'rb') as file_in:
    api.drive['Holiday Photos'].upload(file_in)

强烈建议将文件句柄以二进制形式而不是文本形式打开,以防止后续的解码错误。

照片库

您可以通过 photos 属性访问iCloud照片库。

>>> api.photos.all
<PhotoAlbum: 'All Photos'>

单个相册可通过 albums 属性访问

>>> api.photos.albums['Screenshots']
<PhotoAlbum: 'Screenshots'>

您可以通过迭代访问照片资产。‘所有照片’相册按 added_date 排序,因此最近添加的照片将首先返回。所有其他相册按 asset_date(代表exif日期)排序

>>> for photo in api.photos.albums['Screenshots']:
        print(photo, photo.filename)
<PhotoAsset: id=AVbLPCGkp798nTb9KZozCXtO7jds> IMG_6045.JPG

要下载照片,请使用 download 方法,它将返回一个 response object,该对象已将 stream 设置为 True,因此您可以从中读取原始响应对象

photo = next(iter(api.photos.albums['Screenshots']), None)
download = photo.download()
with open(photo.filename, 'wb') as opened_file:
    opened_file.write(download.raw.read())

注意:考虑使用 shutil.copyfile 或其他缓冲策略下载文件,这样整个文件在写入之前不会全部读入内存。

可以通过 versions 属性访问每个版本的详细信息

>>> photo.versions.keys()
['medium', 'original', 'thumb']

要下载照片资产的特定版本,请将版本传递给 download()

download = photo.download('thumb')
with open(photo.versions['thumb']['filename'], 'wb') as thumb_file:
    thumb_file.write(download.raw.read())

代码示例

如果您想查看一些代码示例,请参阅 代码示例文件

项目详情


下载文件

下载适用于您平台的文件。如果您不确定该选择哪个,请了解更多关于安装包的信息。

源代码分发

pyicloud-1.0.0.tar.gz (32.9 kB 查看散列值)

上传时间 源代码

构建分发

pyicloud-1.0.0-py3-none-any.whl (32.5 kB 查看散列值)

上传时间 Python 3

由以下提供支持