从 Tableau 地图中抓取数据
Posted
技术标签:
【中文标题】从 Tableau 地图中抓取数据【英文标题】:Scraping Data from a Tableau Map 【发布时间】:2020-11-11 11:16:57 【问题描述】:我正在尝试获取伊利诺伊州纳洛酮配送中心的位置和名称,以开展关于阿片类药物危机的研究项目。
公共卫生部https://idph.illinois.gov/OpioidDataDashboard/可以从这里访问这个由画面生成的仪表板
我已经尝试了我能找到的一切。首先使用 Tableau 的界面更改 url 以“下载”数据。那只能让我下载一个 pdf 地图,而不是它背后的实际数据集。其次,我修改了我在 Stack Overflow 上见过几次的 python 脚本来尝试请求数据。但是,我认为它遇到了某种错误。代码如下。
url = "https://interactive.data.illinois.gov/t/DPH/views/opioidTDWEB_prod/NaloxoneDistributionLocations"
r = requests.get(
url,
params=
":embed":"y",
":showAppBanner":"false",
":showShareOptions":"true",
":display_count":"no",
"showVizHome": "no"
)
soup = BeautifulSoup(r.text, "html.parser")
print(soup)
tableauData = json.loads(soup.find("textarea","id": "tsConfigContainer").text)
dataUrl = f'https://tableau.ons.org.brtableauData["vizql_root"]/bootstrapSession/sessions/tableauData["sessionid"]'
r = requests.post(dataUrl, data=
"sheet_id": tableauData["sheetId"],
)
dataReg = re.search('\d+;(.*)\d+;(.*)', r.text, re.MULTILINE)
info = json.loads(dataReg.group(1))
data = json.loads(dataReg.group(2))
print(data["secondaryInfo"]["presModelMap"]["dataDictionary"]["presModelHolder"]["genDataDictionaryPresModel"]["dataSegments"]["0"]["dataColumns"])
感谢任何帮助。
【问题讨论】:
【参考方案1】:编辑
我创建了一个tableau scraper library 来将工作表数据提取到熊猫数据框中。
代码更简单,但在您的情况下,您仍然需要使用 xsrf 令牌构建 URL:
from tableauscraper import TableauScraper as TS
import requests
from bs4 import BeautifulSoup
init_url = "https://idph.illinois.gov/OpioidDataDashboard/"
r = requests.get(init_url)
soup = BeautifulSoup(r.text, "html.parser")
paramTags = dict([
(t["name"], t["value"])
for t in soup.find("div", "class": "tableauPlaceholder").findAll("param")
])
url = f'paramTags["host_url"]trusted/paramTags["ticket"]paramTags["site_root"]/views/paramTags["name"]'
ts = TS()
ts.loads(url)
dashboard = ts.getWorkbook()
for t in dashboard.worksheets:
# show worksheet name
print(f"WORKSHEET NAME : t.name")
# show dataframe for this worksheet
print(t.data)
Try this on repl.it
原帖
这有点复杂,因为有以下几种组合:
存在 tsconfig textarea 的画面“配置页面”不是原始页面的一部分。该 url 是从一些param
html 标签动态构建的
它在 cookie 中使用了一个交叉伪造令牌,但为了获取该 cookie,您需要调用一个特定的 api,其 url 是从一些 param
html 标记动态构建的
通过 tsconfig 参数,我们可以构建数据 url,正如您在其他 *** 帖子中找到的那样,例如 this、this 和 this
流程如下:
调用GET https://idph.illinois.gov/OpioidDataDashboard/
,在类tableauPlaceholder
的div下抓取param
标签
从那里主机是:https://interactive.data.illinois.gov
从以前的param
标签,构建如下所示的“会话 URL”:
GET /trusted/ticket/t/DPH/views/opioidTDWEB_prod/MortalityandMorbidity
上面的 url 将仅用于存储 cookie(包括 cookie 中的 xsrf 令牌)
从之前的 param
标签,构建如下所示的“配置 URL”:
GET /t/DPH/views/opioidTDWEB_prod/MortalityandMorbidity
提取id为tsConfigContainer
的textarea并从中解析json
从上面提取的 json 中构建“数据 url”,url 如下所示:
POST /vizql/t/DPH/w/opioidTDWEB_prod/v/MortalityandMorbidity/bootstrapSession/sessions/session_id
然后你有一个 json 响应,前面有一些字符串以防止json hijacking。你需要正则表达式来提取它,然后解析巨大的 json 数据
所需的所有网址都是这样的:
GET https://idph.illinois.gov/OpioidDataDashboard/
GET https://interactive.data.illinois.gov/trusted/yIm7jkXyRQuH9Ff1oPvz_w==:790xMcZuwmnvijXHg6ymRTrU/t/DPH/views/opioidTDWEB_prod/MortalityandMorbidity
GET https://interactive.data.illinois.gov/t/DPH/views/opioidTDWEB_prod/MortalityandMorbidity
POST https://interactive.data.illinois.gov/vizql/t/DPH/w/opioidTDWEB_prod/v/MortalityandMorbidity/bootstrapSession/sessions/2A3E3BA96A6C4E65B36AEDB4A536D09F-1:0
完整代码:
import requests
from bs4 import BeautifulSoup
import json
import re
s = requests.Session()
init_url = "https://idph.illinois.gov/OpioidDataDashboard/"
print(f"GET init_url")
r = s.get(init_url)
soup = BeautifulSoup(r.text, "html.parser")
paramTags = dict([
(t["name"], t["value"])
for t in soup.find("div", "class":"tableauPlaceholder").findAll("param")
])
# get xsrf cookie
session_url = f'paramTags["host_url"]trusted/paramTags["ticket"]paramTags["site_root"]/views/paramTags["name"]'
print(f"GET session_url")
r = s.get(session_url)
config_url = f'paramTags["host_url"][:-1]paramTags["site_root"]/views/paramTags["name"]'
print(f"GET config_url")
r = s.get(config_url,
params =
":embed": "y",
":showVizHome": "no",
":host_url": "https://interactive.data.illinois.gov/",
":embed_code_version": 2,
":tabs": "yes",
":toolbar": "no",
":showShareOptions": "false",
":display_spinner": "no",
":loadOrderID": 0,
)
soup = BeautifulSoup(r.text, "html.parser")
tableauData = json.loads(soup.find("textarea","id": "tsConfigContainer").text)
dataUrl = f'paramTags["host_url"][:-1]tableauData["vizql_root"]/bootstrapSession/sessions/tableauData["sessionid"]'
print(f"POST dataUrl")
r = s.post(dataUrl, data=
"sheet_id": tableauData["sheetId"],
)
dataReg = re.search('\d+;(.*)\d+;(.*)', r.text, re.MULTILINE)
info = json.loads(dataReg.group(1))
data = json.loads(dataReg.group(2))
print(data["secondaryInfo"]["presModelMap"]["dataDictionary"]["presModelHolder"]["genDataDictionaryPresModel"]["dataSegments"]["0"]["dataColumns"])
Try this on repl.it
【讨论】:
以上是关于从 Tableau 地图中抓取数据的主要内容,如果未能解决你的问题,请参考以下文章
使用 python 和 Beautifulsoup4 从抓取数据中写入和保存 CSV 文件