爬虫综合案例-使用Scrapy爬取当当网的图片信息

news/2024/7/19 12:27:04 标签: 爬虫, scrapy

目标

本节将继续讲解 Scrapy 框架的使用。具体包括 Scrapy 爬虫框架以及内部每个组件的使用(Selector 选择器、Spider 爬虫类、Downloader 和 Spider 中间件、ItemPipeline 管道类等)。

本例目标是爬取当当图书网站中所有关于 “python” 关键字的图片信息,网址http://search.dangdang.com/?key=python&act=input,要求将图书图片下载存储到指定目录,而图书信息写入到数据库中。

通过本例练习,要掌握 Scrapy 框架结构、运行原理、以及框架内部各个组件的使用。通过 Scrapy 框架的深入学习,学会自定义 Spider类爬取处理信息。

操作

运行爬虫之前,请导入 pythonbook.sql 到 MySQL。

  • 第1步,创建工程

新建文件夹,用于存放项目。然后在命令行窗口中使用cd命令切换到当前目录,再输入下面命令创建Scrapy项目。

scrapy startproject dangdang

使用cd命令切换到dangdang目录,再输入下面命令创建Scrapy程序。

cd dangdang
scrapy genspider pythonbook search.dangdang.com
  • 第3步,自动创建目录及文件

完成前面2步操作,Scrapy会自动在指定文件夹中创建一个项目,并初始化爬虫程序文件的模板,如下图所示。
在这里插入图片描述
注意,一般创建爬虫文件时,以网站域名命名。

  • 第4步,设置数据存储模板

打开items.py文件,然后输入下面代码,设置数据存储的模板。

import scrapy


class DangdangItem(scrapy.Item):
    '''
      当当图书的Item 类,包含所有要爬取的信息
    '''

    # define the fields for your item here like:
    # name = scrapy.Field()

    name        = scrapy.Field() # 书名
    price       = scrapy.Field() # 价格
    pic         = scrapy.Field() # 图片链接
    author      = scrapy.Field() # 作者
    publisher   = scrapy.Field() # 出版商
    comments    = scrapy.Field() # 评论数量
    pubdate     = scrapy.Field() # 发行日期
    description = scrapy.Field() # 描述

在spiders子目录中打开pythonbook.py文件,然后输入下面代码,设置下载器开始下载的页面URL。以及爬虫要爬取的HTML结构片段。最后,使用for循环和迭代器,把每一个项目结构片段传递给items.py定义的字段模板。

完成本步操作之前,读者应该先熟悉目标页面的结构,以及要抓取的HTML片段,同时熟悉XPath基本语法,可以先使用浏览器访问该页面,使用F12键,查看一下HTML结构。

import scrapy, re
from dangdang.items import DangdangItem


class PythonbookPySpider(scrapy.Spider):
    name = 'pythonbook'
    allowed_domains = ['search.dangdang.com']
    start_urls = ['http://search.dangdang.com/?key=python&act=input']

    p = 1 # 爬取页数变量

    def parse(self, response):
        '''
          递归解析响应数据
        '''

        print('*'*64)

        dlist = response.selector.xpath(".//ul[@class='bigimg']/li")

        for dd in dlist:
            item = DangdangItem()
            item['name'] = dd.xpath("./a/@title").extract_first() #good
            price = dd.xpath(".//span[@class='search_now_price']").extract_first()
            price = re.findall(".*?([0-9]*\.[0-9]*)",price)
            if(price[0]):
                item['price'] = price[0]
            else:
                item['price'] = None
            item['pic'] = dd.xpath(".//img/@data-original|.//img/@src").extract_first()
            item['author'] = dd.xpath(".//a[@name='itemlist-author']/@title").extract_first()
            item['publisher'] = dd.xpath(".//a[@name='P_cbs']/text()").extract_first() #good
            item['comments'] = dd.xpath(".//a[@class='search_comment_num']/text()").extract_first() #wrong
            item['pubdate'] = dd.re_first("(([0-9]{4})-([0-9]{2})-([0-9]{2}))") #good
            item['description'] = dd.xpath(".//p[@class='detail']/text()").extract_first() # good

            yield item

        self.p += 1

        # 想爬取多少页?
        if(self.p<=10):
            next_url = "http://search.dangdang.com/?key=python&act=input&page_index=" + str(self.p)
            url = response.urljoin(next_url)
            yield scrapy.Request(url=url, callback=self.parse)
  • 第6步,设置配置文件

