从 Tableau 地图中抓取数据

Tag*_*ano 3 python screen-scraping web-scraping tableau-api

我正在尝试提取伊利诺伊州纳洛酮配送中心的位置和名称,以用于阿片类药物危机的研究项目。

公共卫生部可从这里访问此表格生成的仪表板https://idph.illinois.gov/OpioidDataDashboard/

我已经尝试了我能找到的一切。首先更改 URL 以使用 Tableau 的界面“下载”数据。那只能让我下载 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"])

Run Code Online (Sandbox Code Playgroud)

感谢任何帮助。

Ber*_*tel 6

编辑

\n

我制作了一个tableau scraper 库来将工作表数据提取到 pandas 数据框中。

\n

代码更简单,但在您的情况下,您仍然需要使用 xsrf 令牌构建 URL:

\n
from tableauscraper import TableauScraper as TS\nimport requests\nfrom bs4 import BeautifulSoup\n\ninit_url = "https://idph.illinois.gov/OpioidDataDashboard/"\nr = requests.get(init_url)\nsoup = BeautifulSoup(r.text, "html.parser")\nparamTags = dict([\n    (t["name"], t["value"])\n    for t in soup.find("div", {"class": "tableauPlaceholder"}).findAll("param")\n])\n\nurl = f\'{paramTags["host_url"]}trusted/{paramTags["ticket"]}{paramTags["site_root"]}/views/{paramTags["name"]}\'\n\nts = TS()\nts.loads(url)\ndashboard = ts.getWorkbook()\n\nfor t in dashboard.worksheets:\n    # show worksheet name\n    print(f"WORKSHEET NAME : {t.name}")\n    # show dataframe for this worksheet\n    print(t.data)\n
Run Code Online (Sandbox Code Playgroud)\n

在 repl.it 上试试这个

\n
\n

原帖

\n

这有点复杂,因为有以下几种组合:

\n
    \n
  • 有 tsconfig textarea 的 tableau“配置页面”不是原始页面的一部分。url 是根据一些paramhtml 标签动态构建的
  • \n
  • 它在 cookie 中使用交叉伪造令牌,但为了获取该 cookie,您需要调用特定的 api,其 url 是从某些paramhtml 标签动态构建的
  • \n
  • 根据 tsconfig 参数,我们可以构建数据 url,正如您在其他 stackoverflow 帖子中发现的那样,例如thisthisthis
  • \n
\n

流程如下:

\n
    \n
  • 调用GET https://idph.illinois.gov/OpioidDataDashboard/,用 class 刮取paramdiv 下的标签tableauPlaceholder
  • \n
\n

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

\n\n

上面的url将仅用于存储cookies(包括cookies中的xsrf令牌)

\n\n

提取带有 id 的 textareatsConfigContainer并从中解析 json

\n
    \n
  • 从上面提取的 json 构建“数据 url”,该 url 如下所示:

    \n
    POST /vizql/t/DPH/w/opioidTDWEB_prod/v/MortalityandMorbidity/bootstrapSession/sessions/{session_id}\n
    Run Code Online (Sandbox Code Playgroud)\n
  • \n
\n

然后你就有了一个 json 响应,前面有一些字符串以防止json 劫持。你需要正则表达式来提取它,然后解析巨大的json数据

\n

所需的所有网址如下:

\n
GET https://idph.illinois.gov/OpioidDataDashboard/\nGET https://interactive.data.illinois.gov/trusted/yIm7jkXyRQuH9Ff1oPvz_w==:790xMcZuwmnvijXHg6ymRTrU/t/DPH/views/opioidTDWEB_prod/MortalityandMorbidity\nGET https://interactive.data.illinois.gov/t/DPH/views/opioidTDWEB_prod/MortalityandMorbidity\nPOST https://interactive.data.illinois.gov/vizql/t/DPH/w/opioidTDWEB_prod/v/MortalityandMorbidity/bootstrapSession/sessions/2A3E3BA96A6C4E65B36AEDB4A536D09F-1:0\n
Run Code Online (Sandbox Code Playgroud)\n

完整代码:

\n
import requests\nfrom bs4 import BeautifulSoup\nimport json\nimport re\n\ns = requests.Session()\n\ninit_url = "https://idph.illinois.gov/OpioidDataDashboard/"\nprint(f"GET {init_url}")\nr = s.get(init_url)\nsoup = BeautifulSoup(r.text, "html.parser")\nparamTags = dict([\n    (t["name"], t["value"]) \n    for t in soup.find("div", {"class":"tableauPlaceholder"}).findAll("param")\n])\n\n# get xsrf cookie\nsession_url = f\'{paramTags["host_url"]}trusted/{paramTags["ticket"]}{paramTags["site_root"]}/views/{paramTags["name"]}\'\nprint(f"GET {session_url}")\nr = s.get(session_url)\n\nconfig_url = f\'{paramTags["host_url"][:-1]}{paramTags["site_root"]}/views/{paramTags["name"]}\'\nprint(f"GET {config_url}")\nr = s.get(config_url,\n    params = {\n        ":embed": "y",\n        ":showVizHome": "no",\n        ":host_url": "https://interactive.data.illinois.gov/",\n        ":embed_code_version": 2,\n        ":tabs": "yes",\n        ":toolbar": "no",\n        ":showShareOptions": "false",\n        ":display_spinner": "no",\n        ":loadOrderID": 0,\n})\nsoup = BeautifulSoup(r.text, "html.parser")\ntableauData = json.loads(soup.find("textarea",{"id": "tsConfigContainer"}).text)\n\ndataUrl = f\'{paramTags["host_url"][:-1]}{tableauData["vizql_root"]}/bootstrapSession/sessions/{tableauData["sessionid"]}\'\nprint(f"POST {dataUrl}")\nr = s.post(dataUrl, data= {\n    "sheet_id": tableauData["sheetId"],\n})\ndataReg = re.search(\'\\d+;({.*})\\d+;({.*})\', r.text, re.MULTILINE)\ninfo = json.loads(dataReg.group(1))\ndata = json.loads(dataReg.group(2))\n\nprint(data["secondaryInfo"]["presModelMap"]["dataDictionary"]["presModelHolder"]["genDataDictionaryPresModel"]["dataSegments"]["0"]["dataColumns"])\n
Run Code Online (Sandbox Code Playgroud)\n

在 repl.it 上试试这个

\n