scrapy爬取当当网青春爱情文学图书数据

网址:青春爱情文学_畅销青春爱情文学类图书【推荐 正版 价格】_青春文学-当当网

dangdang.py

import scrapy
from..items import DangdangBookItem

# 定义名为DangdangSpider的爬虫类,继承自scrapy.Spider类
class DangdangSpider(scrapy.Spider):
    # 爬虫的名称,用于在Scrapy项目中唯一标识这个爬虫
    name = "dangdang"
    # 当当网图书分类页面的基础URL,这里指向了某个图书分类页面(具体分类由URL中的参数决定)
    base_url = ""
    # 起始URL列表,爬虫开始抓取的页面,这里只设置了基础URL作为起始页面
    start_urls = [base_url]
    # 用于记录当前正在抓取的页码,初始化为1
    page_num = 1

    def parse(self, response):
        """
        解析函数,用于处理抓取到的页面内容(response),提取图书相关信息,并生成数据项(item),
        同时处理翻页逻辑,继续抓取后续页面内容。

        参数:
            response: 抓取到的页面响应对象,包含了页面的HTML内容等信息。
        """
        # 使用CSS选择器选取页面中包含图书信息的每个li元素(每个li通常对应一本书的信息展示块)
        books = response.css('ul.bigimg li')
        for book in books:
            item = DangdangBookItem()
            # 使用CSS选择器选取图书标题对应的元素(a标签)
            title_element = book.css('p.name a')
            # 进一步获取标题文本内容,如果获取到则去除两端空白字符后赋值给item['title'],
            # 如果没获取到则设置默认值 "Title not found"
            title = title_element.css('::text').get()
            if title:
                item['title'] = title.strip()
            else:
                item['title'] = "Title not found"

            # 使用CSS选择器选取图书当前价格对应的元素,并获取文本内容,
            # 如果获取到则去除价格符号'¥'和两端空白字符后赋值给item['price'],
            # 如果没获取到则设置默认值 "Price not found"
            price_text = book.css('span.search_now_price::text').get()
            if price_text:
                item['price'] = price_text.replace('¥', '').strip()
            else:
                item['price'] = "Price not found"

            # 使用CSS选择器选取图书原价对应的元素,并获取文本内容,
            # 如果获取到则去除价格符号'¥'和两端空白字符后赋值给item['original_price'],
            # 如果没获取到则设置默认值 "Original price not found"
            original_price_text = book.css('span.search_pre_price::text').get()
            if original_price_text:
                item['original_price'] = original_price_text.replace('¥', '').strip()
            else:
                item['original_price'] = "Original price not found"

            # 使用CSS选择器选取图书折扣对应的元素,并获取文本内容,
            # 如果获取到则通过字符串处理提取折扣数值(去除括号等多余字符)后赋值给item['discount'],
            # 如果没获取到则设置默认值 "Discount not found"
            discount_text = book.css('span.search_discount::text').get()
            if discount_text:
                item['discount'] = discount_text.split('(')[1].split(')')[0].strip()
            else:
                item['discount'] = "Discount not found"

            # 使用CSS选择器选取图书作者对应的元素(a标签),获取所有作者文本内容列表,
            # 如果获取到则用逗号连接作者名字并去除两端空白字符后赋值给item['author'],
            # 如果没获取到则设置默认值 "Author not found"
            authors = book.css('p.search_book_author a:first-child::text').getall()
            if authors:
                item['author'] = ', '.join(authors).strip()
            else:
                item['author'] = "Author not found"

            # 使用CSS选择器选取图书出版社对应的元素(a标签),获取出版社文本内容,
            # 如果获取到则去除两端空白字符后赋值给item['publisher'],
            # 如果没获取到则设置默认值 "Publisher not found"
            publisher = book.css('p.search_book_author a:last-child::text').get()
            if publisher:
                item['publisher'] = publisher.strip()
            else:
                item['publisher'] = "Publisher not found"

            # 使用CSS选择器选取图书评论数量对应的元素(a标签),获取文本内容,
            # 如果获取到则去除"条评论"字样和两端空白字符后赋值给item['comment_num'],
            # 如果没获取到则设置默认值 "Comment number not found"
            comment_num_element = book.css('p.search_star_line a')
            comment_num = comment_num_element.css('::text').get()
            if comment_num:
                item['comment_num'] = comment_num.replace('条评论', '').strip()
            else:
                item['comment_num'] = "Comment number not found"

            # 使用CSS选择器选取图书描述对应的元素(p.detail),获取所有描述文本内容列表,
            # 如果获取到则合并文本内容并去除两端空白字符后赋值给item['description'],
            # 如果没获取到则设置默认值 "Description not found"
            description_element = book.css('p.detail')
            description = description_element.css('::text').getall()
            if description:
                item['description'] = ' '.join([desc.strip() for desc in description]).strip()
            else:
                item['description'] = "Description not found"

            # 使用CSS选择器选取图书出版日期对应的元素(span元素),获取文本内容,
            # 如果获取到则去除两端空白字符后赋值给item['release_date'],
            # 如果没获取到则设置默认值 "Release date not found"
            release_date_element = book.css('p.search_book_author span:nth-child(2)::text').get()
            if release_date_element:
                item['release_date'] = release_date_element.strip()
            else:
                item['release_date'] = "Release date not found"

            # 将提取好信息的item(数据项)通过生成器返回,交给后续的管道(pipeline)处理
            yield item

        # 初始化下一页的URL为None
        next_page_url = None
        # 使用CSS选择器选取页面中指向“下一页”的链接元素(a标签)
        next_page_links = response.css('div.paging li.next a')
        if next_page_links:
            # 如果存在“下一页”链接,则获取第一个链接的href属性值作为下一页的URL
            next_page_url = next_page_links[0].attrib['href']
        if next_page_url:
            # 如果获取到了下一页的URL,则构造一个新的Scrapy请求,使用当前的解析函数(self.parse)
            # 作为回调函数继续处理下一页的内容,这样可以实现自动翻页抓取
            yield scrapy.Request(response.urljoin(next_page_url), callback=self.parse)
