首页 > 解决方案 > 从 Kickstarter 项目中抓取文本不会返回任何内容

问题描述

我正在尝试从 Kickstarter 项目网页中抓取项目的主要文本。我有以下代码适用于第一个 URL,但不适用于第二个和第三个 URL。我想知道是否可以轻松修复我的代码而无需使用其他包?

url = "https://www.kickstarter.com/projects/1365297844/kuhkubus-3d-escher-figures?ref=discovery_staff_picks_category_newest"
#url = "https://www.kickstarter.com/projects/clarissaredwine/swingby-a-voyager-gravity-puzzle?ref=discovery_staff_picks_category_newest"
#url = "https://www.kickstarter.com/projects/100389301/us-army-navy-marines-air-force-special-challenge-c?ref=category"

page = requests.get(url)
soup = BeautifulSoup(page.content, 'html.parser')
body_text = soup.find(class_='rte__content')
all_text = body_text.find_all('p')
for i in all_text:
    print(i.get_text())

标签: htmlpython-3.xweb-scrapingbeautifulsoupgraphql

解决方案


这个网站使用了一个GraphQL API :

POST https://www.kickstarter.com/graph

我们可以使用它来获取站点数据,而不是为任何 URL(任何项目)抓取 html。此外,还有两个字段storyrisks我们将提取它们。

这个 Graphql API 需要一个嵌入在meta页面标签中的 csrf 令牌(任何页面都可以)。此外,我们需要使用请求会话存储 cookie,否则调用将失败。

以下是使用简单使用 API 的示例:

import requests
from bs4 import BeautifulSoup

s = requests.Session()
r = s.get("https://www.kickstarter.com")
soup = BeautifulSoup(r.text, 'html.parser')
xcsrf = soup.find("meta", {"name": "csrf-token"})["content"]

query = """
query GetEndedToLive($slug: String!) {
  project(slug: $slug) {
      id
      deadlineAt
      showCtaToLiveProjects
      state
      description
      url
      __typename
  }
}"""

r = s.post("https://www.kickstarter.com/graph",
    headers= {
        "x-csrf-token": xcsrf
    },
    json = {
        "query": query,
        "variables": {
            "slug":"kuhkubus-3d-escher-figures"
        }
    })

print(r.json())

从您的第二个链接中,它显示了查询中的有趣字段。完整的查询如下:

query Campaign($slug: String!) {
  project(slug: $slug) {
    id
    isSharingProjectBudget
    risks
    story(assetWidth: 680)
    currency
    spreadsheet {
      displayMode
      public
      url
      data {
        name
        value
        phase
        rowNum
        __typename
      }
      dataLastUpdatedAt
      __typename
    }
    environmentalCommitments {
      id
      commitmentCategory
      description
      __typename
    }
    __typename
  }
}

我们只对 感兴趣,story所以risks我们将拥有:

query Campaign($slug: String!) {
  project(slug: $slug) {
    risks
    story(assetWidth: 680)
  }
}

请注意,我们需要作为 url 一部分的项目 slug,例如clarissaredwine/swingby-a-voyager-gravity-puzzle,您的第二个 url 的 slug。

这是一个示例实现,它提取 slug,遍历 slug 并为每个 slug 调用 GraphQL 端点,它会打印每个 slug 的故事和风险:

import requests
from bs4 import BeautifulSoup
import re

urls = [ 
    "https://www.kickstarter.com/projects/1365297844/kuhkubus-3d-escher-figures?ref=discovery_staff_picks_category_newest",
    "https://www.kickstarter.com/projects/clarissaredwine/swingby-a-voyager-gravity-puzzle?ref=discovery_staff_picks_category_newest",
    "https://www.kickstarter.com/projects/100389301/us-army-navy-marines-air-force-special-challenge-c?ref=category"
]
slugs = []

#extract slugs from url
for url in urls:
    slugs.append(re.search('/projects/(.*)\?', url).group(1))

s = requests.Session()
r = s.get("https://www.kickstarter.com")
soup = BeautifulSoup(r.text, 'html.parser')
xcsrf = soup.find("meta", {"name": "csrf-token"})["content"]

query = """
query Campaign($slug: String!) {
  project(slug: $slug) {
    risks
    story(assetWidth: 680)
  }
}"""

for slug in slugs:
    print(f"--------{slug}------")
    r = s.post("https://www.kickstarter.com/graph",
        headers= {
            "x-csrf-token": xcsrf
        },
        json = {
            "operationName":"Campaign",
            "variables":{
                "slug": slug
            },
            "query": query
        })

    result = r.json()

    print("-------STORY--------")
    story_html = result["data"]["project"]["story"]
    soup = BeautifulSoup(story_html, 'html.parser')
    for i in soup.find_all('p'):
        print(i.get_text())

    print("-------RISKS--------")
    print(result["data"]["project"]["risks"])

我想如果你在这个网站上抓取其他内容,你可以将 graphQL 端点用于许多其他事情。但是,请注意,该 API 已禁用自省,因此您只能在站点上查找现有架构使用情况(您无法获取整个架构)


推荐阅读