Scrapy+Selenium处理Nepalitimes网站Load More按钮爬取问题
解决方案:Nepalitimes网站爬取问题修复
一、Selenium触发Load More按钮失败修复
核心问题排查与调整
- 确认按钮定位准确性:直接从浏览器开发者工具复制按钮的稳定CSS选择器/XPath(比如
button.load-more-btn或//button[text()='Load More']),避免使用易变的元素路径。 - 增加显式等待逻辑:避免页面未加载完成就执行点击操作,确保按钮处于可交互状态。
- 循环加载直到按钮消失:通过循环触发点击,直到按钮不存在或无法点击,确保加载所有内容。
调整后代码示例
from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC def parse(self, response): driver = self.driver while True: try: # 等待Load More按钮可点击,超时10秒 load_btn = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, "button[data-testid='load-more']")) ) load_btn.click() # 等待新文章加载完成(以最后一篇文章出现为标志) WebDriverWait(driver, 5).until( EC.presence_of_element_located((By.CSS_SELECTOR, ".article-card:last-child")) ) except Exception: # 按钮消失或无法点击,终止循环 break # 提取所有文章数据 articles = driver.find_elements(By.CSS_SELECTOR, ".article-card") for article in articles: yield { "title": article.find_element(By.CSS_SELECTOR, "h2").text, "url": article.find_element(By.CSS_SELECTOR, "a").get_attribute("href"), # 按需添加其他字段 }
二、GraphQL接口爬取异常修复
核心问题排查与调整
- 完善分页逻辑:通过
offset和limit变量实现分页请求,每次递增offset直到返回数据为空。 - 确保回调函数生效:发送POST请求时必须指定
callback=self.parse_ajax_response,避免默认走parse函数。 - 过滤archive页面:提取URL时判断是否包含
/archive/,直接跳过此类数据。
调整后代码示例
import scrapy import json class NepaliTimesSpider(scrapy.Spider): name = "nepalitimes" allowed_domains = ["nepalitimes.com"] GRAPHQL_URL = "https://nepalitimes.com/graphql" def start_requests(self): # 初始分页参数 offset = 0 limit = 10 # 从开发者工具复制的GraphQL查询语句 query = """ query FetchArticles($offset: Int!, $limit: Int!) { articles(offset: $offset, limit: $limit) { nodes { title url publishDate } } } """ payload = { "query": query, "variables": {"offset": offset, "limit": limit} } yield scrapy.Request( url=self.GRAPHQL_URL, method="POST", body=json.dumps(payload), headers={"Content-Type": "application/json"}, callback=self.parse_ajax_response, meta={"offset": offset + limit, "limit": limit} ) def parse_ajax_response(self, response): data = json.loads(response.text) articles = data.get("data", {}).get("articles", {}).get("nodes", []) for article in articles: article_url = article.get("url") # 过滤archive页面数据 if "/archive/" in article_url: continue yield { "title": article.get("title"), "url": article_url, "publish_date": article.get("publishDate") } # 继续请求下一页(如果当前返回数据量等于limit,说明还有内容) offset = response.meta.get("offset") limit = response.meta.get("limit") if len(articles) == limit: query = """ query FetchArticles($offset: Int!, $limit: Int!) { articles(offset: $offset, limit: $limit) { nodes { title url publishDate } } } """ payload = { "query": query, "variables": {"offset": offset, "limit": limit} } yield scrapy.Request( url=self.GRAPHQL_URL, method="POST", body=json.dumps(payload), headers={"Content-Type": "application/json"}, callback=self.parse_ajax_response, meta={"offset": offset + limit, "limit": limit} )
额外注意事项
- 检查GraphQL接口的请求头:部分接口需要携带
X-CSRF-Token等验证头,可从浏览器的Network请求中复制。 - 配置反爬策略:在
settings.py中设置DOWNLOAD_DELAY = 2,避免请求过于频繁导致被封禁。 - 验证接口返回:如果返回报错,检查查询语句中的字段是否与接口定义一致,变量格式是否符合要求。
内容的提问来源于stack exchange,提问作者user13998985
相关产品推荐
相关产品推荐