pipelines.py
import csv
from scrapy.exceptions import DropItem

# 定义名为DangdangBooksPipeline的管道类,用于处理爬虫提取到的数据项(item),
# 这里主要功能是将图书信息写入CSV文件。
class DangdangBooksPipeline:
    def open_spider(self, spider):
        """
        在爬虫启动时被调用,用于初始化文件相关操作,比如打开文件、决定写入模式(追加或覆盖),
        以及初始化CSV写入器等。

        参数:
            spider: 当前正在运行的爬虫对象实例。
        """
        try:
            # 尝试以读模式打开名为'dangdang_books.csv'的文件(用于检查文件是否已存在)
            with open('dangdang_books.csv', 'r', newline='', encoding='utf-8') as f:
                # 提示用户输入,决定是否追加数据到已存在的文件中
                choice = input("文件已存在,是否追加数据?(y/n): ")
                if choice.lower() == 'y':
                    # 如果用户选择追加(输入'y'),则以追加模式打开文件,并初始化CSV写入器,
                    # 指定写入的字段名(列名)
                    self.file = open('dangdang_books.csv', 'a', newline='', encoding='utf-8')
                    self.writer = csv.DictWriter(self.file, fieldnames=['书名', '价格', '原价',
                                                                        '折扣', '作者', '出版社',
                                                                        '评论数量', '描述', '出版日期'])
                else:
                    # 如果用户选择不追加(输入其他字符),则以覆盖写入模式打开文件,
                    # 并初始化CSV写入器,同时写入表头(字段名)
                    self.file = open('dangdang_books.csv', 'w', newline='', encoding='utf-8')
                    self.writer = csv.DictWriter(self.file, fieldnames=['书名', '价格', '原价',
                                                                        '折扣', '作者', '出版社',
                                                                        '评论数量', '描述', '出版日期'])
                    self.writer.writeheader()
        except FileNotFoundError:
            # 如果文件不存在,则以写入模式打开文件(相当于新建文件),
            # 初始化CSV写入器并写入表头(字段名)
            self.file = open('dangdang_books.csv', 'w', newline='', encoding='utf-8')
            self.writer = csv.DictWriter(self.file, fieldnames=['书名', '价格', '原价',
                                                                '折扣', '作者', '出版社',
                                                                '评论数量', '描述', '出版日期'])
            self.writer.writeheader()

    def process_item(self, item, spider):
        """
        处理每个数据项(item)的方法,在这里会对数据项进行验证,
        如果各项关键信息都存在,则进行格式转换后写入CSV文件,并返回该数据项让后续管道继续处理,
        如果有关键信息缺失,则丢弃该数据项。

        参数:
            item: 爬虫提取到的一个数据项,包含图书的各项信息。
            spider: 当前正在运行的爬虫对象实例。
        """
        # 检查数据项中各项关键信息是否都存在(不为空)
        if item['title'] and item['price'] and item['original_price'] and item['discount'] and item['author'] and item[
            'publisher'] and item['comment_num'] and item['description'] and item['release_date']:
            # 如果各项关键信息都存在,则构造一个新的字典,将英文键名转换为中文键名(对应CSV的列名)
            new_item = {
                '书名': item['title'],
                '价格': item['price'],
                '原价': item['original_price'],
                '折扣': item['discount'],
                '作者': item['author'],
                '出版社': item['publisher'],
                '评论数量': item['comment_num'],
                '描述': item['description'],
                '出版日期': item['release_date']
            }
            # 使用CSV写入器将转换后的字典数据写入CSV文件
            self.writer.writerow(new_item)
            return item
        else:
            # 如果有关键信息缺失,则抛出DropItem异常,表示丢弃这个无效的数据项
            raise DropItem(f"Invalid item: {item}")

    def close_spider(self, spider):
        
        在爬虫关闭时被调用,用于关闭之前打开的文件资源。

        参数:
            spider: 当前正在运行的爬虫对象实例。
        
        self.file.close()

返回结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luky!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值