爬虫请求返回「必须提供查询字符串」错误的原因排查
爬取Sayurbox GraphQL接口报错问题解决
目标页面
https://www.sayurbox.com/category/vegetables-1-a0d03d59?selectedCategoryType=ops&touch_point=screen_CATEGORY_sembako-1-e6a33b51§ion_source=shop_list_slider_navigation_category_vegetables-1-a0d03d59
原代码
dcID="RGVsaXZlcnlDb25maWc6VGh1cnNkYXksIDA5IEZlYnJ1YXJ5IDIwMjN8SkswMXxTRDI5fGZhbHNl" slugcat="vegetables-1-a0d03d59" url="https://www.sayurbox.com/graphql/v1?deduplicate=1" payload={"operationName":"getCartItemCount", "variables":{"deliveryConfigId":DCId}, "query":"query getCartItemCount($deliveryConfigId: ID!) { cart(deliveryConfigId: $deliveryConfigId) { id count __typename } }"},{"operationName":"getProducts", "variables":{"deliveryConfigId":DCId, "sortBy":"related_product", "isInstantDelivery":False, "slug":slugcat, "first":12, "abTestFeatures":[]}, "query":"query getProducts($deliveryConfigId: ID!, $sortBy: CatalogueSortType!, $slug: String!, $after: String, $first: Int, $isInstantDelivery: Boolean, $abTestFeatures: [String!]) { productsByCategoryOrSubcategoryAndDeliveryConfig( deliveryConfigId: $deliveryConfigId sortBy: $sortBy slug: $slug after: $after first: $first isInstantDelivery: $isInstantDelivery abTestFeatures: $abTestFeatures ) { edges { node { ...ProductInfoFragment __typename } __typename } pageInfo { hasNextPage endCursor __typename } productBuilder __typename } } fragment ProductInfoFragment on Product { id uuid deliveryConfigId displayName priceRanges priceMin priceMax actualPriceMin actualPriceMax slug label isInstant isInstantOnly nextDayAvailability heroImage promo discount isDiscount variantType imageIds isStockAvailable defaultVariantSkuCode quantitySoldFormatted promotion { quota isShown campaignId __typename } productVariants { productVariant { id skuCode variantName maxQty isDiscount stockAvailable promotion { quota campaignId isShown __typename } __typename } pageInfo { hasPreviousPage hasNextPage __typename } __typename } __typename }"} response=requests.get(url,headers=headers,json=payload) response.json()
错误返回结果
[{'errors': [{'message': 'Must provide query string.', 'extensions': {'timestamp': 1675842901472}}]}, {'errors': [{'message': 'Must provide query string.', 'extensions': {'timestamp': 1675842901472}}]}]
问题排查与修复
1. 请求方法错误
GraphQL接口几乎都使用POST请求接收JSON格式的请求体,你用了requests.get(),GET请求不会将json参数放入请求体,服务端无法获取到query内容,直接返回"Must provide query string."错误。
2. 变量名大小写不匹配
代码中定义的变量是dcID,但payload里写的是DCId,Python是大小写敏感的,这会导致传递的deliveryConfigId值错误(甚至可能抛出未定义错误)。
3. 批量请求格式(可选)
如果要发送批量GraphQL请求,需要将payload包装为一个列表,但要确认目标服务端支持批量请求;如果不需要同时获取购物车数量和商品列表,可以拆分请求单独发送。
修正后的代码示例
import requests dcID = "RGVsaXZlcnlDb25maWc6VGh1cnNkYXksIDA5IEZlYnJ1YXJ5IDIwMjN8SkswMXxTRDI5fGZhbHNl" slugcat = "vegetables-1-a0d03d59" url = "https://www.sayurbox.com/graphql/v1?deduplicate=1" # 商品列表请求payload products_payload = { "operationName": "getProducts", "variables": { "deliveryConfigId": dcID, "sortBy": "related_product", "isInstantDelivery": False, "slug": slugcat, "first": 12, "abTestFeatures": [] }, "query": """query getProducts($deliveryConfigId: ID!, $sortBy: CatalogueSortType!, $slug: String!, $after: String, $first: Int, $isInstantDelivery: Boolean, $abTestFeatures: [String!]) { productsByCategoryOrSubcategoryAndDeliveryConfig( deliveryConfigId: $deliveryConfigId sortBy: $sortBy slug: $slug after: $after first: $first isInstantDelivery: $isInstantDelivery abTestFeatures: $abTestFeatures ) { edges { node { ...ProductInfoFragment __typename } __typename } pageInfo { hasNextPage endCursor __typename } productBuilder __typename } } fragment ProductInfoFragment on Product { id uuid deliveryConfigId displayName priceRanges priceMin priceMax actualPriceMin actualPriceMax slug label isInstant isInstantOnly nextDayAvailability heroImage promo discount isDiscount variantType imageIds isStockAvailable defaultVariantSkuCode quantitySoldFormatted promotion { quota isShown campaignId __typename } productVariants { productVariant { id skuCode variantName maxQty isDiscount stockAvailable promotion { quota campaignId isShown __typename } __typename } pageInfo { hasPreviousPage hasNextPage __typename } __typename } __typename }""" } # 使用POST请求,传入headers(确保headers包含正确的Content-Type等信息) response = requests.post(url, headers=headers, json=products_payload) print(response.json())
内容的提问来源于stack exchange,提问作者Hal
相关产品推荐
相关产品推荐

