爬虫 scrapy ——scrapy shell调试及下载当当网数据(十一)

目录

一、scrapy shell

1.什么是scrapy shell?

 2.安装 ipython

3.使用scrapy shell

二、当当网案例

1.在items.py中定义数据结构

2.在dang.py中解析数据

3.使用pipeline保存

4.多条管道的使用

5.多页下载

参考


一、scrapy shell

1.什么是scrapy shell?

什么是scrapy shell?

        scrapy终端,是一个交互终端,供您在未启动spider的情况下尝试及调试您的爬取代码。其本意是用来测试提取数据的代码,不过您可以将其作为正常的python终端,在上面测任何的python代码。该终端是用来测试Xpath或css表达式,查看他们的工作方式及从爬取的网页中提取的数据。在编写您的spider时,一旦熟悉了scrapy终端后,您会发现其在开发和调试spider时发挥的最大作用。

 2.安装 ipython

安装ipython

        pip install ipython 

        安装ipython后,scrapy终端将使用ipython代替python终端,ipython终端与其他相比更为强大,提供智能的自动补全,高亮输出及其他特性。

3.使用scrapy shell

在终端输入以下命令

scrapy shell 域名

eg:scrapy shell www.baidu.com

输出:进入到ipython

以上命令返回了一个response,可以直接使用

如下所示:可以调试返回的结果

二、当当网案例

目标:爬取当当网目标图书类目的所有图片、书名和价格,实现三者并行下载。

1.在items.py中定义数据结构

定义要获取的图片、书名和价格

