从Tableau地图中抓取数据。

3

我正在为关于阿片类药物危机的研究项目,试图获取伊利诺伊州纳洛酮分销中心的位置和名称。

这个由Tableau生成的仪表板可以从公共卫生部门的以下链接处访问: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.br{tableauData["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个回答

6

编辑

我已经制作了一个 Tableau 爬虫库 以将工作表数据提取到 pandas 数据帧中。

这段代码更简单,但在您的情况下,您仍然需要使用 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)

在 repl.it 上尝试此链接


原始帖子

它有点复杂,因为它们组合了以下几个方面:

  • tableau“配置页面”,其中包含 tsconfig 文本区域不是原始页面的一部分。URL 是从一些 param HTML 标签动态构建的。
  • 它在 cookie 中使用了一个跨站请求伪造令牌,但是为了获取该 cookie,您需要调用特定的 API,其 URL 是从一些 param HTML 标签动态构建的。
  • tsconfig 参数中,我们可以像你在其他 stackoverflow 帖子中发现的那样构建数据 URL,例如这个这个这个

流程如下:

  • 调用 GET https://idph.illinois.gov/OpioidDataDashboard/,抓取 class 为 tableauPlaceholder 的 div 下的 param 标签。

从那里开始,主机是:https://interactive.data.illinois.gov

  • from the former param tags, build the "session URL" which looks like this :

    GET /trusted/{ticket}/t/DPH/views/opioidTDWEB_prod/MortalityandMorbidity
    

上面的URL将仅用于存储Cookie(包括Cookie中的xsrf标记)。

  • from the former param tags, build the "configuration URL" which looks like this :

    GET /t/DPH/views/opioidTDWEB_prod/MortalityandMorbidity
    
提取id为tsConfigContainer的文本区域并解析其中的json数据。
  • build the "data url" from the json extracted above, the url looks like this :

    POST /vizql/t/DPH/w/opioidTDWEB_prod/v/MortalityandMorbidity/bootstrapSession/sessions/{session_id}
    

然后你会得到一个带有一些字符串的json响应,以防止json劫持。您需要使用正则表达式提取它,然后解析大量的JSON数据。

所有需要的URL将类似于:

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"])

在 repl.it 上尝试此代码


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接