打开settings.py文件,然后添加下面代码。

           
BOT_NAME = 'dangdang'

SPIDER_MODULES = ['dangdang.spiders']
NEWSPIDER_MODULE = 'dangdang.spiders'

USER_AGENT = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36'

ITEM_PIPELINES = {
    'dangdang.pipelines.DangdangPipeline': 300, # 处理item的类
    'dangdang.pipelines.MysqlPipeline'   : 301, # 把item写入数据库的类
    'dangdang.pipelines.ImagePipeline'   : 302, # 下载图片的类
}
MYSQL_HOST = 'localhost'
MYSQL_DATABASE = 'dangdang'
MYSQL_USER = 'root'
MYSQL_PASS = '11111111'
MYSQL_PORT = 3306
IMAGES_STORE = "./dangdang_images" # 存储图片的文件夹
  • 第7步,编写数据处理脚本

打开pipelines.py文件,输入代码。设计图片下载和数据库存储。

import pymysql
from scrapy.exceptions import DropItem
from scrapy import Request
from scrapy.pipelines.images import ImagesPipeline


class DangdangPipeline(object):
    '''处理item的类'''

    def process_item(self, item, spider):
        '''
        处理爬取数据,如果爬取的书名没有,就把这条记录剔除。
        :param item: 爬虫item类
        :param spider: 爬虫超类
        :return: 剔除条目或者item
        '''
        if item['name'] == None:
            raise DropItem("Drop item found: %s" % item)
        else:
            return item


class MysqlPipeline(object):
    '''处理item写入数据库的类'''

    def __init__(self, host, database, user, password, port):
        '''
        构造类,创建全局变量
        :param host: 数据库主机路径
        :param database: 数据库名称
        :param user: 数据库用户名
        :param password: 数据库密码
        :param port: 数据库端口
        '''
        self.host = host
        self.database = database
        self.user = user
        self.password = password
        self.port = port
        self.db = None
        self.cursor = None

    @classmethod
    def from_crawler(cls, crawler):
        '''
        类方法,用此方法获取数据库相应信息来连接数据库
        :param crawler: crawler类
        :return: 带有数据库信息的类。在这里调用类构造方法
        '''
        return cls(
            host=crawler.settings.get("MYSQL_HOST"),
            database=crawler.settings.get("MYSQL_DATABASE"),
            user=crawler.settings.get("MYSQL_USER"),
            password=crawler.settings.get("MYSQL_PASS"),
            port=crawler.settings.get("MYSQL_PORT")
        )

    def open_spider(self, spider):
        '''
        通过设置文件里的信息链接到数据库,并准备好游标以便后面使用
        :param spider:
        :return:
        '''
        self.db = pymysql.connect(self.host, self.user, self.password, self.database, charset='utf8', port=self.port)
        self.cursor = self.db.cursor()

    def process_item(self, item, spider):
        '''
        执行插入操作,将item相应信息插入数据库并保存
        :param item: 爬虫item类
        :param spider: spider超类
        :return: 爬虫item类
        '''
        sql = "insert into pythonbook(name,price,pic,author,publisher,comments, pubdate, description) values('%s','%s','%s','%s','%s','%s', '%s', '%s')"%(item['name'],item['price'],item['pic'],item['author'],item['publisher'],item['comments'], item['pubdate'],item['description'])
        self.cursor.execute(sql)
        self.db.commit()
        return item

    def close_spider(self, spider):
        '''
        关闭数据库连接
        '''
        self.db.close()


