使用 Python 访问 HTML <script> 标记内的深层嵌套数据

Posted

技术标签:

【中文标题】使用 Python 访问 HTML <script> 标记内的深层嵌套数据【英文标题】:Access deeply nested data inside HTML <script> tag with Python 【发布时间】:2020-02-25 12:34:13 【问题描述】:

所以我正在尝试从具有深度嵌套的 &lt;script&gt; 标记的站点中获取特定数据。

使用import json,希望尝试使事情变得更容易,导致著名的Expecting value: line 1 column 1 (char 0) 错误。所以,我尝试了以下方法1,但成功率为零。

本质上,连接到站点的相对简单的步骤,捕捉特定的&lt;script&gt;标签是没有问题的。从中获取我需要的数据似乎有问题。

假设以下元素:

script_tag = '''
<script id="startup" type="text/javascript">
$(document).ready(function () createJsonChart(
"series":["name":"BNames","color":"#0043de","legendIndex":0,
"stack":null,
"data":["name":"BNames","color":"#0043de","y":0.0,
"legendIndex":0,
"events":"click":function()return false;,
"subtotal":0.0,"displayValue":"0","tooltip":"",
"name":"BNames","color":"#0043de","y":114.6,
"legendIndex":0,
"events":"click":function()return false;,
"subtotal":0.0,"displayValue":"0",
"tooltip":"BNames: 114,60 % <br/> Month: oktober 2018",
"name":"BNames","color":"#0043de","y":108.5,
"legendIndex":0,
"events":"click":function()return false;,
"subtotal":0.0,"displayValue":"0",
"tooltip":"BNames: 108,50 % <br/> Month: september 2019",
"name":"BNames","color":"#0043de","y":0.0,
"legendIndex":0,
"events":"click":function()return false;,
"subtotal":0.0,"displayValue":"0","tooltip":""],
"type":"line","marker":"enabled":false,
"linecolor":null,"lineWidth":0,
"fillColor":null,"symbol":null,"radius":4,
"dashStyle":"Solid","lineWidth":2,
"step":"center","zIndex":"2","name":"Mandatory","color":"#f20808",
"legendIndex":0,"stack":1,
"data":["name":"Mandatory","color":"#f20808","y":104.1,
"legendIndex":0,
"events":"click":function()return false;,"subtotal":0.0,"displayValue":"0",
"tooltip":"Mandatory: 104,10 %: 104,10 %",
"name":"Mandatory","color":"#f20808","y":104.1,
"legendIndex":0,
"events":"click":function()return false;,
"subtotal":0.0,"displayValue":"0",
"tooltip":"Mandatory: 104,10 %",
"name":"Mandatory","color":"#f20808","y":104.1,
"legendIndex":0,
"events":"click":function()return false;,
"subtotal":0.0,"displayValue":"0",
"tooltip":"Mandatory: 104,10 %"],
"type":"line","marker":"enabled":false,
"linecolor":null,"lineWidth":0,"fillColor":null,
"symbol":null,"radius":4,"dashStyle":"Solid","lineWidth":2,
"step":"center", "zIndex":"2","name":"Preferred","color":"#38d615",
"legendIndex":0,"stack":2,
"data":["name":"Preferred","color":"#38d615","y":121.0,
"legendIndex":0,
"events":"click":function()return false;,"subtotal":0.0,"displayValue":"0",
"tooltip":"Preferred: 121,00 %: 121,00 %",
"name":"Preferred","color":"#38d615","y":121.0,
"legendIndex":0,
"events":"click":function()return false;,"subtotal":0.0,"displayValue":"0",
"tooltip":"Preferred: 121,00 %",
"name":"Preferred","color":"#38d615","y":121.0,
"legendIndex":0,
"events":"click":function()return false;,"subtotal":0.0,"displayValue":"0",
"tooltip":"Preferred: 121,00 %"]],
"resizeElement":null,"credits":"enabled":false);$('#__Page').lumnaInit(''););
</script>
'''

实际上这个&lt;script&gt; 标签更大。它包含 3 部分数据,此处命名为 BNamesMandatoryPreferred。我需要来自BNames 的数据,特别是最后一个条目。因此,预期结果将来自"tooltip":"BNames: 108,50 % &lt;br/&gt; Month: september 2019" 部分,其中BNames: 108,50 % 在一个变量中,Month: september 2019 在另一个变量中。

使用正则表达式回答

url_part=soup.find("script", attrs='id':'startup').text
info=re.findall(r'\s\w*\s\d*', url_part)[-1]
result=re.findall(r'(BNames: (\d+[,]\d+\s[%]))', url_part)[-1][1]

首先定义要处理的 html 标记。其次,查找所有出现的实例,其中包含任意大小的字母 (\w*),后跟空格 (\s) 和任意大小的数字 (\d*)。这与 2019 年 9 月或 2019 年 8 月之类的任何内容相匹配。最后,查找与 BNames: 匹配的实例以及此设置中的数字:数字、逗号、数字、空格和百分号。因此(\d+[,]\d+\s[%] 这确实匹配从 80,6 % 到 120,05 % 的所有内容

【问题讨论】:

不用深入,用正则搜索脚本标签内的文本,我不喜欢用抓取功能来处理javascript标签,正则表达式更快.我已经在这里回答了这个javascript-scrape 【参考方案1】:

Beleidsdekkingsgraad 字符串上使用以下正则表达式匹配。 BNames 的想法相同。

import re, requests

r = requests.get('https://www.pensioenfondstno.nl/overons/dekkingsgraad')
p = re.compile(r'"(Beleidsdekkingsgraad:[\s\S]*?)"', re.MULTILINE)
data = p.findall(r.text)[-1].split(' <br/> ')
print(data[0])
print(data[1])

正则表达式:

【讨论】:

好吧,QHarr 非常接近,并且对样本数据非常有效。然而,在实际场景中,它捕获了太多必要的信息。这真的是一件很烦人的事情。所以,按照 Linh Nguyen 的建议,看看正则表达式。所以我做了。最终代码已添加到我的问题中。虽然它不像你的那么整洁,但它确实有效。如果您有任何改进代码的建议,我会全力以赴。 能否提供源码? source url。您应该在此处看到 BNames 为 Beleidsdekkingsgraad 和 Month 在原始设置中为 Periode 你只想要这两个值吗? 正则表达式的强大背后有一些美妙之处。测试它并完美运行。因此接受您的答案作为解决方案。

以上是关于使用 Python 访问 HTML <script> 标记内的深层嵌套数据的主要内容,如果未能解决你的问题,请参考以下文章

嵌套 JSON 对象中的数组使用 for-in 来循环访问每个数组

Spring Boot 访问静态资源缺少 scr/main/resources

javascript基本语法

使用 Python 访问 HTML <script> 标记内的深层嵌套数据

R:如何使用 dplyr(函数 scr_postgres)从 redshift 中的模式中选择表?

安卓手机屏幕录像之scr