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

Scrapy无法登录BoardGameGeek平台的技术求助

解决BGG爬虫登录时找不到Form元素的问题

问题原因

你遇到的No <form> element found错误,确实是因为BGG当前的登录表单是通过JavaScript动态生成的。Scrapy默认的下载器只能获取页面的静态HTML,无法渲染JS,所以返回的响应里没有实际的<form>节点,导致FormRequest.from_response失效。

解决方案

方案一:直接调用BGG登录API(高效推荐)

BGG提供了官方登录API,无需依赖JS渲染,直接构造POST请求即可完成登录:

import scrapy
from scrapy import Request

class BGGSpider(scrapy.Spider):
    name = "bgg"
    headers = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0'}

    def start_requests(self):
        # 先访问首页获取CSRF Token
        yield scrapy.Request(
            url='https://boardgamegeek.com/',
            callback=self.get_csrf_token,
            headers=self.headers
        )

    def get_csrf_token(self, response):
        # 从页面meta标签提取CSRF Token
        csrf_token = response.xpath('//meta[@name="csrf-token"]/@content').get()
        if not csrf_token:
            self.logger.error("获取CSRF Token失败")
            return
        
        # 构造登录POST请求
        yield scrapy.Request(
            url='https://boardgamegeek.com/api/v1/user/login',
            method='POST',
            headers={
                **self.headers,
                'X-CSRF-Token': csrf_token,
                'Content-Type': 'application/json'
            },
            body='{"username": "你的账号", "password": "你的密码"}',
            callback=self.after_login
        )

    def after_login(self, response):
        # 检查登录结果
        result = response.json()
        if result.get('user'):
            self.logger.info("登录成功")
            # 开始抓取目标页面
            base_url = "https://www.boardgamegeek.com/browse/boardgame/page/"
            for page in range(1, 21):  # 可调整抓取页数
                yield Request(
                    url=f"{base_url}{page}",
                    callback=self.parse_deeper,
                    headers=self.headers,
                    dont_filter=True
                )
        else:
            self.logger.error(f"登录失败: {result.get('message', '未知错误')}")

    def parse_deeper(self, response):
        # 此处编写具体页面解析逻辑,示例提取游戏标题
        for title in response.xpath('//a[@class="primary"]/text()').getall():
            yield {'游戏标题': title.strip()}

方案二:用Scrapy+Selenium渲染JS

如果API调用受限,或需要模拟真实浏览器行为,可使用Selenium加载JS渲染后的页面:

  1. 先安装依赖:pip install scrapy-selenium
  2. 在settings.py中配置Selenium:
DOWNLOADER_MIDDLEWARES = {
    'scrapy_selenium.SeleniumMiddleware': 800
}
SELENIUM_DRIVER_NAME = 'firefox'  # 或'chrome'
SELENIUM_DRIVER_EXECUTABLE_PATH = '/path/to/你的浏览器驱动'  # 替换为实际路径
SELENIUM_DRIVER_ARGUMENTS = ['-headless']  # 无头模式,可选
  1. 修改爬虫代码:
from scrapy import Spider, FormRequest
from scrapy_selenium import SeleniumRequest

class BGGSpider(Spider):
    name = "bgg"
    headers = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0'}

    def start_requests(self):
        # 用SeleniumRequest加载登录页,渲染JS
        yield SeleniumRequest(
            url='https://boardgamegeek.com/login/',
            callback=self.login,
            headers=self.headers
        )

    def login(self, response):
        # 此时响应已包含JS渲染后的表单
        return FormRequest.from_response(
            response,
            formdata={
                'username': '你的账号',
                'password': '你的密码',
            },
            callback=self.after_login,
            headers=self.headers
        )

    def after_login(self, response):
        # 验证登录状态(检查页面是否显示用户名)
        if response.xpath('//span[@class="username"]/text()'):
            self.logger.info("登录成功")
            base_url = "https://www.boardgamegeek.com/browse/boardgame/page/"
            for page in range(1, 21):
                yield SeleniumRequest(
                    url=f"{base_url}{page}",
                    callback=self.parse_deeper,
                    headers=self.headers,
                    dont_filter=True
                )
        else:
            self.logger.error("登录失败")

    def parse_deeper(self, response):
        # 示例解析逻辑
        for title in response.xpath('//a[@class="primary"]/text()').getall():
            yield {'游戏标题': title.strip()}

注意事项

  • 替换代码中的你的账号和你的密码为真实BGG账号信息。
  • 方案一中的API接口若有变动,可查看BGG官方文档确认最新接口。
  • 使用Selenium时,确保浏览器驱动与浏览器版本匹配,避免兼容性问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 23:30:53