You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Selenium点击Show More按钮遇阻及GraphQL爬取报错求助

问题描述

最新更新见底部

我使用Python 3.12.3和Selenium进行网页爬取,希望在爬取前加载更多书评内容(理想情况是加载全部或尽可能多,网站可能会限制单页总评论数)。手动点击Show More按钮至少10次都能成功,但自动化点击始终失败。

目标按钮:Show More Button
我无法成功点击该按钮,推测原因是其包含:before伪元素,且页面存在多个div.Button__container元素,导致点击时误触其他按钮。

爬取书评脚本

以下是我用于爬取书评的脚本,但它无法实现点击加载更多的功能,我希望通过Selenium解决这个问题:

import requests
import os
from bs4 import BeautifulSoup
import pandas as pd
import time
from selenium import webdriver
from IPython.display import display, Image

driver = webdriver.Chrome()
title = driver.title

for i in range(0,1000,100):
    # McCurdy - 《我很高兴我妈死了》
    response = requests.get('https://www.goodreads.com/book/show/26074156/reviews?reviewFilters={%22workId%22:%22kca://work/amzn1.gr.work.v1.FSsY8ohzUZCeEXoBsiEYqw%22,%22after%22:%22NjgxNSwxNTAwNjU3MjE4NDI1%22}')
print(response.status_code)

time.sleep(6)

doc = BeautifulSoup(response.text, 'html.parser')

df = pd.DataFrame(columns=['ReviewDate','ReviewerName','Rating','ReviewText','ReviewerMeta'])

ratings = []

# 遍历所有找到的元素
for tag in book_tags_:
    # 获取当前元素的aria-label属性
    aria_label = tag.get('aria-label')
    
    # 检查aria-label是否不为空且符合预期格式
    if aria_label and 'Rating ' in aria_label and ' out of 5' in aria_label:
        # 拆分aria-label提取所需文本
        rating_text = aria_label.split('Rating ')[1].split(' out of 5')[0]
        
        # 将评分文本添加到列表
        ratings.append(rating_text)
    else:
        print(f"Skipping element with aria-label: {aria_label}")

# 从评分列表创建DataFrame
df = pd.DataFrame({'Rating': ratings})

# 其他字段的提取逻辑与此类似

尝试点击Show More按钮的代码

以下是我尝试点击按钮的代码:

import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, StaleElementReferenceException, ElementClickInterceptedException

# 初始化WebDriver
driver = webdriver.Chrome()  # 或使用你其他的驱动

url = "https://www.goodreads.com/book/show/59364173/reviews?reviewFilters={%22workId%22:%22kca://work/amzn1.gr.work.v3.JeHZlXvg2e1mD9_k%22,%22after%22:%22MjYwMTYsMTY2MDc1MjY5MjY2Mw%22}"

# 打开页面
driver.get(url)
time.sleep(20)

def click_show_more():
    while True:
        try:
            # 滚动到页面底部确保按钮可见
            driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
            
            # 找到'Show More'按钮
            show_more_button = WebDriverWait(driver, 10).until(
                EC.visibility_of_element_located((By.CSS_SELECTOR, 'div.Divider.Divider--contents.Divider--largeMargin > div.Button__container'))
            )
            
            # 将元素滚动到视图中
            driver.execute_script("arguments[0].scrollIntoView(true);", show_more_button)
            
            # 确保元素可点击
            WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, 'div.Divider.Divider--contents.Divider--largeMargin > div.Button__container')))
            
            # 必要时使用JavaScript点击按钮
            driver.execute_script("arguments[0].click();", show_more_button)
            
            # 点击后等待新内容加载(可选)
            WebDriverWait(driver, 10).until(EC.staleness_of(show_more_button))
        
        except TimeoutException:
            print("未找到更多'Show More'按钮或超时。")
            break
        except StaleElementReferenceException:
            print("StaleElementReferenceException: 重新查找按钮。")
            continue
        except ElementClickInterceptedException:
            print("ElementClickInterceptedException: 元素被遮挡。")
            continue
        except Exception as e:
            print(f"点击'Show More'按钮出错: {e}")
            break

