0
votes

I have a tableau 10.0 server. I have a view at https://example.com/views/SomeReport/Totals. In a browser, if I navigate to https://example.com/views/SomeReport/Totals.csv I am prompted to login and then the view is downloaded as a csv file.

Im trying to download this same file using python

Details

  • tableau 10.0
  • python 2.7
  • tableau running on windows
  • python running on a mac OSX El capitan

Here is what I tried:

        tableau_file_path = '{tableau_path}{tableau_view}{file_ext}'.format(**cur_file)
        local_path = cur_file['local_path']
        user = 'xxxx'
        password = 'xxxx'

        # print tableau_file_path  # outputs https://example.com/views/SomeReport/Totals.csv
        # print local_path         # outputs ../generated_resources/Some Folder Name/Totals.csv 

        r = requests.get(tableau_file_path, auth=HTTPBasicAuth(user, password), stream=True, timeout=5)
        # also tried auth=(user, password)
        print r.status_code   # outputs 401
        if r.status_code == 200:
            with open(local_path, 'wb') as f: 
                f.write(r.content)

I feel like this should work, but it does not. I get 401 for the status

If I remove the .csv from the path, it works and I do get the actual tableau dashboard page. But I need the csv file not the html from the tableau dashboard

I've copied the username and password directly from my code and pasted them into the form that pops up in my browser and the creds work seemingly ruling out issues with wrong creds.

This works for normal webpages, but does not work when hitting the url prompts a file download. How can I make this work with file downloads?

Note I am open to other methods of downloading the file with python

2
status code 401 means unauthorized (also when authentication fails)...are you sure the username and password are correct? - jatinderjit
@jatinderjit positive, I copied them directly from the code and pasted them into the web form, right in, no issue - Wesley Smith
you can use DevTools in Chrome or Firefox to see what browser sends to server. Maybe it sends extre data, cookies, encrypted password, etc. - furas
@furas I cant see it in the dev tools because its a file being downloaded not an actual page but that made me think. If I remove the .csv from the path, it works and I do get the actual page. But I need the file not the html from the tableau dashboard - Wesley Smith
@DelightedD0D HTTP live headers is nice tool but it doesn't show you information if you don't logout first. - furas

2 Answers

1
votes

I'm gonna go out on a limb here and say that Tableau is not doing HTTP basic authentication, but rather have their own method. So when you try to access the thing through requests, it rejects it because it doesn't know how to auth like that.

When you access it as a human, it knows to redirect you to their login page and do the whole "login flow" and finally redirect you to the proper download page. However, requests tries to clearly identify itself as a robot, and uses a special UserAgent string (just "requests" I believe), which is why the server decides to return a straight up 401.

Depending on your use case, you could log in manually, and see what cookies are being set by Tableau, and include those in your request. You might also change your user agent to a browser one (they start with "Mozilla 5.0/" in the vast-vast majority of cases), and see if you download the login page. If that's the case, you could reverse-engineer some of their login process - enough to find out how they submit the user and password, and how they redirect to the page you want. After that, you'll most likely make a POST to their login infrastructure with that data and the required redirect.

1
votes

Tableau exposes a REST API which you can use to download the csv file. API end point is https://MY-SERVER/api/VERSION_NUM/auth/signin where VERSION_NUM depends on the version of tableau server and mapping for API version & Tableau server version can be found here. For authentication to work successfully, you'll need to get a token using above mentioned API end point. You can then pass this token in header parameter 'X-tableau-auth'. Here's the code:

import requests, json
from config import USERNAME, PASSWORD, SERVER_NAME

# NOTE! Substitute your own values for the following variables
server_name = SERVER_NAME # Name or IP address of your installation of Tableau Server
user_name = USERNAME    # User name to sign in as (e.g. admin)
password = PASSWORD
site_url_id = ""          # Site to sign in to. An empty string is used to specify the default site.

signin_url = "http://{}/api/3.1/auth/signin".format(server_name)

csv_url = "https://{}/#/views/Test/Dash/A.csv".format(server_name)

payload = { "credentials": { "name": user_name, "password": password, "site": {"contentUrl": site_url_id }}}

headers = {
  'accept': 'text/csv,application/json',
  'content-type': 'application/json'
}

# Send the request to the server
req = requests.post(signin_url, json=payload, headers=headers)
req.raise_for_status()

# Get the response
response = json.loads(req.content)

# Parse the response JSON. The response body will look similar
# to the following example:
#
# {
#    "credentials": {
#        "site": {
#            "id": "xxxxxxxxxx-xxxx-xxxx-xxxxxxxxxx",
#            "contentUrl": ""
#        },
#        "user": {
#            "id": "xxxxxxxxxx-xxxx-xxxx-xxxxxxxxxx"
#        },
#         "token": "AUTH-TOKEN"
#    }
# }


# Get the authentication token from the <credentials> element
token = response["credentials"]["token"]

# Get the site ID from the <site> element
site_id = response["credentials"]["site"]["id"]

print('Sign in successful!')
print('\tToken: {token}'.format(token=token))
print('\tSite ID: {site_id}'.format(site_id=site_id))

# Set the authentication header using the token returned by the Sign In method.
headers['X-tableau-auth']=token


csv_req = requests.get(csv_url, headers=headers)
# ... Make other calls here ...

csv_req.raise_for_status() 
print(csv_req.status_code) #200

with open('something.csv', 'wb') as f:
  f.write(csv_req.content)

This passes the authentication issue but I'm getting page source in the response as opposed to CSV file

page source:

<!DOCTYPE html><html xmlns:ng="" xmlns:tb=""><head ng-csp><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=1024, maximum-scale=2"><meta name="format-detection" content="telephone=no"><meta name="vizportal-config" data-buildId="6m6iytne3n8" data-staticAssetsUrlPrefix=""><link rel="stylesheet" type="text/css" href="vizportal.css?6m6iytne3n8"><script src="/javascripts/api/tableau-2.2.2.min.js?6m6iytne3n8"></script><script src="vizportalMinLibs.js?6m6iytne3n8"></script><script src="vizportal.min.js?6m6iytne3n8"></script></head><body class="tb-body"><div ng-app="VizPortalRun" id="ng-app" tb-window-resize class="tb-app"><div ui-view="" class="tb-app-inner"></div><tb:react-toaster></tb:react-toaster><script type="text/ng-template" id="inline_stackedElement.html"><div tb-window-resize tb-left="left" tb-top="top" tb-right="right" tb-bottom="bottom" tb-visible="visible" class="tb-absolute"></div></script><tb:stacked-elements></tb:stacked-elements></div></body></html>

It'll be extremely helpful if anyone can please guide me how to get the text/csv response as opposed to html.