Youtube Data API V3 returning ssl.SSLWantWriteError: The operation did not complete (write) error

Youtube Data API V3 returning ssl.SSLWantWriteError: The operation did not complete (write) error

我之前问过几乎相同的问题,但由于在尝试解决该问题时出现了一个新错误,使最初的错误黯然失色,因此未能获得解决方案。问题是当我尝试使用下面的代码上传 youtube 视频时,我在标题中收到错误(下面的完整跟踪)并且无法这样做。我对导致错误的原因一无所知,非常感谢任何帮助。

def upload(beatName, mainArtist, keywords, oneLiner):

    CLIENT_SECRET_FILE = 'client_secret.json'
    API_NAME = 'youtube'
    API_VERSION = 'v3'
    SCOPES = ['https://www.googleapis.com/auth/youtube']

    service = Create_Service(CLIENT_SECRET_FILE, API_NAME, API_VERSION, SCOPES)

    upload_date_time = DT.datetime(2020, 9, 3, 12, 30, 0).isoformat() + '.000Z'

    title = "OTOROSHI - "test"
    description = 'Test'

    request_body = {
        'snippet': {
            'categoryI': 10,
            'title': title,
            'description': description,
            'tags': keywords
        },
        'status': {
            'privacyStatus': 'private',
            'publishAt': upload_date_time,
            'selfDeclaredMadeForKids': False,
        },
        'notifySubscribers': True
    }

    mediaFile = MediaFileUpload('E:\Everything.mp4')

    response_upload = service.videos().insert(
        part = 'snippet,status',
        body = request_body,
        media_body = mediaFile
    ).execute()