# 调用函数
click_show_more()

更新后的问题与代码

根据@x1337loser的建议,我在get_data()函数中添加了列表追加逻辑,之后出现报错(报错截图)。我还移除了打印输出中的用户头像,并在脚本末尾添加了数据字典和写入CSV的逻辑,这些步骤未引发问题。

以下是更新后的代码:

import time
import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
from bs4 import BeautifulSoup
import pandas as pd

ReviewText = []
ratings = []
Reviewer = []
reviewdt = []
ReviewerId = []
Author_ID = []

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

def get_data(content):
    data = content['data']['getReviews']['edges']
    for i in data:
        id_user = i['node']['creator']['id']
        rev_img = i['node']['creator']['imageUrlSquare']
        is_author = i['node']['creator']['isAuthor']
        follower_count = i['node']['creator']['followersCount']
        name = i['node']['creator']['name']
        review = BeautifulSoup(i['node']['text'], "lxml").text # 移除文本中的HTML标签
        review_create_data = i['node']['createdAt']
        result_ms = pd.to_datetime(review_create_data,unit='ms') # 解码时间戳
        review_liked = i['node']['likeCount']
        rating = i['node']['rating']
                                                                                                                                   # 可在result_ms和reviewer_follower之间添加\nreviewer_image:  {rev_img}
        print(f"评论者姓名:  {name}\n评论者用户ID:  {id_user}\n是否为作者:  {is_author}\n评论日期:  {result_ms}\n评论者粉丝数:  {follower_count}\n评论内容:  {review}\n评分:  {rating}\n点赞数:  {review_liked}\n=========================================")

        # print(f"是否为作者:  {is_author}\n评论者粉丝数:  {follower_count}\n点赞数:  {review_liked}\n=========================================")


        ReviewText_content = {review}
        rating_text = {rating}
        Reviewer_name = {name}
        reviewdt_content = {result_ms}
        reviewer_id = {id_user}
        author = {is_author}
        
        # 将内容添加到对应列表
        Reviewer.append(Reviewer_name)
        reviewdt.append(reviewdt_content)
        ratings.append(rating_text)    
        ReviewText.append(ReviewText_content)
        ReviewerId.append(reviewer_id)
        Author_ID.append(author)


