I was coding this script that can get into my liked videos and make a playlist on Spotify with the title and artist of each video.
I already tried to renew the Token from the Spotify API manager, but for some reason it's still showing the following error:
status': 400, 'message': 'Only valid bearer authentication supported'}}
Traceback (most recent call last):
File "/Users/gzangerme/Desktop/Python Project/SpotifyAutomation.py", line 159, in <module>
cp.add_song_to_playlist()
File "/Users/gzangerme/Desktop/Python Project/SpotifyAutomation.py", line 129, in add_song_to_playlist
self.get_liked_videos()
File "/Users/gzangerme/Desktop/Python Project/SpotifyAutomation.py", line 76, in get_liked_videos
"spotify_uri": self.get_spotify_uri(song_name, artist)
File "/Users/gzangerme/Desktop/Python Project/SpotifyAutomation.py", line 119, in get_spotify_uri
songs = response_json["tracks"]["items"]
KeyError: 'tracks'
I noticed that the KeyError is showing up because the call is returning an error.
Here follows the code for the project:
import json
import requests
import os
from secrets import spotify_user_id, spotify_token
import google_auth_oauthlib.flow
import googleapiclient.discovery
import googleapiclient.errors
import youtube_dl
class CreatePlaylist:
def __init__(self):
self.user_id = spotify_user_id
self.spotify_token = spotify_token
self.youtube_client = self.get_youtube_client()
self.all_song_info = {}
#Step 1
def get_youtube_client(self):
os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
api_service_name = "youtube"
api_version = "v3"
#Get Credentials for API Client
flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file('client_secret.json', scopes=['https://www.googleapis.com/auth/youtube'])
credentials = flow.run_console()
youtube_client = googleapiclient.discovery.build(api_service_name, api_version, credentials=credentials)
return youtube_client
#Step 2
def get_liked_video(self):
request = self.youtube_client.videos().list(
part="snippet,contentDetails,statistics",
myRating="Like"
)
response = request.execute()
#Get information on each video on the list
for item in response["items"]:
video_title = item["snippet"]["title"]
youtube_url = "https://www.youtube.com/watch?v={}".format(item["id"])
#use youtube_dl to colect the name of the song and the artist
video = youtube_dl.YoutubeDL({}).extract_info(youtube_url,download = False)
song_name = video["track"]
artist = video["artist"]
self.all_song_info[video_title] = {
"youtube_url":youtube_url,
"song_name":song_name,
"artist":artist,
"spotify_uri":self.get_spotify_uri(song_name,artist)
}
#Step 3
def create_playlist(self):
request_body = json.dumps({
"name":"Youtube Liked Videos",
"description":"Todos os Videos com Like do YT",
"public": True
})
query = "https://api.spotify.com/v1/users/{user_id}/playlists".format()
response = requests.post(
query,
data= request_body,
headers={
"Content-type": "application/json",
"Authorization": "Bearer {}".format(spotify_token)
}
)
response_json = response.json
#playlistId
return response_json["id"]
#Step 4
def get_spotify_uri(self, song_name, artist):
query = "https://api.spotify.com/v1/search".format(
song_name,
artist
)
response = requests.get(
query,
headers={
"Content-type": "application/json",
"Authorization": "Bearer {}".format(spotify_token)
}
)
response_json = response.json()
songs = response_json["tracks"]["items"]
#configurar para utilizar somente a primeira musica
uri = songs[0]["uri"]
return uri
#Step 5
def add_song_to_playlist(self):
self.get_liked_video()
uris = []
for song ,info in self.all_song_info():
uris.apend(info["spotify_uri"])
#create new playlist
playlist_id = self.create_playlist
#add musics to the created playlist
request_data = json.dumps(uris)
query = "https://api.spotify.com/v1/playlists/{playlist_id}/tracks".format(playlist_id)
response = requests.post(
query,
data=request_data,
headers = {
"Content-Type":"application/json",
"Authorization": "Bearer {}".format(self.spotify_token)
}
)
response_json = response.json()
return response_json
CreatePlaylist()
I think the spotify_token and spotify_user_id are the issue. If you go to:
https://pypi.org/project/spotify-token/ it is a Python script where you can generate a Spotify token.
As for the spotify_user_id that is your username on Spotify. To find your username, go to: https://www.spotify.com/us/ , click on Profile > Account > Account overview
Hope it helps.
Related
Just started learning Python. I am trying to gather data by webscraping and tweet out info. But everytime I rerun the code. I get
Forbidden: 403 Forbidden
187 - Status is a duplicate.
How do I loop this script without getting this error?
Here's my code :
def scrape ():
page = requests.get("https://www.reuters.com/business/future-of-money/")
soup = BeautifulSoup(page.content, "html.parser")
home = soup.find(class_="editorial-franchise-layout__main__3cLBl")
posts = home.find_all(class_="text__text__1FZLe text__dark-grey__3Ml43 text__inherit-font__1Y8w3 text__inherit-size__1DZJi link__underline_on_hover__2zGL4")
top_post = posts[0].find("h3", class_="text__text__1FZLe text__dark-grey__3Ml43 text__medium__1kbOh text__heading_3__1kDhc heading__base__2T28j heading__heading_3__3aL54 hero-card__title__33EFM").find_all("span")[0].text.strip()
tweet (top_post)
def tweet (top_post):
api_key = 'deletedforprivacy'
api_key_secret = 'deletedforprivacy'
access_token = 'deletedforprivacy'
access_token_secret = 'deletedforprivacy'
authenticator = tweepy.OAuthHandler(api_key, api_key_secret)
authenticator.set_access_token(access_token, access_token_secret)
api = tweepy.API(authenticator, wait_on_rate_limit=True)
api.update_status(f"{top_post} \nSource : https://www.reuters.com/business/future-of-money/")
print(top_post)
scrape()
The twitter api checks if the content is duplicate and if it is duplicate it returns:
Request returned an error: 403 {"detail":"You are not allowed to create a Tweet with duplicate content.","type":"about:blank","title":"Forbidden","status":403}
I added an simple function to check if the previous content is same as the one about to be added
** Full Code**
from requests_oauthlib import OAuth1Session
import os
import json
import requests
from bs4 import BeautifulSoup
import time
user_id = 000000000000000 # Get userid from https://tweeterid.com/
bearer_token = "<BEARER_TOKEN>"
consumer_key = "<CONSUMER_KEY>"
consumer_secret = "<CONSUMER_SECRET>"
def init():
# Get request token
request_token_url = "https://api.twitter.com/oauth/request_token?oauth_callback=oob&x_auth_access_type=write"
oauth = OAuth1Session(consumer_key, client_secret=consumer_secret)
try:
fetch_response = oauth.fetch_request_token(request_token_url)
except ValueError:
print(
"There may have been an issue with the consumer_key or consumer_secret you entered."
)
resource_owner_key = fetch_response.get("oauth_token")
resource_owner_secret = fetch_response.get("oauth_token_secret")
print("Got OAuth token and secret")
# Get authorization
base_authorization_url = "https://api.twitter.com/oauth/authorize"
authorization_url = oauth.authorization_url(base_authorization_url)
print("Please go here and authorize: %s" % authorization_url)
verifier = input("Paste the PIN here: ")
# Get the access token
access_token_url = "https://api.twitter.com/oauth/access_token"
oauth = OAuth1Session(
consumer_key,
client_secret=consumer_secret,
resource_owner_key=resource_owner_key,
resource_owner_secret=resource_owner_secret,
verifier=verifier,
)
oauth_tokens = oauth.fetch_access_token(access_token_url)
access_token = oauth_tokens["oauth_token"]
access_token_secret = oauth_tokens["oauth_token_secret"]
# Make the request
oauth = OAuth1Session(
consumer_key,
client_secret=consumer_secret,
resource_owner_key=access_token,
resource_owner_secret=access_token_secret,
)
scraper(oauth, bearer_token)
def bearer_oauth(r):
"""
Method required by bearer token authentication.
"""
r.headers["Authorization"] = f"Bearer {bearer_token}"
r.headers["User-Agent"] = "v2UserTweetsPython"
return r
def previous_tweet():
url = "https://api.twitter.com/2/users/{}/tweets".format(user_id)
# Tweet fields are adjustable.
# Options include:
# attachments, author_id, context_annotations,
# conversation_id, created_at, entities, geo, id,
# in_reply_to_user_id, lang, non_public_metrics, organic_metrics,
# possibly_sensitive, promoted_metrics, public_metrics, referenced_tweets,
# source, text, and withheld
params = {"tweet.fields": "text"}
response = requests.request(
"GET", url, auth=bearer_oauth, params=params)
print(response.status_code)
if response.status_code != 200:
raise Exception(
"Request returned an error: {} {}".format(
response.status_code, response.text
)
)
# checking if this is the first post
if response.json() != {'meta': {'result_count': 0}}:
# Since twitter changes html to small url I am splitting at \n to match to new payload
previous_tweet_text = response.json()["data"][0]["text"].split("\n")[0]
previous_payload = {"text": f"{previous_tweet_text}"}
else:
previous_payload = {"text": f""}
return previous_payload
def scraper(oauth, bearer_token):
while True:
page = requests.get(
"https://www.reuters.com/business/future-of-money/")
soup = BeautifulSoup(page.content, "html.parser")
home = soup.find(class_="editorial-franchise-layout__main__3cLBl")
posts = home.find_all(
class_="text__text__1FZLe text__dark-grey__3Ml43 text__inherit-font__1Y8w3 text__inherit-size__1DZJi link__underline_on_hover__2zGL4")
top_post = posts[0].find(
"h3", class_="text__text__1FZLe text__dark-grey__3Ml43 text__medium__1kbOh text__heading_3__1kDhc heading__base__2T28j heading__heading_3__3aL54 hero-card__title__33EFM").find_all("span")[0].text.strip()
# Be sure to add replace the text of the with the text you wish to Tweet. You can also add parameters to post polls, quote Tweets, Tweet with reply settings, and Tweet to Super Followers in addition to other features.
payload = {
"text": f"{top_post}\nSource:https://www.reuters.com/business/future-of-money/"}
current_checker_payload = {"text": payload["text"].split("\n")[0]}
previous_payload = previous_tweet()
if previous_payload != current_checker_payload:
tweet(payload, oauth)
else:
print("Content hasn't changed")
time.sleep(60)
def tweet(payload, oauth):
# Making the request
response = oauth.post(
"https://api.twitter.com/2/tweets",
json=payload,
)
if response.status_code != 201:
raise Exception(
"Request returned an error: {} {}".format(
response.status_code, response.text)
)
print("Response code: {}".format(response.status_code))
# Showing the response as JSON
json_response = response.json()
print(json.dumps(json_response, indent=4, sort_keys=True))
if __name__ == "__main__":
init()
** Output**
Response code: 201
{
"data": {
"id": "1598558336672497664",
"text": "FTX ex-CEO Bankman-Fried claims he was unaware of improper use of customer funds -ABC News\nSource:URL" #couldn't post short url in stackoverflow
}
}
Content hasn't changed
Content hasn't changed
Content hasn't changed
Hope this helps. Happy Coding :)
I'm finding the Kraken Futures API confusing compared to other providers. Using a demo account I'm trying to make basic private requests and not working so far with authentication error. The code mainly comes from Kraken docs (non-futures)
Futures auth doc: https://support.kraken.com/hc/en-us/articles/360022635592-Generate-authentication-strings-REST-API-
api_sec = "MxA2FwIQxCxsfy2XDa4R8PwTjwLKjzT8GSOw+qOVuWGh3Lx6PtyW0f94J5XXKz9mP8bztRJSDQJVKBsHFicrDr/N"
api_url = "https://futures.kraken.com/derivatives/api/v3"
api_key = 'Y7kVv/hW0JWRRAhJtA8BuJkUX+E0gWmTL5NWf4lRPN8f+iYoJp9AoYwW'
def get_kraken_signature(urlpath, data, secret):
postdata = urllib.parse.urlencode(data)
encoded = (str(data['nonce']) + postdata).encode()
message = urlpath.encode() + hashlib.sha256(encoded).digest()
mac = hmac.new(base64.b64decode(secret), message, hashlib.sha256)
sigdigest = base64.b64encode(mac.digest())
return sigdigest.decode()
# Attaches auth headers and returns results of a get request
def kraken_request(uri_path, data, api_key, api_sec):
headers = {}
headers['API-Key'] = api_key
# get_kraken_signature() as defined in the 'Authentication' section
headers['API-Sign'] = get_kraken_signature(uri_path, data, api_sec)
req = requests.get((api_url + uri_path), headers=headers, data=data)
return req
# Construct the request and print the result
resp = kraken_request('/accounts', {
"nonce": str(int(1000*time.time()))
}, api_key, api_sec)
Output
{"result":"error","error":"authenticationError","serverTime":"2022-05-13T10:14:50.838Z"}
An overview of the project.
API Used
Using Spotify API to Create a Playlist and add music to the playlist
Using Youtube Data API to retrieve liked videos
OAuth 2.0 for verification
Goal:
The liked youtube videos of my youtube account should automatically come inside my Spotify newly created playlist
Code:
import json
import os
import google_auth_oauthlib.flow
import google.oauth2.credentials
import googleapiclient.discovery
import googleapiclient.errors
import requests
import youtube_dl
from secret import spotify_token, spotify_user_id
from exceptions import ResponseException
class CreatePlaylist:
def __init__(self):
self.user_id = spotify_user_id
self.spotify_token = spotify_token
self.youtube_client = self.get_youtube_client()
self.all_song_info = {}
# connect to youtube data api
def get_youtube_client(self):
# Disable OAuthlib's HTTPS verification when running locally.
# *DO NOT* leave this option enabled in production.
os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
api_service_name = "youtube"
api_version = "v3"
client_secrets_file = "youtube_auth.json"
# Get credentials and create an API client
scopes = ["https://www.googleapis.com/auth/youtube.readonly"]
flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file(
client_secrets_file, scopes)
credentials = flow.run_console()
# from the Youtube DATA API
youtube_client = googleapiclient.discovery.build(
api_service_name, api_version, credentials=credentials)
return youtube_client
# remove **kwarg that are not set
def get_liked_videos(self):
request = self.youtube_client.videos().list(
part="snippet,contentDetails,statistics", myRating="like"
)
response = request.execute()
# collect each video and get important information
for item in response['items']:
video_title = item['snippet']['title']
youtube_url = "https://www.youtube.com/watch?v={}".format(
item["id"])
# use youtube_dl to collect song name and artist name
video = youtube_dl.YoutubeDL({}).extract_info(
youtube_url, download=False)
song_name = video['track']
artist = video['artist']
# save all important info and skip any missing song and artist
self.all_song_info[video_title] = {
"youtube_url": youtube_url,
"song_name": song_name,
"artist": artist,
# add the uri, easy to get song to put into playlist
"spotify_uri": self.get_spotify_uri(song_name, artist)
}
# create a new playlist
def create_playlist(self):
request_body = json.dumps({
"name": "Youtube Liked Songs",
"description": "All liked youtube video songs",
"public": True
})
query = "https://api.spotify.com/v1/users/{}/playlists".format(
self.user_id)
response = requests.post(
query,
data=request_body,
headers={
"Content-Type": "application/json",
"Authorization": "Bearer {}".format(self.spotify_token)
}
)
response_json = response.json()
# playlis id
return response_json["id"]
# search for the song on Spotify
def get_spotify_uri(self, song_name, artist):
query = "https://api.spotify.com/v1/search?query=track%3A{}+artist%3A{}&type=track&offset=0&limit=20".format(
song_name,
artist
)
response = requests.get(
query,
headers={
"Content-Type": "application/json",
"Authorization": "Bearer {}".format(self.spotify_token)
}
)
response_json = response.json()
songs = response_json["tracks"]["items"]
# only use the first song
uri = songs[0]["uri"]
return uri
# add the song in new spotify_playlist
def add_song_to_playlist(self):
# populate dictionary with our liked songs
self.get_liked_videos()
# collect all of uri
uris = []
for song, info in self.all_song_info.items():
uris.append(info['spotify_uri'])
# create a new playlist
playlist_id = self.create_playlist()
# add all songs into new playlist
request_data = json.dumps(uris)
query = "https://api.spotify.com/v1/playlists/{}/tracks".format(
playlist_id)
response = requests.post(
query,
data=request_data,
headers={
"Content-Type": "application/json",
"Authorization": "Bearer {}".format(self.spotify_token)
})
if response.status_code < 200 or response.status_code > 300:
raise ResponseException(response.status_code)
response_json = response.json()
return response_json
if __name__ == '__main__':
cp = CreatePlaylist()
cp.add_song_to_playlist()
Output
A new playlist is made inside spotify libray but the song in the list doesn't belong to my liked videos and the songs are repeated in the playlist the number of songs are almost 5-6 and all are same
Link of the song: https://www.youtube.com/watch?v=4awXLGzlf7E
Thanks in advance kinda help.
I have this python function and i wish to execute this using lambda handler function hence I have written this code. When I execute in Pycharm I don't see any output in console. Can someone guide what is the problem with below code?
import json
from json import loads
import requests
from requests import exceptions
from requests.auth import HTTPBasicAuth
def lambda_handler(event, context):
test_post_headers_body_json()
return {"statusCode": 200, "body": json.dumps("Hello from Lambda!")}
def test_post_headers_body_json():
client_id = "WJRYDHNGROIZHL8B"
client_secret = "V5VXK6FLG1YI0GD2XY3H"
user = "automation-store-admin1#abc.com"
password = "c0Ba5PBdvVl2"
access_point = "https://api.platform.abc.com/auth/oauth/token"
grant_type = "password"
headers = {"Content-Type": "application/x-www-form-urlencoded"}
# auth = auth.HTTPBasicAuth(client_id, client_secret)
data = {"grant_type": grant_type, "username": user, "password": password}
resp = None
try:
resp = requests.post(
access_point,
auth=HTTPBasicAuth(client_id, client_secret),
data=data,
headers=headers,
)
except exceptions.ConnectionError:
exit(1)
if resp.status_code == 200:
resp = loads(resp.text)
if "access_token" in resp:
print(resp["access_token"])
exit(0)
exit(1)
It is normal because when running you code, Python only declare the function not using it. You should add a __main__ entry point at the end of your file:
import json
from json import loads
import requests
from requests import exceptions
from requests.auth import HTTPBasicAuth
def lambda_handler(event, context):
test_post_headers_body_json()
return {"statusCode": 200, "body": json.dumps("Hello from Lambda!")}
def test_post_headers_body_json():
client_id = "WJRYDHNGROIZHL8B"
client_secret = "V5VXK6FLG1YI0GD2XY3H"
user = "automation-store-admin1#abc.com"
password = "c0Ba5PBdvVl2"
access_point = "https://api.platform.abc.com/auth/oauth/token"
grant_type = "password"
headers = {"Content-Type": "application/x-www-form-urlencoded"}
# auth = auth.HTTPBasicAuth(client_id, client_secret)
data = {"grant_type": grant_type, "username": user, "password": password}
resp = None
try:
resp = requests.post(
access_point,
auth=HTTPBasicAuth(client_id, client_secret),
data=data,
headers=headers,
)
except exceptions.ConnectionError:
exit(1)
if resp.status_code == 200:
resp = loads(resp.text)
if "access_token" in resp:
print(resp["access_token"])
exit(0)
exit(1)
# added part
if __name__ == '__main__':
test_post_headers_body_json()
I'm a beginner with Python and trying to build a service that takes information from api.ai, passes it to an API, then returns a confirmation message from the JSON it returns.
app.py:
#!/usr/bin/env python
from __future__ import print_function
from future.standard_library import install_aliases
install_aliases()
from urllib.parse import urlparse, urlencode
from urllib.request import urlopen, Request
from urllib.error import HTTPError
import json
import os
import sys
import logging
from flask import Flask, render_template
from flask import request
from flask import make_response
# Flask app should start in global layout
app = Flask(__name__)
app.logger.addHandler(logging.StreamHandler(sys.stdout))
app.logger.setLevel(logging.ERROR)
#app.route('/webhook', methods=['POST'])
def webhook():
req = request.get_json(silent=True, force=True)
print("Request:")
print(json.dumps(req, indent=4))
res = processRequest(req)
res = json.dumps(res, indent=4)
# print(res)
r = make_response(res)
r.headers['Content-Type'] = 'application/json'
return r
def processRequest(req):
if req.get("result").get("action") != "bookMyConference":
return {}
#oauth
orequest = req.get("originalRequest") # work down the tree
odata = orequest.get("data") # work down the tree
user = odata.get("user") # work down the tree
access_token = user.get("access_token")
#data
result = req.get("result") # work down the tree
parameters = result.get("parameters") # work down the tree
startdate = parameters.get("start-date")
meetingname = parameters.get("meeting-name")
payload = {
"start-date": startdate,
"end-date": startdate,
"meeting-name": meetingname
}
# POST info to join.me
baseurl = "https://api.join.me/v1/meetings"
p = Request(baseurl)
p.add_header('Content-Type', 'application/json; charset=utf-8')
p.add_header('Authorization', 'Bearer ' + access_token) #from oauth
jsondata = json.dumps(payload)
jsondataasbytes = jsondata.encode('utf-8') # needs to be bytes
jresult = urlopen(p, jsondataasbytes).read()
data = json.loads(jresult)
res = makeWebhookResult(data)
return res
def makeWebhookResult(data):
speech = "Appointment scheduled!"
print("Response:")
print(speech)
return {
"speech": speech,
"displayText": speech,
# "data": data,
"source": "heroku-bookmyconference"
}
if __name__ == '__main__':
port = int(os.getenv('PORT', 5000))
print("Starting app on port %d" % port)
app.run(debug=False, port=port, host='0.0.0.0')
Edit 4: Here's the error I'm getting in my Heroku logs:
2017-03-21T19:06:09.383612+00:00 app[web.1]: HTTPError: HTTP Error
400: Bad Request
Borrowing from here, using urlib modules inside processRequest() you could add your payload to urlopen like this:
req = Request(yql_url)
req.add_header('Content-Type', 'application/json; charset=utf-8')
jsondata = json.dumps(payload)
jsondataasbytes = jsondata.encode('utf-8') # needs to be bytes
result = urlopen(req, jsondataasbytes).read()
data = json.loads(result)
Things get more succinct if using the requests module:
headers = {'content-type': 'application/json'}
result = requests.post(yql_url, data=json.dumps(payload), headers=headers)
data = result.json()
EDIT: Adding some details specific to the join.me api
Looking at the join.me docs you'll need to obtain an access token to add to your header. But you also need an app auth code before you can get an access token. You can get the app auth code manually, or by chaining some redirects.
To get started, try this url in your browser and get the code from the callback params. Using your join.me creds:
auth_url = 'https://secure.join.me/api/public/v1/auth/oauth2' \
+ '?client_id=' + client_id \
+ '&scope=scheduler%20start_meeting' \
+ '&redirect_uri=' + callback_url \
+ '&state=ABCD' \
+ '&response_type=code'
print(auth_url) # try in browser
To get an access token:
token_url = 'https://secure.join.me/api/public/v1/auth/token'
headers = {'content-type': 'application/json'}
token_params = {
'client_id': client_id,
'client_secret': client_secret,
'code': auth_code,
'redirect_uri': callback_url,
'grant_type': 'authorization_code'
}
result = requests.post(token_url, data=json.dumps(token_params), headers=headers)
access_token = result.json().get('access_token')
Then your header for the post to /meetings would need to look like:
headers = {
'content-type': 'application/json',
'Authorization': 'Bearer ' + access_token
}