【问题标题】:Python - How can I scrape with bs4 a javascript code)?Python - 我如何使用 bs4 抓取 javascript 代码)?
【发布时间】:2019-05-18 07:14:47
【问题描述】:

所以我一直在尝试从 html 中提取一个 javascript 值。代码中有很多 javascript,但我只想能够打印出这个:

var spConfig=newProduct.Config({
  "attributes": {
    "531": {
      "id": "531",
      "options": [
        {
          "id": "18",
          "hunter": "0",
          "products": [
            "128709"
          ]
        },
        {
          "label": "40 1\/2",
          "hunter": "0",
          "products": [
            "120151"
          ]
        },
        {
          "id": "33",
          "hunter": "0",
          "products": [
            "120152"
          ]
        },
        {
          "id": "36",
          "hunter": "0",
          "products": [
            "128710"
          ]
        },
        {
          "id": "42",
          "hunter": "0",
          "products": [
            "125490"
          ]
        }
      ]
    }
  },

  "Id": "120153",

});

所以我开始编写如下代码:

test = bs4.find_all('script', {'type': 'text/javascript'})
print(test)

我得到的输出非常大,所以我不能在这里全部发布,但其中之一是我在顶部提到的 javascript,我只想打印出 var spConfig=newProduct.Config

我怎样才能做到这一点,以便能够打印出var spConfig=newProduct.Config....,然后我可以使用 json.loads 将其转换为 json,以便我以后可以更轻松地抓取它?

对于任何问题或我没有很好解释的事情。我会在评论中欣赏所有我可以在stackoverflow中提高自己的东西! :)

编辑:

bs4 为 javascripts 打印的更多示例

<script type="text/javascript">varoptionsPrice=newProduct.Options({
  "priceFormat": {
    "pattern": "%s\u00a0\u20ac",
    "precision": 2,
    "requiredPrecision": 2,
    "decimalSymbol": ",",
    "groupSymbol": "\u00a0",
    "groupLength": 3,
    "integerRequired": 1
  },
  "showBoths": false,
  "idSuffix": "_clone",
  "skipCalculate": 1,
  "defaultTax": 20,
  "currentTax": 20,
  "tierPrices": [

  ],
  "tierPricesInclTax": [

  ],
  "swatchPrices": null
});</script>,
<script type="text/javascript">var spConfig=newProduct.Config({
  "attributes": {
    "531": {
      "id": "531",
      "options": [
        {
          "id": "18",
          "hunter": "0",
          "products": [
            "128709"
          ]
        },
        {
          "label": "40 1\/2",
          "hunter": "0",
          "products": [
            "120151"
          ]
        },
        {
          "id": "33",
          "hunter": "0",
          "products": [
            "120152"
          ]
        },
        {
          "id": "36",
          "hunter": "0",
          "products": [
            "128710"
          ]
        },
        {
          "id": "42",
          "hunter": "0",
          "products": [
            "125490"
          ]
        }
      ]
    }
  },

  "Id": "120153"
});</script>,
<scripttype="text/javascript">document.observe('dom:loaded',
function(){
  varswatchesConfig=newProduct.ConfigurableSwatches(spConfig);
});</script>

编辑更新 2:

try:
    product_li_tags = bs4.find_all('script', {'type': 'text/javascript'})
except Exception:
    product_li_tags = []


for product_li_tag in product_li_tags:
   try:
        pat = "product.Config\((.+)\);"
        json_str = re.search(pat, product_li_tag, flags=re.DOTALL).group(1)
        print(json_str)
   except:
       pass

#json.loads(json_str)
print("Nothing")
sys.exit()

【问题讨论】:

标签: python beautifulsoup


【解决方案1】:

我可以想到 3 个可能的选项 - 您使用哪一个可能取决于项目的规模以及您需要它的灵活性

  • 使用正则表达式从脚本中提取对象(最快,最不灵活)

  • 使用ANTLR或类似(如pyjsparser)解析js语法

  • 使用 Selenium 或其他可以为您解释 JS 的无头浏览器。使用此选项,您可以使用 selenium 执行调用以获取变量 like this

  • 的值

正则表达式示例(#1)

>>> script_body = """
    var x=product.Config({
        "key": {"a":1}
});
"""
>>> pat = "product.Config\((.+)\);"
>>> json_str = re.search(pat, script_body, flags=re.DOTALL).group(1)
>>> json.loads(json_str)
{'key': {'a': 1}}
>>> json.loads(json_str)['key']['a']
1

【讨论】:

  • 哦,嗯。因为当我抓取整个 html.parse 时,值就在那里。该值在script type="'text/javascript" 内。我的想法是,也许可以像通常使用 bs4 那样刮掉它,但我认为这是不可能的?
  • 有什么方法可以从测试变量中发布更多输出?只是为了看看到底是什么结构。
  • AFAIK bs4 将无法解释 JS 语法,因此作为字符串的脚本主体是尽可能多的。
  • @gtalarico 我刚刚看到你的例子,我想我可以试一试,但我不知道 json_str 中的“s”代表什么?
  • 对不起,这是示例中的拼写错误 - sscript_body 我已经更正了示例
【解决方案2】:

您可以使用.text 函数来获取每个标签内的内容。然后,如果您知道要获取专门以“varoptionsPrice”开头的代码,则可以对其进行过滤:

soup = BeautifulSoup(myhtml, 'lxml')

script_blocks = soup.find_all('script', {'type': 'text/javascript'})
special_code = ''
for s in script_blocks:
    if s.text.strip().startswith('varOptionsPrice'):
        special_code = s.text
        break

print(special_code)

编辑:要在 cmets 中回答您的问题,有几种不同的方法可以提取具有 JSON 的文本部分。您可以通过正则表达式传递它以获取第一个左括号之间和最后 ); 之前的所有内容。虽然如果你想完全避免正则表达式,你可以这样做:

json_stuff = special_code[special_code.find('(')+1:special_code.rfind(')')]

然后用它制作一个可用的字典:

import json
j = json.loads(json_stuff)
print(j['defaultTax'])  # This should return a value of 20

【讨论】:

  • 是的!就在这里!!!但是,如果我们想将 s.text 创建为 json,如果它当然找到了呢?
  • @Hellosiroverthere,我已更新我的 cmets 以回答 JSON 部分。
猜你喜欢
  • 1970-01-01
  • 2013-12-04
  • 2019-03-08
  • 2021-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多