【问题标题】:Extract data from <script> BeautifulSoup Python从 <script> BeautifulSoup Python 中提取数据
【发布时间】:2021-04-28 16:27:35
【问题描述】:

我有这个代码:

product_url = 'https://www.burton.com/us/en/p/burton-elite-long-sleeve-tshirt/W21-203921.html?cgid=womens-tees'
res = requests.get(product_url, headers=headers)
soup = BeautifulSoup(res.text, 'html.parser')
product = soup.find('main', {'id': 'main-content'})
details = product.find('script')
data = json.loads(details.string)

给出这个输出:

<script>
        __metadata.product = {
            id: "W21-203921",
            sku: "W21-203921",
            ph1: 'SOFTGOODS',
            ph2: 'BASIC FLEECE AND TEE',
            ph3: 'LS TEES',
            ph4: '',
            upc: '190450612509',
            ean: '9009521451408',
            brand: "Burton",
            category: "womens-tees",
            primaryCategory: "womens-sale-sweaters-shirts",
            currency: "USD",
            gender: "Unisex",
            label: "Burton Elite Long Sleeve T-Shirt",
            name: "Burton Elite Long Sleeve T-Shirt"
        };
        __metadata.criteo = {
            pageType: 'ProductPage'
        };
    </script>

现在我想提取其中的一些数据,例如 id、品牌、类别和名称。

我已经查看了这个论坛上几乎每个主题都有非常相似的问题,并尝试了他们的解决方案,但没有任何效果。他们中的大多数人以各种方式按照 data = json.loads(details) 的方式做一些事情,但它们似乎都不起作用。我得到的最常见的错误是:

json.decoder.JSONDecodeError: Expecting value: line 2 column 9 (char 9)

TypeError: the JSON object must be str, bytes or bytearray, not Tag