class Scrapy095Item(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    # 通俗地讲就是你下载的数据都有什么

    # 爬取图片
    img = scrapy.Field()
    # 爬取书名
    name = scrapy.Field()
    # 爬取价格
    price = scrapy.Field()

    pass

2.在dang.py中解析数据

同时下载书名、图片和价格,找到三者共在的标签 ‘ul’

定位Xpath路径,我们之前是这样写的,获取了每个内容的列表,但是我们想要的是书名、图片和价格相对应的结果。

# 找到三者共同所在的标签
img = response.xpath('//ul[@id="component_59"]/li//img/@src')
name = response.xpath('//ul[@id="component_59"]/li//img/@alt')
response.xpath('//ul[@id="component_59"]/li//p[@class="price"]/span[1]/text()')

所以我们现在这样写:

调用selector下的Xpath,可以同时获取一个 li 中的三个内容。

# 所有selector对象可以在此调用 Xpath方法
li_list = response.xpath('//ul[@id="component_59"]/li')
for li in li_list:
    img = li.xpath('.//img/@src').extract_first()
    name = li.xpath('.//img/@alt').extract_first()
    price = li.xpath('.//p[@class="price"]/span[1]/text()').extract_first()
    print(img,name,price)

这样就获取到了。 

但是发现,图片全都为 “none”,这是因为网页的懒加载造成的,避免网页一下子加载太多数据。

所以我们要找到真正的图片链接,即 ‘data-original’,而不是‘src’。

然后我们修改路径,得到下面结果。

又发现了问题,我们并没有拿到第一个数据的链接,因为第一个数据没有‘data-original’属性。

修改为以下代码

 # 所有selector对象可以在此调用 Xpath方法
li_list = response.xpath('//ul[@id="component_59"]/li')
for li in li_list:
    # 第一章图片的链接在 src 里
    # 其余图片的链接在 data-original 里
    img = li.xpath('.//img/@data-original').extract_first()
    if img:
         img = img
    else:
         img = li.xpath('.//img/@src').extract_first()

     name = li.xpath('.//img/@alt').extract_first()
     price = li.xpath('.//p[@class="price"]/span[1]/text()').extract_first()
     print(img,name,price)

 这样我们就获取到了所有数据

3.使用pipeline保存

将数据交给 pipeline,添加最后两行代码。

调用 items.py 中的 Scrapy095Item 类。其中img=,name=和price=为 items.py中定义的变量。

# 所有selector对象可以在此调用 Xpath方法
li_list = response.xpath('//ul[@id="component_59"]/li')
for li in li_list:
    # 第一章图片的链接在 src 里
    # 其余图片的链接在 data-original 里
    img = li.xpath('.//img/@data-original').extract_first()
    if img:
       img = img
    else:
       img = li.xpath('.//img/@src').extract_first()

    name = li.xpath('.//img/@alt').extract_first()
    price = li.xpath('.//p[@class="price"]/span[1]/text()').extract_first()
    print(img,name,price)

    book = Scrapy095Item(img=img,name=name,price=price)

    # 将 book 交给 pipeline 下载
    yield book

什么是yield?        

        带有yield的函数可以视作一个生成器generator,可用于迭代。yield是一个类似于return的关键字,迭代一个遇到yield时就返回yield后面的值。重点是:下一次迭代时,从上一次迭代遇到的yield后面的代码开始执行。

        也就是说,yield会不断把book传递给pipeline。

如果要使用管道的话,就要在 settings.py 中开启管道,解开注释。

在 pipelines.py 中保存数据

# 如果要使用管道的话,就要在 settings.py 中开启管道
class Scrapy095Pipeline:
    # item 就是 yield 的返回值
    def process_item(self, item, spider):
        # 保存数据
        with open('book.json','a', encoding='utf-8') as file:
            # 存在的问题
            # item 是一个对象,需要将其转换为 str
            # 写文件的方式要改为 ‘a’ 追加模式,而不是 ‘w’覆盖模式。
            file.write(str(item))

        return item

需要注意的是:

        item 是一个对象,需要将其转换为 str

        写文件的方式要改为 ‘a’ 追加模式,而不是 ‘w’覆盖模式。

这样就把内容保存下载来了

但是这样写文件的缺点是,写数据时需要频繁的打开关闭文件,对文件的操作过于频繁。

所以我们只要打开并关闭一次文件

定义两个函数 open_spider 和 close_spider ,这两个函数是 scrapy的内置函数,可以操作文件只打开或者关闭一次。

# 如果要使用管道的话,就要在 settings.py 中开启管道
class Scrapy095Pipeline:
    # 在爬虫文件开始之前就执行的一个文件
    def open_spider(self, spider):
        print('++++++++++++++++++++++++++')
        self.fp = open('book.json','w',encoding='utf-8')

    # item 就是 yield 的返回值
    def process_item(self, item, spider):
        # 我们不这样保存
        # # 保存数据
        # with open('book.json','a', encoding='utf-8') as file:
        #     # 存在的问题
        #     # item 是一个对象,需要将其转换为 str
        #     # 写文件的方式要改为 ‘a’ 追加模式,而不是 ‘w’覆盖模式。
        #     file.write(str(item))

        self.fp.write(str(item))

        return item

    # 在爬虫文件执行完之后再执行的方法
    def close_spider(self, spider):
        print('----------------------')
        self.fp.close()

4.多条管道的使用

在 pipelines.py 中添加一个类,模仿上一个类写,用来下载图片,注意,这个类中定义的方法要与上一个类相同,然后我们在这个类中写下载图片的代码,最后返回 item

import urllib.request
# 多条管道开启
# (1)定义管道类
# (2)在settings中开启管道
class Scrapy095_download_Pipeline:
    def process_item(self, item, spider):

        url = 'http:' + item.get('img')
        filename = './books/' + item.get('name') + '.jpg'
        urllib.request.urlretrieve(url=url, filename=filename)
        return item

重要的是,我们要为下图片创建一个新管道,才能实现JSON数据保存和图片下载的同时进行。

在 settings.py 中新添加一个管道,修改的名字就是我们定义的类名。

这样再运行爬虫文件,就可以得到JSON文件和所有的图片了。

5.多页下载

找一下每一页的url之间的规律

# http://category.dangdang.com/pg2-cp01.36.04.00.00.00.html

# http://category.dangdang.com/pg3-cp01.36.04.00.00.00.html

# http://category.dangdang.com/pg4-cp01.36.04.00.00.00.html

可以看到,只有page不一样

所以我们可以在 dang.py 的类中定义一个url_base。

url_base = 'http://category.dangdang.com/pg'
page = 1

然后在 parse方法中添加以下代码

使用 yield 将新的url再传递给 parse() 方法。

# 多个页面的请求
# 每一页爬取的业务逻辑都是一样的,所以我们只需要将执行的那个页的请求再次调用parse方法
# http://category.dangdang.com/pg2-cp01.36.04.00.00.00.html
# http://category.dangdang.com/pg3-cp01.36.04.00.00.00.html
# http://category.dangdang.com/pg4-cp01.36.04.00.00.00.html

if self.page < 10:
     self.page = self.page + 1
     url = self.url_base + str(self.page) + '-cp01.36.04.00.00.00.html'

     # 怎么调用 parse 方法
     # scrapy.Request 就是scrapy的get请求
     # url 就是请求地址,callback就是你要执行的那个函数,不需要加‘ () ’
     yield scrapy.Request(url=url, callback=self.parse)

完整代码:

dang.py

import scrapy
from ..items import Scrapy095Item

class DangSpider(scrapy.Spider):
    name = 'dang'
    # 如果是多页下载,allowed_domains只保留域名,去掉协议和地址,为的是扩大允许范围
    allowed_domains = ['category.dangdang.com']
    start_urls = ['http://category.dangdang.com/cp01.36.04.00.00.00.html']

    url_base = 'http://category.dangdang.com/pg'
    page = 1

    def parse(self, response):
        print('=============================')
        # pipeline  下载数据
        # items     定义数据结构

        # 找到三者共同所在的标签
        # img = response.xpath('//ul[@id="component_59"]/li//img/@data-original')
        # name = response.xpath('//ul[@id="component_59"]/li//img/@alt')
        # price = response.xpath('//ul[@id="component_59"]/li//p[@class="price"]/span[1]/text()')

        # 所有selector对象可以在此调用 Xpath方法
        li_list = response.xpath('//ul[@id="component_59"]/li')
        for li in li_list:
            # 第一章图片的链接在 src 里
            # 其余图片的链接在 data-original 里
            img = li.xpath('.//img/@data-original').extract_first()
            if img:
                img = img
            else:
                img = li.xpath('.//img/@src').extract_first()

            name = li.xpath('.//img/@alt').extract_first()
            price = li.xpath('.//p[@class="price"]/span[1]/text()').extract_first()
            print(img,name,price)

            book = Scrapy095Item(img=img,name=name,price=price)

            # 将 book 交给 pipeline 下载
            yield book


        # 多个页面的请求
        # 每一页爬取的业务逻辑都是一样的,所以我们只需要将执行的那个页的请求再次调用parse方法
        # http://category.dangdang.com/pg2-cp01.36.04.00.00.00.html
        # http://category.dangdang.com/pg3-cp01.36.04.00.00.00.html
        # http://category.dangdang.com/pg4-cp01.36.04.00.00.00.html

        if self.page < 10:
            self.page = self.page + 1
            url = self.url_base + str(self.page) + '-cp01.36.04.00.00.00.html'

            # 怎么调用 parse 方法
            # scrapy.Request 就是scrapy的get请求
            # url 就是请求地址,callback就是你要执行的那个函数,不需要加‘ () ’
            yield scrapy.Request(url=url, callback=self.parse)

        print('=============================')

items.py

# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html

import scrapy


class Scrapy095Item(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    # 通俗地讲就是你下载的数据都有什么

    # 爬取图片
    img = scrapy.Field()
    # 爬取书名
    name = scrapy.Field()
    # 爬取价格
    price = scrapy.Field()

    pass

pipelines.py

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html


# useful for handling different item types with a single interface
from itemadapter import ItemAdapter

# 如果要使用管道的话,就要在 settings.py 中开启管道
class Scrapy095Pipeline:
    # 在爬虫文件开始之前就执行的一个文件
    def open_spider(self, spider):
        print('++++++++++++++++++++++++++')
        self.fp = open('book.json', 'w', encoding='utf-8')

    # item 就是 yield 的返回值
    def process_item(self, item, spider):
        # 我们不这样保存
        # # 保存数据
        # with open('book.json','a', encoding='utf-8') as file:
        #     # 存在的问题
        #     # item 是一个对象,需要将其转换为 str
        #     # 写文件的方式要改为 ‘a’ 追加模式,而不是 ‘w’覆盖模式。
        #     file.write(str(item))

        self.fp.write(str(item))

        return item

    # 在爬虫文件执行完之后再执行的方法
    def close_spider(self, spider):
        print('----------------------')
        self.fp.close()


import urllib.request
# 多条管道开启
# (1)定义管道类
# (2)在settings中开启管道
class Scrapy095_download_Pipeline:
    def process_item(self, item, spider):

        url = 'http:' + item.get('img')
        filename = './books/' + item.get('name') + '.jpg'
        urllib.request.urlretrieve(url=url, filename=filename)
        return item

settings.py 中只 取消ROBOTSTXT_OBEY的注释,并添加下面的管道。

# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
    # 管道可以有很多个,但管道是有优先级的,优先级范围是 1-1000, 值越小,优先级越高。
   'scrapy_095.pipelines.Scrapy095Pipeline': 300,
   'scrapy_095.pipelines.Scrapy095_download_Pipeline': 301,
}

参考

尚硅谷Python爬虫教程小白零基础速通(含python基础+爬虫案例)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/244059.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

行业锦囊|住建厅CA证书使用指南

数字化、高效率、低成本已成为企业发展转型的重要考量因素&#xff0c;建筑工程企业也不例外。而工程资料签署和管理又是每个建筑工程企业发展转型绕不开的课题&#xff1a;在建工程项目异地盖章周期性长、庞大的签署量及海量的文件管理如何优化&#xff1b;企业如何缩短对公业…

AXURE地图获取方法

AXURE地图截取地址 https://axhub.im/maps/ 1、点击上方地图或筛选所需地区的地图&#xff0c;点击复制到 Axure 按钮&#xff0c;到 Axure 粘贴就可以了 2、复制到 Axure 后&#xff0c;转化为 svg 图形&#xff0c;就可以随意更改尺寸/颜色/边框&#xff0c;具体操作如下&am…

【AI美图】第01期效果图,AI人工智能无绘画,美图欣赏

人工智能被各大平台传递&#xff0c;我也来凑一下热闹&#xff0c;放一组基础图片展示 介绍一下模型来源Stability AI&#xff1a; Stability AI直接将模型开源&#xff0c;且方式之粗暴&#xff0c;几乎完全不对生成内容做任何审核或者过滤。目前发布的稳定版本仅包含部分关…

ProcessOn基本介绍和完成案列和自定义元件

ProcessOn基本介绍和完成案列和自定义元件 1.什么是ProcessOn2.初步使用ProcessOn3.使用ProcessOn完成医疗项目门诊模块的流程图4.使用ProcessOn完成医疗项住院模块的流程图5.使用ProcessOn完成医疗项目药房模块的流程图5.使用ProcessOn完成会议oa模块的流程图6.自定义原件库1.…

设计表单表格组件

前言 什么是表单表格呢&#xff1f;简单来说就是在一个表格里面进行表单操作&#xff0c;执行增删改查。这在一些后台管理系统中是尤为常见的。 今天我们根据vue2 element-ui来设计一个表单表格组件。&#xff08;不涉及完整代码&#xff0c;想要使用完整功能可以看底部连接…

超级计算机行业分析:中国市场概念及发展现状研究

近年来&#xff0c;在技术研发和产业应用的共同推动下&#xff0c;中国超级计算机(简称“超算”)快速发展——技术创新方面&#xff0c;采用自主研发芯片的多个国内超算曾在世界超算榜单上排名第一;整体规模方面&#xff0c;在最新发布的全球超算榜单中&#xff0c;中国占比超过…

<博图> 浮点数(real)相加时结果不精确的解决方法

问题截图&#xff1a; 每按下一次按钮&#xff0c;浮点数1 加0.1&#xff0c;结果放在浮点数1中&#xff1b;在加到第8次时会多出小数点位数。 解决方法&#xff1a; 对要进行相加的浮点数进行转换&#xff0c;如下 &#xff1a; &#xff08;注&#xff1a;如果图片模糊保存…

流程图、泳道图的介绍和示例分享,以及自定义元件库的介绍

目录 一. 流程图介绍 二. Processon使用 新建一个流程图 图形的使用 三. 流程图示例 登录界面 门诊业务流程图 住院业务流程图 药房业务流程图 会议OA流程图 四. 泳道图介绍 五. 自定义元件库 5.1 新建一个元件库 5.2 创建元件 5.3 使用自定义元件库 一. 流程图介…

四六级高频词组9

目录 词组 其他链接 词组 351. be guilty of 犯有…罪或过失 352. be in the habit of 习惯于 353. break off &#xff08;a habit&#xff09; 改掉&#xff08;某种习惯&#xff09; 354. break sb. of &#xff08;a habit&#xff09;使某人改掉&#xff08;某习惯&a…

UI自动化Selenium 测试报告BeautifulReport使用及修改

一、BeautifulReport安装 pip安装 pip install BeautifulReport Pycharm中安装 二、原生报告样式 原生报告&#xff0c;因为我使用ddtunittest数据驱动模式&#xff0c;所以Excel中所有参数都会被拼接出来&#xff0c;导致测试方法里面有太多不需要展示的内容&#xff1b; …

关于impdp导入时候索引是否使用了并行了?

关于impdp导入时候索引是否使用了并行的问题&#xff0c;不是看sqlfile&#xff0c;而是看实际worker 参看&#xff1a;Impdp Parallel Index Creation Always Creates Indexes with Degree 1 (Doc ID 1289032.1&#xff09; Oracle Database - Enterprise Edition - Version …

JavaScript如果实现一段文字的高亮显示

JavaScript实现将一段文字检索高亮显示,效果如下: 实现方法:调用highlight函数 highlight("JavaScript如果实现一段文字的高亮显示", "高亮")实战代码实现: function highlight(value, search)

android studio 创建按钮项目

1&#xff09;、新建一个empty activity项目&#xff0c;切换到project视图&#xff1a; 2&#xff09;、修改app\src\main\res\layout\activity_main.xml文件&#xff0c;修改后如下&#xff1a; <?xml version"1.0" encoding"utf-8"?> <andr…

人机融合与意图理解

人机融合本质上是人类智能与机器自动化之间的协同。 人机融合的目标是利用人类智能和机器自动化的优势&#xff0c;使二者相互补充、相互支持&#xff0c;共同实现更高效、更智能的工作和生活方式。 人类智能和机器自动化具有不同的特点和优势。人类智能具有创造性、灵活性、推…

B站武sir-django教程(2)

day16 Django开发 主题&#xff1a;员工管理系统 1.新建项目 2.创建app python manage.py startapp app01注册app&#xff1a; 3.设计表结构&#xff08;django&#xff09; from django.db import modelsclass Department(models.Model):""" 部门表 "&…

38.如何让自动注入找到多个依赖Bean时不报错

1、引言 当我们从 Spring 容器中“拉”取一个 Bean 回来的时候&#xff0c;可以按照名字去拉取&#xff0c;也可以按照类型去拉取&#xff0c;按照 BeanName 拉取的话&#xff0c;一般来说只要 BeanName 书写没有问题&#xff0c;都是没问题的。但是如果是按照类型去拉取&…

四六级高频词组8

目录 词组 其他链接 词组 301. in fashion&#xff08;stylish&#xff0c; most modern&#xff09;时兴&#xff0c;流行 302. after the fashion &#xff08;of&#xff09; 依照… 303. find fault with&#xff08;complain about&#xff1b;criticize&#xff09;找…

Spring Bean基础

写在最前面: 本文运行的示例在我github项目中的spring-bean模块&#xff0c;源码位置: spring-bean 前言 为什么要先掌握 Spring Bean 的基础知识&#xff1f; 我们知道 Spring 框架提供的一个最重要也是最核心的能力就是管理 Bean 实例。以下是其原因&#xff1a; 核心组件…

Nginx(四层+七层代理)+Tomcat实现负载均衡、动静分离

一、Tomcat多实例部署 具体步骤请看我之前的博客 写文章-CSDN创作中心https://mp.csdn.net/mp_blog/creation/editor/134956765?spm1001.2014.3001.9457 1.1 访问测试多实例的部署 1.2 分别在三个tomcat服务上部署jsp的动态页面 mkdir /usr/local/tomcat/webapps/test vim …

STM32-固件打包部署

STM32-固件打包部署 Fang XS.1452512966qq.com STM32固件输出 工程上使用Keil开发STM32软件&#xff1b;在调试过程中&#xff0c;可直接编译下载&#xff1b;例如bootloader和APP&#xff0c;在调试时&#xff0c;可以直接下载2次&#xff1b;但是工程上&#xff0c;需要大…