第三方登录平台免不了需要登录验证,比起命令行输入账号密码再填充表单,不如直接用浏览器实在
pip install PyQt5
pip install PyQt5-tools
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtWebKit import *
from PyQt5.QtWebKitWidgets import *
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow
class webForm():
def __init__(self):
self.app = QApplication(sys.argv)
self.form = QWidget()
self._debug = False
self.webview = QWebView()
self.webview.page().mainFrame().urlChanged.connect(self._urlChanged)
vbox = QVBoxLayout()
vbox.addWidget(self.webview)
main = QGridLayout()
main.setSpacing(0)
main.addLayout(vbox, 0, 0)
self.form.setLayout(main)
self.callbackFunc = None
def show(self, url, title, debug=False):
""" 打开内嵌浏览器(在浏览器窗口关闭前,主线程将被挂起)
url: 目标链接
title: 窗口标题
debug: 是否在控制台打印出每次url跳转及参数
"""
self._debug = debug
self.form.setWindowTitle(title)
self.form.show()
self.webview.load(QUrl(url))
self.app.exec_()
def close(self):
self.form.close()
def __del__(self):
self.close()
def set_callback(self, callbackFunc):
""" callbackFunc(QUrl) """
self.callbackFunc = callbackFunc
def _loadFinished(self,bool):
if bool != True:
return
url = self.webview.url()
if self.callbackFunc != None:
self.callbackFunc(url)
def _urlChanged(self, url):
##debug#############
if self._debug == True:
query = QUrlQuery(url.query())
items = query.queryItems()
logging.info("--------------------------------------")
logging.info("[url]: " + url.toString())
logging.info("[host]: " + url.host())
#print(" query: " + query.toString())
for i in items:
logging.info(" " + i[0] + " : " + i[1])
#####################
if self.callbackFunc != None:
self.callbackFunc(url)
pass
使用tweepy进行登录验证拿到access_token后,可以自由发布文字、图片。
pip install tweepy
import tweepy
import logging
class Twitter():
def __init__(self, consumer_key, consumer_secret, redirect_uri):
""" 需要提供应用密钥和回调地址
参数获取链接:https://dev.twitter.com/apps
redirect_uri必须填写一个可以访问的网址
"""
self._consumer_key = consumer_key
self._consumer_secret = consumer_secret
self._redirect_uri = redirect_uri
self._access_token = ""
self._access_token_secret = ""
self.is_access = False
def access(self):
""" 获取授权
return True is success
"""
flag = False
self.is_access = False
self.auth = tweepy.OAuthHandler(self._consumer_key, self._consumer_secret)
logging.info("请求授权URL")
try:
url = self.auth.get_authorization_url(False,'HMAC-SHA1')
logging.info("请求授权URL-成功")
except Exception as e:
logging.error("请求授权URL-失败")
return flag
logging.info("进行授权验证")
self._form = webForm()
self._form.set_callback(self.webview_callback)
self._form.show(url, "分享到Twitter", True)
if self.is_access == True:
flag = True
return flag
def webview_callback(self, url):
if QUrl(self._redirect_uri).host() == url.host():
query = QUrlQuery(url.query())
items = query.queryItems()
if self._get_access_token(items) == True:
self._form.close()
def is_access(self):
return self.is_access
def _get_access_token(self, list):
""" 获取授权token
return True is success
"""
for i in list:
if i[0] == 'oauth_verifier':
try:
self._access_token, self._access_token_secret = self.auth.get_access_token(i[1])
self.api = tweepy.API(self.auth)
self._client_id = self.api.me().id
self.is_access = True
logging.info("请求授权 成功")
logging.info("用户名: " + self.api.me().name)
#logging.info("access_token: " + self._access_token)
#logging.info("access_token_secret: " + self._access_token_secret)
return True
except Exception as e:
self.is_access = False
logging.error("请求授 权失败 code:%d\n msg:%s",e.args)
#raise e
return False
def post_tweet(self, text):
""" 发布tweet
不要连续发两条一样的推,不然会遇到错误
{'code': 187, 'message': 'Status is a duplicate.'}
return True is success
"""
if self.is_access == False:
logging.info("没有获取授权!")
return False
if text == "":
logging.info("tweet文不能为空!")
return False
try:
self.api.update_status(text)
logging.info("发布tweet 成功")
except Exception as e:
logging.error("发布tweet 失败: ",e.args)
return False
return True
def post_tweet_with_media(self,text,filenames):
""" 发布带媒体的tweet
text = "hello world"
filenames = ['C:\\1.png', 'C:\\2.png', ...]
return True is success
"""
if self.is_access == False:
logging.info("没有获取授权!")
return False
try:
# upload images and get media_ids
media_ids = []
for filename in filenames:
res = self.api.media_upload(filename)
media_ids.append(res.media_id)
# tweet with multiple images
self.api.update_status(status=text, media_ids=media_ids)
logging.info("发布带图tweet 成功")
except Exception as e:
logging.error("发布带图tweet 失败: ",e.args)
return False
def get_last_tweet(self):
""" 获取最新一条tweet
return NOT "" is success
"""
text = ""
if self.is_access == False:
logging.info("没有获取授权!")
else:
try:
tweet = self.api.user_timeline(id = self._client_id, count = 1)[0]
logging.info("获取tweet 成功")
text = tweet.text
except Exception as e:
logging.error("获取tweet 失败: ",e.args)
return text
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
tw = Twitter("Consumer Key","Consumer Secret","https://www.baidu.com")
if tw.access() == True:
print("验证成功")
import random
text = "Hello World " + str(random.random())
tw.post_tweet(text)
#filenames = ['C:\\1.png', 'C:\\2.png']
filenames = ['output.mp4']
if tw.post_tweet_with_media(text,filenames) == True:
print("分享成功")
else:
print("分享失败")
Youtube 上只能分享视频。Youtube API 使用Google开发者后台,所以需要先与Google对接。
pip install --upgrade google-api-python-client
pip install --upgrade google-auth google-auth-oauthlib google-auth-httplib2
pip install oauth2client
pip install apiclient
pip install httplib2
import logging
import time
import httplib2
import oauth2client.client
import oauth2client.file
import oauth2client.tools
import googleapiclient.discovery
import googleapiclient.errors
import googleapiclient.http
class Youtube():
def __init__(self, client_secret_path, redirect_uri):
""" 需要提供凭据文件和回调地址
1.在Google API Console新建应用 (https://console.developers.google.com/apis)
2.生成Oauth凭据
3.新建网页客户端
4.填写登录回调地址redirect_uri,地址必须在 Search Console中认证通过 (https://www.google.com/webmasters/tools)
5.到凭据页面下载网页客户端的凭据client_secret.json (可以考虑把凭据文件放到服务器上)
"""
self._client_secret_file = client_secret_path
self._callback_url = "https://raw.githubusercontent.com/aa13058219642/facebook-callback/master"
#请求权限 (其他权限详见
#https://developers.google.com/youtube/v3/guides/auth/client-side-web-apps)
self._scopes = ['https://www.googleapis.com/auth/youtube','https://www.googleapis.com/auth/youtube.upload']
self._access_token = ""
self.is_access = False
def access(self):
""" 获取授权
return True is success
"""
state = False
self.is_access = False
self.flow = oauth2client.client.flow_from_clientsecrets(self._client_secret_file,
self._scopes,redirect_uri=self._callback_url,
prompt='consent')
url = self._get_authorize_url()
if url != None:
logging.info("进行Google授权验证")
self._form = webForm()
self._form.set_callback(self.webview_callback)
self._form.show(url,"分享到Youtube")
if self.is_access == True:
state = True
return state
def _get_authorize_url(self):
""" 请求授权URL
return NOT None success
"""
url = None
try:
logging.info("请求授权URL")
url = self.flow.step1_get_authorize_url()
except Exception as e:
logging.error("请求授权URL-失败")
url = None
return url
def _get_access_token(self, list):
""" 获取授权token
return True is success
"""
state = False
for i in list:
if i[0] == 'code':
try:
credentials = self.flow.step2_exchange(i[1])
#保存授权文件
#storage = oauth2client.file.Storage('credentials.json')
#storage.put(credentials)
http = httplib2.Http()
http = credentials.authorize(http)
self._youtube = googleapiclient.discovery.build("youtube","v3",http = http,cache_discovery=False)
self.is_access = True
logging.info("请求授权 成功")
#logging.info("用户名: " + self.api.me().name)
#logging.info("access_token: " + self._access_token)
#logging.info("access_token_secret: " + self._access_token_secret)
state = True
except Exception as e:
self.is_access = False
logging.error("请求授 权失败 code:%d\n msg:%s",e.args)
elif i[0] == "error":
logging.info("请求授权 失败")
for j in list:
logging.info(j[0] + ": " + j[1])
break
return state
def webview_callback(self, url):
if QUrl(self._callback_url).host() == url.host():
query = QUrlQuery(url.query())
items = query.queryItems()
if self._get_access_token(items) == True:
self.is_access = True
else:
self.is_access = False
self._form.close()
pass
def post(self,text):
print("post")
def upload(self, filename, title, description='', category='22', keywords='', privacyStatus='public'):
""" 上传视频
filename: 文件名及路径
title: 视频标题
description: 视频描述
category: 视频类别ID(id列表: https://gist.github.com/dgp/1b24bf2961521bd75d6c)
keywords: 关键字 (以英文逗号分隔)
privacyStatus: 隐私权限 ('public', 'private', 'unlisted')
"""
if self.is_access == False:
logging.info("没有获取授权!")
return False
tags = None
if keywords:
tags = keywords.split(',')
body = dict(snippet=dict(title=title,
description=description,
tags=tags,
categoryId=category),
status=dict(privacyStatus=privacyStatus))
# Call the API's videos.insert method to create and upload the video.
insert_request = self._youtube.videos().insert(part=','.join(body.keys()),
body=body,
# The chunksize parameter specifies the size of each chunk of data,
# in bytes, that will be uploaded at a time. Set a higher value for
# reliable connections as fewer chunks lead to faster uploads.
# Set a lower value for better recovery on less reliable connections.
#
# Setting 'chunksize' equal to -1 in the code below means that the entire
# file will be uploaded in a single HTTP request. (If the upload fails,
# it will still be retried where it left off.) This is usually a best
# practice, but if you're using Python older than 2.6 or if you're
# running on App Engine, you should set the chunksize to something
# like 1024 * 1024 (1 megabyte).
media_body=googleapiclient.http.MediaFileUpload(filename, chunksize=-1, resumable=True))
# This method implements an exponential backoff strategy to resume a
# failed upload.
response = None
error = None
retry = 0
while response is None:
try:
logging.info('Uploading file...')
status, response = insert_request.next_chunk()
if response is not None:
if 'id' in response:
logging.info('Video id "%s" was successfully uploaded.' % response['id'])
else:
logging.info('The upload failed with an unexpected response: %s' % response)
except Exception as e:
error = 'A retriable error occurred: %s' % e
if error is not None:
logging.info(error)
retry += 1
if retry > MAX_RETRIES:
logging.info('No longer attempting to retry.')
max_sleep = 2 ** retry
sleep_seconds = random.random() * max_sleep
logging.info('Sleeping %f seconds and then retrying...' % sleep_seconds)
time.sleep(sleep_seconds)
return True
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
youtu = Youtube('client_secret.json',"https://www.example.com")
if youtu.access() == True:
print("验证成功\n开始上传视频")
video = "output.mp4"
title = "Hello World"
if youtu.upload(video,title)==True:
print("分享成功")
else:
print("分享失败")
Facebook停用了public_actions权限以后,就不再支持登录后接管账户的操作了,要通过facebook分享内容只能通过额外提供的分享页面,该页面只能分享网址。不过可以通过在目标网址中添加mate标签来实现部分自定义内容。
Facebook分享不需要第三方库
import logging
class Facebook():
def __init__(self, app_id, redirect_uri):
""" 需要提供应用ID和回调地址
app_id: 获取步骤详见 https://developers.facebook.com/
redirect_uri: 必须是开发者应用中的“应用域名”属性里的子域名 (https://developers.facebook.com/apps/{{{{app_id}}}}/settings/basic/)
"""
self._app_id = app_id
#self._app_secret = ""
self._redirect_uri = redirect_uri
self.is_access = False
def webview_callback(self, url):
if QUrl(self._redirect_uri).host() == url.host():
self.is_access = True
return
def post(self, link, hashtag=None, display=None):
"""打开发布动态窗口
link: 要分享的目标网址(目标网站配置指南: https://developers.facebook.com/docs/sharing/webmasters#markup)
hashtag: 话题标签(不需要带“#”号)
display: 展示方式(详见 https://developers.facebook.com/docs/sharing/reference/share-dialog)
"""
logging.info("登录验证")
if display == None:
display = "popup"
tag = "" if hashtag == None else "&hashtag=%23" + hashtag
url = ("https://www.facebook.com/dialog/feed?" + "app_id=" + self._app_id + "&display=" + display + "&link=" + link + "&redirect_uri=" + self._redirect_uri + tag)
self.is_access = False
self._form = webForm()
self._form.set_callback(self.webview_callback)
self._form.show(url,"分享到facebook")
return self.is_access
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
link = "https://www.google.com/"
fb = Facebook("app_id","https://www.baidu.com/")
if fb.post("https://www.google.com/","hellooo") == True:
print("分享成功")
else:
print("分享失败")