【问题讨论】:

    标签: python json beautifulsoup


    【解决方案1】:

    仅从 ajax 格式获取数据会更容易、更健壮。只需将其添加到 params 参数中即可。然后你可以从 json 格式/字典中取出你想要的任何东西。也适用于您在 cmets 中提供的其他网址。

    import requests
    
    url = 'https://www.burton.com/us/en/p/burton-elite-long-sleeve-tshirt/W21-203921.html?cgid=womens-tees'
    payload = {'format':'ajax'}
    
    jsonData = requests.get(url, params=payload).json()
    

    输出:

    print(jsonData['data']['products'][0])
    {'id': 'W21-203921', 'hideOutOfStockVariants': True, 'brand': 'Burton', 'name': 'Burton Elite Long Sleeve T-Shirt', 'subtitle': '100% Organic Cotton Long Sleeve Graphic T Shirt', 'shortDescription': "A comfortable long sleeve T-shirt that's an unsung favorite for social hour and Sunday in the park.", 'gender': 'Unisex', 'season': 'W21', 'isBoard': False, 'hasSizeChart': True, 'hasSizeFinder': False, 'selectedVariations': {'variationColor': '', 'variationSize': ''}, 'links': {'master': 'https://www.burton.com/us/en/p/burton-elite-long-sleeve-tshirt/W21-203921.html', 'variations': '/on/demandware.store/Sites-Burton_NA-Site/en_US/Product-GetVariationJSON?pid=W21-203921', 'manual': '/us/en/help/manuals.html', 'yotpoAPI': 'https://api.yotpo.com/v1/widget/AbBl1exDWS4rzXsg73rzUKlzUOo10aeMXRkIGHVG/products/W21-203921/reviews?per_page=0', 'tech': '/on/demandware.store/Sites-Burton_NA-Site/en_US/Product-GetTechFeaturesJSON?pids=W21-203921', 'recommendations': '/on/demandware.store/Sites-Burton_NA-Site/en_US/Product-GetRecommendationsJSON?pids=W21-203921', 'ultimateSetup': '/on/demandware.store/Sites-Burton_NA-Site/en_US/Product-GetRecommendationsJSON?pids=W21-203921', 'dynamicslots': '/on/demandware.store/Sites-Burton_NA-Site/en_US/Slot-GetDynamicSlots?pid=W21-203921'}, 'variationValueCount': {'variationColor': 4, 'variationSize': 7}, 'finePrint': [], 'images': {'type': 'PRODUCT_LEVEL', 'views': [{'id': '_4U', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392102001_4U.png'}}, {'id': '_3W', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392102001_3W.png'}}, {'id': '_4M', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392102001_4M.png'}}, {'id': '_5M', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392102001_5M.png'}}, {'id': '_6W', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392102001_6W.png'}}, {'id': '_1', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392102001_1.png'}}], 'variationImageData': [{'variationColorID': '20392102001', 'display': {'category': {'primary': '_4U', 'focus': '_1'}}, 'views': [{'id': '_4U', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392102001_4U.png'}}, {'id': '_3W', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392102001_3W.png'}}, {'id': '_4M', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392102001_4M.png'}}, {'id': '_5M', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392102001_5M.png'}}, {'id': '_6W', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392102001_6W.png'}}, {'id': '_1', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392102001_1.png'}}]}, {'variationColorID': '20392102300', 'display': {'category': {'primary': '_4U', 'focus': '_1'}}, 'views': [{'id': '_4U', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392102300_4U.png'}}, {'id': '_3M', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392102300_3M.png'}}, {'id': '_4W', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392102300_4W.png'}}, {'id': '_5M', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392102300_5M.png'}}, {'id': '_6W', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392102300_6W.png'}}, {'id': '_1', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392102300_1.png'}}]}, {'variationColorID': '20392103200', 'display': {'category': {'primary': '_4'}}, 'views': [{'id': '_4', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392103200_4.png'}}, {'id': '_3', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392103200_3.png'}}, {'id': '_5', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392103200_5.png'}}, {'id': '_6', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392103200_6.png'}}]}, {'variationColorID': '20392103400', 'display': {'category': {'primary': '_3'}}, 'views': [{'id': '_3', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392103400_3.png'}}, {'id': '_4', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392103400_4.png'}}, {'id': '_5', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392103400_5.png'}}, {'id': '_6', 'active': True, 'type': 'image', 'masterLevel': False, 'template': None, 'format': None, 'url': {'base': 'https://www.burton.com/static/product/W21/20392103400_6.png'}}]}]}, 'ean': '9009521451408', 'upc': '190450612509', 'badges': '', 'category': 'womens-tees', 'primaryCategory': 'womens-sale-sweaters-shirts', 'hasUltimateSetup': False, 'ph1': 'SOFTGOODS', 'ph2': 'BASIC FLEECE AND TEE', 'ph3': 'LS TEES', 'ph4': '', 'videoID': '', 'videoPoster': '', 'videoVertical': '', 'spectrumObjects': False, 'scrollingText': False, 'cartSpecialCalloutMessage': False, 'disableEcommerce': False}
    

    更新:

    要获取价格和库存,您需要从第一个响应中提取产品 ID,然后发出新请求:

    import requests
    import pandas as pd
    
    urls = ['https://www.burton.com/us/en/p/burton-elite-long-sleeve-tshirt/W21-203921.html?cgid=womens-tees','https://www.burton.com/us/en/p/girls-burton-chicklet-flat-top-snowboard/W21-107341.html']
    payload = {'format':'ajax'}
    
    productID_list = []
    for url in urls:
        jsonData = requests.get(url, params=payload).json()
        productID = jsonData['data']['masterID']
        productID_list.append(productID)
    
    
    stock = []
    for productID in productID_list:
        prod_url = 'https://www.burton.com/on/demandware.store/Sites-Burton_NA-Site/en_US/Product-GetVariationJSON'
        payload = {'pid':productID,
                   'pricing':''}
        productData = requests.get(prod_url, params=payload).json()
        
        
        for each in productData['data']['variations']['variationValues']:
            row = {}
            row['name'] = each['name']
            row['color'] = each['variationColor']['displayName']
            row['size'] = each['variationSize']['displayName']
            row['standard_price'] = each['price']['standardPriceUnformatted']
            row['sale_price'] = each['price']['salePriceUnformatted']
            row['isOnSale'] = each['price']['isOnSale']
            row['available'] = each['status']['available']
            row['inStock'] = each['status']['meta']['type']
            
            stock.append(row)
        
    df = pd.DataFrame(stock)    
    

    输出:

    print (df.to_string())
                                             name          color size standard_price sale_price  isOnSale  available        inStock
    0            Burton Elite Long Sleeve T-Shirt     True Black    L          39.95                False       True       IN_STOCK
    1            Burton Elite Long Sleeve T-Shirt     True Black    M          39.95                False       True       IN_STOCK
    2            Burton Elite Long Sleeve T-Shirt     True Black    S          39.95                False       True       IN_STOCK
    3            Burton Elite Long Sleeve T-Shirt     True Black   XL          39.95                False       True       IN_STOCK
    4            Burton Elite Long Sleeve T-Shirt     True Black   XS          39.95                False       True       IN_STOCK
    5            Burton Elite Long Sleeve T-Shirt     True Black  XXL          39.95                False       True       IN_STOCK
    6            Burton Elite Long Sleeve T-Shirt     True Black  XXS          39.95                False       True       IN_STOCK
    7            Burton Elite Long Sleeve T-Shirt  Martini Olive    L          39.95                False       True       IN_STOCK
    8            Burton Elite Long Sleeve T-Shirt  Martini Olive    M          39.95                False       True       IN_STOCK
    9            Burton Elite Long Sleeve T-Shirt  Martini Olive    S          39.95                False       True       IN_STOCK
    10           Burton Elite Long Sleeve T-Shirt  Martini Olive   XL          39.95                False       True       IN_STOCK
    11           Burton Elite Long Sleeve T-Shirt  Martini Olive   XS          39.95                False       True       IN_STOCK
    12           Burton Elite Long Sleeve T-Shirt  Martini Olive  XXL          39.95                False       True       IN_STOCK
    13           Burton Elite Long Sleeve T-Shirt  Martini Olive  XXS          39.95                False       True       IN_STOCK
    14           Burton Elite Long Sleeve T-Shirt     True Penny    L          39.95      27.96      True       True       IN_STOCK
    15           Burton Elite Long Sleeve T-Shirt     True Penny    M          39.95      27.96      True       True       IN_STOCK
    16           Burton Elite Long Sleeve T-Shirt     True Penny    S          39.95      27.96      True      False      BACKORDER
    17           Burton Elite Long Sleeve T-Shirt     True Penny   XL          39.95      27.96      True       True       IN_STOCK
    18           Burton Elite Long Sleeve T-Shirt     True Penny   XS          39.95      27.96      True      False  NOT_AVAILABLE
    19           Burton Elite Long Sleeve T-Shirt     True Penny  XXL          39.95      27.96      True       True       IN_STOCK
    20           Burton Elite Long Sleeve T-Shirt     True Penny  XXS          39.95      27.96      True       True       IN_STOCK
    21           Burton Elite Long Sleeve T-Shirt     Lapis Blue    L          39.95      27.96      True      False  NOT_AVAILABLE
    22           Burton Elite Long Sleeve T-Shirt     Lapis Blue    M          39.95      27.96      True      False      BACKORDER
    23           Burton Elite Long Sleeve T-Shirt     Lapis Blue    S          39.95      27.96      True      False      BACKORDER
    24           Burton Elite Long Sleeve T-Shirt     Lapis Blue   XL          39.95      27.96      True      False  NOT_AVAILABLE
    25           Burton Elite Long Sleeve T-Shirt     Lapis Blue   XS          39.95      27.96      True       True       IN_STOCK
    26           Burton Elite Long Sleeve T-Shirt     Lapis Blue  XXL          39.95      27.96      True      False      BACKORDER
    27           Burton Elite Long Sleeve T-Shirt     Lapis Blue  XXS          39.95      27.96      True       True       IN_STOCK
    28  Girls' Burton Chicklet Flat Top Snowboard             80   80         199.95                False      False      BACKORDER
    29  Girls' Burton Chicklet Flat Top Snowboard             90   90         199.95                False      False      BACKORDER
    30  Girls' Burton Chicklet Flat Top Snowboard            100  100         199.95                False      False      BACKORDER
    31  Girls' Burton Chicklet Flat Top Snowboard            110  110         199.95                False      False      BACKORDER
    32  Girls' Burton Chicklet Flat Top Snowboard            115  115         199.95                False      False      BACKORDER
    33  Girls' Burton Chicklet Flat Top Snowboard            120  120         199.95                False       True       IN_STOCK
    34  Girls' Burton Chicklet Flat Top Snowboard            125  125         199.95                False       True       IN_STOCK
    35  Girls' Burton Chicklet Flat Top Snowboard            130  130         199.95                False       True       IN_STOCK
    

    【讨论】:

    • 好多了。您究竟是如何偶然发现这一点的?
    • 您可以在开发工具 --> 网络 --> XHR 下查看并在其中搜索请求和响应。您会在 url 中看到将 format=ajax 添加到 url
    • 明白了。我检查了 XHR,但没有看到任何明显的东西,所以我认为他们已经在服务器端烘焙了它。感谢您的回复。
    • 啊呀。我只是搜索了一些产品文本来找到它。
    • @chitown88 nvm 我明白了!谢谢你的帮助!
    【解决方案2】:

    我将以下答案留给后代,但this approach 更好。故事的寓意:检查 XHR 请求,看看您是否可以通过使用他们的 API 完全绕过字符串解析。


    正如我在评论中所写,您可以对这些数据做出许多不同的假设,并且可以使用同样多的策略来提取它。

    您使用哪种取决于许多因素:这种数据格式是否可能会改变?这是一次性的刮擦,还是您需要尽可能多地适应未来的修改?如果是后者,根据您对网站的了解,未来哪些修改最有可能?

    鉴于这些问题没有得到解决,我假设您只是想尽可能简单地将其解析为 dict,而无需做出各种面向未来的假设。

    你可以使用:

    import json
    import re
    
    chunk = re.search(r"\{[^}]+", html).group().replace("'", '"')
    data = json.loads(re.sub(r"(\w+):", r'"\1":', chunk) + "}")
    

    假设 JS 对象中没有大括号,字符串中没有冒号,等等。


    作为上述警告的一个例子,OP 已经回复说键 label: "Girls' Burton Chicklet Flat Top Snowboard", 破坏了正则表达式,因为它有一个 ' 被替换为未转义的 "

    可以通过假设' 后面没有" 在同一行上来解决此问题:

    chunk = re.sub(r'\'(?![^\n"]*")', '"', re.search(r"\{[^}]+", html).group())
    data = json.loads(re.sub(r"(\w+):", r'"\1":', chunk) + "}")
    

    ...但这只是将一组假设替换为另一组假设,并且很容易编造一个也打破这种模式的场景。如果用例正在抓取数百万个产品,那么几乎不可避免地会出现意料之外的情况,并且此处显示的模式需要进一步调整。这篇文章是一个概念验证,不能解释任意格式,所以它是读者进行进一步调整的练习。

    【讨论】:

    • 所以对于这个链接burton.com/us/en/p/girls-burton-chicklet-flat-top-snowboard/… 我收到以下错误,从我可以看到格式完全相同所以我不知道为什么。 json.decoder.JSONDecodeError: Expecting ',' delimiter: line 15 column 30 (char 460)
    • 您可以看到字符串中有一个' 字符:"Girls' Burton Chicklet Flat Top Snowboard",。这打破了正则表达式。正如我在评论和帖子中警告的那样,您必须在这里做出一些假设并愿意调整正则表达式以满足您的需求。我会更新正则表达式来处理这个问题,但是如果你不知道你的数据格式并且你正在处理潜在的任意数据,那么它就是一个无休止的敲打地鼠系列。我建议将答案视为概念证明,而不是“保证永远解析每个结构”的答案。远非如此。
    • 谢谢,不确定您是否更新了它,但它仍然无法正常工作。
    • 不,你可以看看上次编辑时间,我还没有更新。现在它已更新,但这将是最后一次更新。您可以将其用作概念证明以适应您的需求。
    • 我明白了。感谢您的帮助。
    猜你喜欢
    • 2019-05-02
    • 2020-06-06
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-31
    • 2021-07-07
    相关资源
    最近更新 更多