class ImagePipeline(ImagesPipeline):
    '''处理图像的类'''

    def get_media_requests(self, item, info):
        '''
        从item内拿到图片url信息,创建Request等待调度执行
        :param item: 爬虫item类
        :param info: 消息
        :return: None, yield一个带图片URL的Request
        '''
        yield Request(item['pic'])

    def item_completed(self, results, item, info):
        '''
        下载完成后,如果url不是有效的,就筛选掉这个数据。
        :param results:
        :param item:
        :param info:
        :return: 爬虫item类
        '''
        image_paths = [x['path'] for ok, x in results if ok]
        if not image_paths:
            raise DropItem("Item contains no images")

        return item

到上面一步,已经完成了爬虫程序的全部设计,下面就可以测试爬虫的工作状态了,在命令行窗口,使用cd命令进入dangdang目录,然后使用scrapy crawl命令进行测试。

           
scrapy crawl pythonbook

执行结果如下图:
在这里插入图片描述
在这里插入图片描述


http://www.niftyadmin.cn/n/5442605.html

相关文章

python应用打包分发纪要

农夫山泉&#xff0c;喝起来有点甜&#xff1b;python应用&#xff0c;运行起来有点慢。但&#xff0c;除了慢&#xff0c;它就别无缺点了。 文章目录 前言Python程序分发思考Cythonpyinstallernuitkaccachenuitka打包原理nuitka主要参数示例: 打包PySide6应用 参考链接 前言 …

数据库迁移测试

数据库 数据源变更&#xff1a;数据库名称、数据库类型等&#xff0c;一般情况下在同类型的数据库之间迁移比较常见&#xff0c;但是也存在不同类型的数据库之间的迁移&#xff0c;例如&#xff1a;oracle到MySQL等等&#xff0c;非关系型数据库到关系型&#xff0c;关系型到非…

STM32CubeMX+freeRTOS+事件组 多任务处理LED和串口打印

摘要:利用CubeMx配置freeeRTOS建立任务并使用事件组实现按键按下时 LED开关和打印信息到串口,上位机接收显示。 验证STM32CubeMx配置的FreeRTOS的任务和事件组使用 方案:按下Key1,绿灯亮或者灭,同时串口打印Key1被按下了到上位机;相关端口和串口配置省略。 新建三个任务…

Linux手动创建用户不使用useradd【七步走完成】

文章目录 第一步&#xff1a;修改 /etc/passwd 文件第二步&#xff1a;修改 /etc/shadow 文件第三步&#xff1a;修改 /etc/group 文件第四步&#xff1a;新建用户家目录第五步&#xff1a;复制/etc/skel目录下的环境变量配置文件到家目录下第六步&#xff1a;修改家目录的权限…

伊理威科技:抖音店铺运营好做吗

在数字营销的浪潮中&#xff0c;抖音以其强大的用户基础和独特的算法推荐机制成为了众多商家眼中的“香饽饽”。然而&#xff0c;对于许多初涉此领域的商家来说&#xff0c;心中不免有这样的疑问&#xff1a;“抖音店铺运营好做吗?” 运营一个抖音店铺并非易事。它既需要创意的…

org.springframework.beans.factory.BeanNotOfRequiredTypeException异常处理

目录 一、问题详情 二、示例代码 三、原因分析 四、解决方案 一、问题详情 在本地启动项目的时候,突然报了如下错误,导致整个项目启动失败了。 org.springframework.bea

java 重载和重写的区别

在开始说它们之间的区别之前&#xff0c;可以先去了解这两个是什么&#xff1f; 重载可参考链接 java方法重载-CSDN博客 重写可参考链接 java 方法重写-CSDN博客 1、方法重载 &#xff08;1&#xff09;方法在同一个类中&#xff1b; &#xff08;2&#xff09;方法的名字必…

从零开始学习typescript系列5: typescript特殊符号之感叹号、问号、双问号

感叹号 感叹号&#xff1a;强制链式调用 表示&#xff1a;这个字段一定要存在实际&#xff1a;如果不存在 》 会运行时报错 function logFun(p: any): void {// 真实情况: p.fruite是存在的console.log(fruit name:, p.fruit!.name); // fruit name: banana// 真实情况: p是…