client_secret.json-youtube-v3-(['https://www.googleapis.com/auth/youtube'],)
['https://www.googleapis.com/auth/youtube']
youtube service created successfully
[INFO   ] [Base        ] Leaving application in progress...
 Traceback (most recent call last):
   File "C:/Users/ricsi/Desktop/Automatic Uploader/main.py", line 34, in <module>
     AutomaticUploader().run()
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\site-packages\kivy\app.py", line 950, in run
     runTouchApp()
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\site-packages\kivy\base.py", line 573, in runTouchApp
     EventLoop.mainloop()
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\site-packages\kivy\base.py", line 347, in mainloop
     self.idle()
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\site-packages\kivy\base.py", line 391, in idle
     self.dispatch_input()
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\site-packages\kivy\base.py", line 342, in dispatch_input
     post_dispatch_input(*pop(0))
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\site-packages\kivy\base.py", line 248, in post_dispatch_input
     listener.dispatch('on_motion', etype, me)
   File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\site-packages\kivy\core\window\__init__.py", line 1412, in on_motion
     self.dispatch('on_touch_down', me)
   File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\site-packages\kivy\core\window\__init__.py", line 1428, in on_touch_down
     if w.dispatch('on_touch_down', touch):
   File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\site-packages\kivy\uix\widget.py", line 545, in on_touch_down
     if child.dispatch('on_touch_down', touch):
   File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\site-packages\kivy\uix\widget.py", line 545, in on_touch_down
     if child.dispatch('on_touch_down', touch):
   File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\site-packages\kivy\uix\behaviors\button.py", line 151, in on_touch_down
     self.dispatch('on_press')
   File "kivy\_event.pyx", line 705, in kivy._event.EventDispatcher.dispatch
   File "kivy\_event.pyx", line 1248, in kivy._event.EventObservers.dispatch
   File "kivy\_event.pyx", line 1132, in kivy._event.EventObservers._dispatch
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\site-packages\kivy\lang\builder.py", line 57, in custom_callback
     exec(__kvlang__.co_value, idmap)
   File "C:\Users\ricsi\Desktop\Automatic Uploader\automaticuploader.kv", line 53, in <module>
     on_press: root.upload()
   File "C:/Users/ricsi/Desktop/Automatic Uploader/main.py", line 27, in upload
     upload(beatName, mainArtist, keywords, oneLiner)
   File "C:\Users\ricsi\Desktop\Automatic Uploader\youtube.py", line 228, in upload
     response_upload = service.videos().insert(
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\site-packages\googleapiclient\_helpers.py", line 134, in positional_wrapper
     return wrapped(*args, **kwargs)
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\site-packages\googleapiclient\http.py", line 892, in execute
     resp, content = _retry_request(
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\site-packages\googleapiclient\http.py", line 204, in _retry_request
     raise exception
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\site-packages\googleapiclient\http.py", line 177, in _retry_request
     resp, content = http.request(uri, method, *args, **kwargs)
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\site-packages\google_auth_httplib2.py", line 200, in request
     response, content = self.http.request(
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\site-packages\httplib2\__init__.py", line 1985, in request
     (response, content) = self._request(
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\site-packages\httplib2\__init__.py", line 1650, in _request
     (response, content) = self._conn_request(
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\site-packages\httplib2\__init__.py", line 1558, in _conn_request
     conn.request(method, request_uri, body, headers)
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\http\client.py", line 1255, in request
     self._send_request(method, url, body, headers, encode_chunked)
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\http\client.py", line 1301, in _send_request
     self.endheaders(body, encode_chunked=encode_chunked)
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\http\client.py", line 1250, in endheaders
     self._send_output(message_body, encode_chunked=encode_chunked)
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\http\client.py", line 1049, in _send_output
     self.send(chunk)
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\http\client.py", line 971, in send
     self.sock.sendall(data)
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\ssl.py", line 1204, in sendall
     v = self.send(byte_view[count:])
   File "C:\Users\ricsi\Anaconda3\envs\Automatic Uploader\lib\ssl.py", line 1173, in send
     return self._sslobj.write(data)
 ssl.SSLWantWriteError: The operation did not complete (write) (_ssl.c:2472)

Process finished with exit code 1

剧情简介

TLS/SSL 错误是由于以下级别之一可能出现的问题造成的:

  1. 客户端应用级别;
  2. Google 的 API 客户端库的级别及其相应的必要条件(参见下面列出的);
  3. 标准 Python 的 SSL 实施级别 (TLS/SSL wrapper for socket objects);
  4. 操作系统本身的级别。

针对OP的上述问题,我的建议如下:

Tackling SSL errors at the level of the client application, if experiencing the kind of errors seen above, instead of uploading video files in one go, do use the Resumable Upload Protocol.

使用 YouTube 数据 API 的可恢复上传功能可以很容易地完成,对于 Python v2 和 v3,如下所示。

请注意,使用此功能将使应用程序 更能应对 network-related 错误

详情

在尝试使用下面引用的 Python 脚本之前,应该确保下面列出的所有库(即 Python 包)都是 up-to-date。 (只需在每个指定的包上发布 pip install --upgrade。)

在修改您的应用程序之前,请测试 API(即 Resumable Uploading Protocol)的可续传上传功能确实可以正常工作(因此也没有 SSL 错误)。

首先要做的是下载 time-tested public Google 脚本 upload_video.py. (This script has an official documentation。要从脚本中获取 self-explanatory 使用信息页面,只需使用命令行选项 --help 发出它。)

如果您是 运行 Python v3,那么您必须修补 upload_video.py 才能在下面看到。在任何情况下,为了测试可恢复上传功能,只需在命令行提示符下发出如下命令:

python upload_video.py --file your_video_file.mp4 --title '...' --description '...' --privacyStatus private

或者,按情况:

python3 upload_video.py --file your_video_file.mp4 --title '...' --description '...' --privacyStatus private

请注意,您的客户端机密文件 -- client_secret.json -- 必须存在于托管 upload_video.py 的目录中。

运行 成功 upload_video.py 后,请务必在您自己的 YouTube 工作室页面上检查新上传的视频(并最终将其删除:

https://studio.youtube.com/channel/YOUR_CHANNEL_ID/videos/upload.

如果一切正常——没有 SSL 错误——,那么您可以继续重构您自己的代码,以便根据您的需要将 Python 代码包含在 upload_video.py 中。

正在修补 upload_video.py

目前由 Google 提供,脚本 upload_video.py 使用 Python v2 语言编码;要使其与 Python v3 一起使用,请对其应用以下更改:

  1. 此脚本的第一行:将 python 替换为 python3(这在 Windows 机器上并不是必需的);
  2. import httplib替换为import http.client
  3. 将所有出现的 httplib. 替换为 http.client.(注意那里的点);
  4. 将所有print EXPRESSION替换为print(EXPRESSION)
  5. 将所有 except IDENTIFIER, e 替换为 except IDENTIFIER as e

否则,请将以下 .patch 文件应用于脚本文件:

--- upload_video.py
+++ upload_video.py
@@ -1,7 +1,7 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 
 import argparse
-import httplib
+import http.client
 import httplib2
 import os
 import random
@@ -23,10 +23,10 @@
 MAX_RETRIES = 10
 
 # Always retry when these exceptions are raised.
-RETRIABLE_EXCEPTIONS = (httplib2.HttpLib2Error, IOError, httplib.NotConnected,
-  httplib.IncompleteRead, httplib.ImproperConnectionState,
-  httplib.CannotSendRequest, httplib.CannotSendHeader,
-  httplib.ResponseNotReady, httplib.BadStatusLine)
+RETRIABLE_EXCEPTIONS = (httplib2.HttpLib2Error, IOError, http.client.NotConnected,
+  http.client.IncompleteRead, http.client.ImproperConnectionState,
+  http.client.CannotSendRequest, http.client.CannotSendHeader,
+  http.client.ResponseNotReady, http.client.BadStatusLine)
 
 # Always retry when an apiclient.errors.HttpError with one of these status
 # codes is raised.
@@ -104,31 +104,31 @@
   retry = 0
   while response is None:
     try:
-      print 'Uploading file...'
+      print('Uploading file...')
       status, response = request.next_chunk()
       if response is not None:
         if 'id' in response:
-          print 'Video id "%s" was successfully uploaded.' % response['id']
+          print('Video id "%s" was successfully uploaded.' % response['id'])
         else:
           exit('The upload failed with an unexpected response: %s' % response)
-    except HttpError, e:
+    except HttpError as e:
       if e.resp.status in RETRIABLE_STATUS_CODES:
         error = 'A retriable HTTP error %d occurred:\n%s' % (e.resp.status,
                                                              e.content)
       else:
         raise
-    except RETRIABLE_EXCEPTIONS, e:
+    except RETRIABLE_EXCEPTIONS as e:
       error = 'A retriable error occurred: %s' % e
 
     if error is not None:
-      print error
+      print(error)
       retry += 1
       if retry > MAX_RETRIES:
         exit('No longer attempting to retry.')
 
       max_sleep = 2 ** retry
       sleep_seconds = random.random() * max_sleep
-      print 'Sleeping %f seconds and then retrying...' % sleep_seconds
+      print('Sleeping %f seconds and then retrying...' % sleep_seconds)
       time.sleep(sleep_seconds)
 
 if __name__ == '__main__':
@@ -150,5 +150,5 @@
 
   try:
     initialize_upload(youtube, args)
-  except HttpError, e:
-    print 'An HTTP error %d occurred:\n%s' % (e.resp.status, e.content)
+  except HttpError as e:
+    print('An HTTP error %d occurred:\n%s' % (e.resp.status, e.content))

上面的.patch文件完全可以用Python自己的工具2to3生成。该工具还可以就地修改 upload_video.py。 (要安装它,只需发出 pip install 2to3。)