爬取 Coze bot store 的使用量数据

对 Coze 上的 bot 的使用量数据有兴趣,想爬取下来分析热门 bot。虽然之前没做过这类爬虫,但好在有 ChatGPT,直接提问,多提几次,就能写出可用的爬虫了

Coze bot store 网页结构

Coze 首页 就是 bot 列表,按 10 个类别组织。初始 bot 列表是 24 个 bot 卡片,bot 卡片已经有使用量数据了。页面底部的 ’Load more‘ 按钮触发滚动加载 bot 卡片

bot 卡片结构
页面底部的Load按钮

方案 Selenium

直接问 Gemini-1.5-Flash 如何模拟用户的翻页操作,Gemini 就推荐了 Selenium、Playwright 2 个工具

有很多浏览器模拟工具可以支持获取 API 返回内容,它们通常被称为 “Web 自动化工具” 或 “浏览器驱动工具”。以下是几个常用的工具:

1. Selenium:

  • 功能强大: Selenium 是最受欢迎的 Web 自动化工具之一,支持多种浏览器,可以模拟用户操作,包括访问网页、填写表单、点击按钮等。
  • 获取 API 返回内容: Selenium 可以通过 requests 库或内置的 get 方法获取 API 返回内容,并解析 JSON 或 XML 数据。
  • 语言支持: 支持 Python、Java、C#、JavaScript 等多种编程语言。

2. Playwright:

  • 现代化工具: Playwright 是一个相对较新的工具,由微软开发,支持 Chrome、Firefox、Safari 和 Edge 等浏览器。
  • API 驱动: Playwright 提供简洁的 API,方便编写自动化脚本。
  • 异步操作: Playwright 支持异步操作,可以提高爬取效率。

主要步骤

初始化 selenium 并加载初始网页

初始化一个 Chrome driver,加载第一个页面,由于加载需要一定时间,等待页面加载完毕

# Set up the Chrome driver
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service)

# Load the webpage
url = 'https://www.coze.com/?cate_type=publish_mode&cate_value=1'

driver.get(url)

wait = WebDriverWait(driver, 10)

定位页面内容

由于 bot 卡片的 css class 是页面唯一的 aLnriRC1g8QLH0wzcyzQ,直接使用 CLASS_NAME 来定位卡片元素

driver.find_elements(By.CLASS_NAME, 'aLnriRC1g8QLH0wzcyzQ')

在卡片内部按照 XPATH 绝对路径来定位 bot 名称、统计数据等其他信息。

注意,这个定位方式与页面设计有关,如果失效,需要分析页面

# bot 名称:
find_element(By.XPATH, './/div[1]/div[2]/span')
# bot 统计数据:
find_element(By.XPATH, './/div[3]/div[1]')

定位加载按钮

因为 Load more 按钮 ’似乎‘ 没有唯一的 class name,所以使用它的所有 class name 组合定位

注意,这个定位方式与页面设计有关,如果失效,需要分析页面

load_more_button = wait.until(
        EC.element_to_be_clickable(
            (
                By.CSS_SELECTOR,
                "button.semi-button.semi-button-primary.semi-button-borderless.Vqs8pu0RTKoOl2fVHiq6.yJDwteqjbdkspQ0V7XOl"
            )
        )
    )

完整代码

完整代码如下

# -*- coding: utf8 -*-
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC


# Set up the Chrome driver
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service)

# Load the webpage
url = 'https://www.coze.com/?cate_type=publish_mode&cate_value=1'

driver.get(url)

wait = WebDriverWait(driver, 10)

while True:
    # Firstly, check `Load more` button exists to ensure the page are rendered
    # 这里使用 Load more 按钮的完整 css class 来定位
    load_more_button = wait.until(
        EC.element_to_be_clickable(
            (
                By.CSS_SELECTOR,
                "button.semi-button.semi-button-primary.semi-button-borderless.Vqs8pu0RTKoOl2fVHiq6.yJDwteqjbdkspQ0V7XOl"
            )
        )
    )

    # Get robot cards in current page
    # bot 卡片的 css class 是 aLnriRC1g8QLH0wzcyzQ
    content = driver.find_elements(By.CLASS_NAME, 'aLnriRC1g8QLH0wzcyzQ')

    for card in content:
        try:
            # 按照 bot 卡片中的 html 元素来绝对定位名称、作者、统计数据、描述等信息
            # 注意:这个定位可能随着 Coze 网页改版而变化,需要及时调整
            print(json.dumps(
                {
                    "name": card.find_element(
                            By.XPATH,
                            './/div[1]/div[2]/span',
                        ).text,
                    "author": card.find_element(
                            By.XPATH,
                            './/div[1]/div[2]/div/span',
                        ).text,
                    "stats": card.find_element(
                            By.XPATH,
                            './/div[3]/div[1]',
                        ).text.replace("\n", ' | '),
                    "description": card.find_element(
                            By.XPATH,
                            './/div[1]/div[2]/p',
                        ).text,
                },
                ensure_ascii=False,
            ))
        except:
            print("Parse element error")

    sleep_duration = random.uniform(1, 3)
    time.sleep(sleep_duration)

    if load_more_button:
        print("Clear current cards, load more")

# 这里把当前页面的 bot 卡片删除,避免 Load more 后页面变长导致卡顿问题       
driver.execute_script("document.querySelectorAll('.aLnriRC1g8QLH0wzcyzQ').forEach(el => el.remove());")
        time.sleep(2)
        # 点击 Load more 按钮加载更多 bot 卡片
        driver.execute_script("arguments[0].click();", load_more_button)
        time.sleep(1)
    else:
        print("No more button, work done")
        break

申明:代码仅用于学习研究,请尊重网站知识产权


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *