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

Scrapy爬取FCA手册后,如何将数据按预期格式导出至Excel?

问题:Scrapy爬取FCA手册后无法生成预期格式的Excel文件

我用Scrapy爬取FCA手册的指定章节,已获取全部数据,但生成的Excel里,unique_ids、topics、clauses字段都是逗号分隔的字符串,无法匹配预期的“每个条目单独一行”的格式。

当前代码

Spider代码

import scrapy
from urllib.parse import urlencode
from scrapy.selector import Selector


class HandBook(scrapy.Spider):
    name = "handbook_spider"

    custom_settings = {
        "LOG_FILE": "handbook_spider.log",
        "ITEM_PIPELINES": {
            "handbook_spider.pipelines.HandbookExcelPipeline": 300,
        },
    }

    headers = {
        "authority": "www.handbook.fca.org.uk",
        "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
        "accept-language": "en,ru;q=0.9",
        "cache-control": "max-age=0",
        "sec-ch-ua": '"Chromium";v="106", "Yandex";v="22", "Not;A=Brand";v="99"',
        "sec-ch-ua-mobile": "?0",
        "sec-ch-ua-platform": '"Linux"',
        "sec-fetch-dest": "document",
        "sec-fetch-mode": "navigate",
        "sec-fetch-site": "cross-site",
        "sec-fetch-user": "?1",
        "upgrade-insecure-requests": "1",
        "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 YaBrowser/22.11.3.832 (beta) Yowser/2.5 Safari/537.36",
    }

    params = {
        "date": "2030-12-01",
        "timeline": "True",
        "view": "chapter",
    }

    url = "https://www.handbook.fca.org.uk/handbook/PRIN/2A/?"

    def start_requests(self):
        base_url = self.url + urlencode(self.params)
        yield scrapy.Request(
            url=base_url, headers=self.headers, callback=self.parse_details
        )

    def parse_details(self, response):
        item = {}
        item["unique_ids"] = ""
        item["chapter_ref"] = ""
        item["chapter"] = ""
        item["topics"] = ""
        item["clauses"] = ""
        handbook_content = response.css("div.handbook-content").extract()
        for content in handbook_content:
            str_content = Selector(text=content)
            item["unique_ids"] = str_content.css(
                "div.details > span.extended::text"
            ).extract()
            item["chapter_ref"] = str_content.css("h1 > span.extended::text").get()
            item["chapter"] = str_content.css("h1::text").extract()[-1]
            item["topics"] = str_content.css("h2.crosstitle::text").extract()
            item["clauses"] = [
                c.split(".")[-1]
                for c in str_content.css("div.details > span.extended::text").extract()
            ]

            yield item

Pipeline代码

from scrapy import signals
import os
import pandas as pd
from scrapy.exporters import CsvItemExporter

import logging


class HandbookExcelPipeline(object):
    def __init__(self):
        self.files = {}

    @classmethod
    def from_crawler(cls, crawler):
        pipeline = cls()
        crawler.signals.connect(pipeline.spider_opened, signals.spider_opened)
        crawler.signals.connect(pipeline.spider_closed, signals.spider_closed)
        return pipeline

    def spider_opened(self, spider):
        export_file = open("%s_items_excel.csv" % spider.name, "wb")
        self.file = export_file
        self.files[spider] = self.file
        self.exporter = CsvItemExporter(self.file)
        self.exporter.encoding = "utf-8"
        self.exporter.start_exporting()

    def process_item(self, item, spider):
        self.exporter.export_item(item)
        return item

    def spider_closed(self, spider):
        self.exporter.finish_exporting()
        self.file.close()
        try:
            df = pd.read_csv("%s_items_excel.csv" % spider.name).fillna("")
            # sorted_df = df.sort_values(by=["unique_ids"])
            output_path = "%s-items-final.xlsx" % spider.name
            with pd.ExcelWriter(output_path, engine="xlsxwriter") as writer:
                explode_cols.to_excel(
                    writer, sheet_name="Sheet1", header=True, index=False
                )

            # os.remove("%s_items_excel.csv" % spider.name)
        except pd.errors.EmptyDataError:
            spider.log("Excel pipeline No columns to parse from file", logging.ERROR)
            os.remove("%s_items_excel.csv" % spider.name)

问题核心

当前输出中,unique_ids、topics、clauses是列表被转为逗号分隔的字符串,全部挤在一行;预期是每个列表元素单独成一行,同时保留对应的chapter_ref和chapter信息。

解决方案

1. 修改Spider的parse_details方法(优先推荐)

不要一次性把所有列表值放到一个item里,而是遍历每个条目,生成独立的item:

def parse_details(self, response):
    handbook_content = response.css("div.handbook-content")
    for content in handbook_content:
        chapter_ref = content.css("h1 > span.extended::text").get()
        chapter = content.css("h1::text").extract()[-1].strip()
        # 遍历每个主题区块
        sections = content.css("div.section")
        for section in sections:
            topic = section.css("h2.crosstitle::text").get().strip() if section.css("h2.crosstitle::text") else ""
            # 遍历每个条款详情
            details = section.css("div.details > span.extended::text").extract()
            for detail in details:
                unique_id = detail.strip()
                clause = unique_id.split(".")[-1]
                yield {
                    "unique_ids": unique_id,
                    "chapter_ref": chapter_ref,
                    "chapter": chapter,
                    "topics": topic,
                    "clauses": clause
                }

这样每个条目都会生成单独的item,后续导出时自然是一行一条数据,无需额外处理。

2. 修复Pipeline中的错误(备选方案)

若仍需要从CSV拆分列表,可修改spider_closed方法,补充未定义的explode_cols逻辑:

def spider_closed(self, spider):
    self.exporter.finish_exporting()
    self.file.close()
    try:
        df = pd.read_csv("%s_items_excel.csv" % spider.name).fillna("")
        # 拆分列表字段为多行
        explode_cols = df.explode(["unique_ids", "topics", "clauses"], ignore_index=True)
        output_path = "%s-items-final.xlsx" % spider.name
        with pd.ExcelWriter(output_path, engine="xlsxwriter") as writer:
            explode_cols.to_excel(writer, sheet_name="Sheet1", header=True, index=False)
        os.remove("%s_items_excel.csv" % spider.name)
    except pd.errors.EmptyDataError:
        spider.log("Excel pipeline: No columns to parse from file", logging.ERROR)
        os.remove("%s_items_excel.csv" % spider.name)

内容的提问来源于stack exchange,提问作者X-something

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 10:25:13