def gatherNextPage(resourceId):
    url = "https://kxbwmqov6jgg3daaamb744ycu4.appsync-api.us-east-1.amazonaws.com/graphql"
    headers = {
        "User-Agent":"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:130.0) Gecko/20100101 Firefox/130.0",
        "X-Api-Key": "da2-xpgsdydkbregjhpr6ejzqdhuwy" # 服务器自动添加的API密钥,客户端请求会严格校验
    }
    data = {
    "operationName": "getReviews",
    "variables": {
        "filters": {
        "resourceType": "WORK",
        "resourceId": f"{resourceId}"
        },
        "pagination": {
        "limit": 100
        }
    },
    "query": "query getReviews($filters: BookReviewsFilterInput!, $pagination: PaginationInput) {\n  getReviews(filters: $filters, pagination: $pagination) {\n    ...BookReviewsFragment\n    __typename\n  }\n}\n\nfragment BookReviewsFragment on BookReviewsConnection {\n  totalCount\n  edges {\n    node {\n      ...ReviewCardFragment\n      __typename\n    }\n    __typename\n  }\n  pageInfo {\n    prevPageToken\n    nextPageToken\n    __typename\n  }\n  __typename\n}\n\nfragment ReviewCardFragment on Review {\n  __typename\n  id\n  creator {\n    ...ReviewerProfileFragment\n    __typename\n  }\n  recommendFor\n  updatedAt\n  createdAt\n  spoilerStatus\n  lastRevisionAt\n  text\n  rating\n  shelving {\n    shelf {\n      name\n      webUrl\n      __typename\n    }\n    taggings {\n      tag {\n        name\n        webUrl\n        __typename\n      }\n      __typename\n    }\n    webUrl\n    __typename\n  }\n  likeCount\n  viewerHasLiked\n  commentCount\n}\n\nfragment ReviewerProfileFragment on User {\n  id: legacyId\n  imageUrlSquare\n  isAuthor\n  ...SocialUserFragment\n  textReviewsCount\n  viewerRelationshipStatus {\n    isBlockedByViewer\n    __typename\n  }\n  name\n  webUrl\n  contributor {\n    id\n    works {\n      totalCount\n      __typename\n    }\n    __typename\n  }\n  __typename\n}\n\nfragment SocialUserFragment on User {\n  viewerRelationshipStatus {\n    isFollowing\n    isFriend\n    __typename\n  }\n  followersCount\n  __typename\n}\n"
    }
    n, data_next = 1, data
    while True:
        time.sleep(3)
        resp = requests.post(url, headers=headers, json=data_next, verify=False)
        data = resp.json()
        get_data(data)
        nextPageToken = data['data']['getReviews']['pageInfo']['nextPageToken']
        if not nextPageToken:
            break

        data_next = {
        "operationName": "getReviews",
        "variables": {
            "filters": {
            "resourceType": "WORK",
            "resourceId": f"{resourceId}"
            },
            "pagination": {
            "after": f"{nextPageToken}", # 从响应数据获取nextPageToken,每次请求最多返回100条结果
            "limit": 100
            }
        },
        "query": "query getReviews($filters: BookReviewsFilterInput!, $pagination: PaginationInput) {\n  getReviews(filters: $filters, pagination: $pagination) {\n    ...BookReviewsFragment\n    __typename\n  }\n}\n\nfragment BookReviewsFragment on BookReviewsConnection {\n  totalCount\n  edges {\n    node {\n      ...ReviewCardFragment\n      __typename\n    }\n    __typename\n  }\n  pageInfo {\n    prevPageToken\n    nextPageToken\n    __typename\n  }\n  __typename\n}\n\nfragment ReviewCardFragment on Review {\n  __typename\n  id\n  creator {\n    ...ReviewerProfileFragment\n    __typename\n  }\n  recommendFor\n  updatedAt\n  createdAt\n  spoilerStatus\n  lastRevisionAt\n  text\n  rating\n  shelving {\n    shelf {\n      name\n      webUrl\n      __typename\n    }\n    taggings {\n      tag {\n        name\n        webUrl\n        __typename\n      }\n      __typename\n    }\n    webUrl\n    __typename\n  }\n  likeCount\n  viewerHasLiked\n  commentCount\n}\n\nfragment ReviewerProfileFragment on User {\n  id: legacyId\n  imageUrlSquare\n  isAuthor\n  ...SocialUserFragment\n  textReviewsCount\n  viewerRelationshipStatus {\n    isBlockedByViewer\n    __typename\n  }\n  name\n  webUrl\n  contributor {\n    id\n    works {\n      totalCount\n      __typename\n    }\n    __typename\n  }\n  __typename\n}\n\nfragment SocialUserFragment on User {\n  viewerRelationshipStatus {\n    isFollowing\n    isFriend\n    __typename\n  }\n  followersCount\n  __typename\n}\n"
        }

gatherNextPage("kca://work/amzn1.gr.work.v3.JeHZlXvg2e1mD9_k")

data_dict = {
    'ReviewDate': reviewdt,
    'ReviewerName': Reviewer,
    'Rating': ratings,
    'ReviewText': ReviewText,
    'ReviewerID': ReviewerId,
    'IsAuthor': Author_ID

    # 'ReviewerMeta': reviewer_meta
}



df = pd.DataFrame(data_dict)
df.to_csv('McCurdy_Sample_Sept30.csv', index=False) 

内容的提问来源于stack exchange,提问作者Bad_coder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 22:10:54