ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Python爬虫学习笔记_DAY_30_1万字快速上手scrapy框架多管道数据下载【Python爬虫】

2022-02-24 14:03:51  阅读:167  来源: 互联网

标签:文件 py src Python self 30 爬虫 item scrapy


p.s.高产量博主,点个关注不迷路!(文章较长,赶时间可以点个收藏或直接跳转完整源码)

目录

I. 实战需求分析与思路

II. 接口的获取与scrapy项目的创建

III.items数据结构文件配置

IV. 爬虫文件的书写

V. 管道的配置

VI. 多页下载处理

VII. 完整源码


I. 实战需求分析与思路

首先,笔记承接上一篇,我们知道一个完整的scrapy框架项目文件有六个部分:

1️⃣ Spiders文件夹:这文件夹我们不陌生,因为每一次新建scrapy爬虫项目后,我们都需要终端进入Spiders文件夹,生产爬虫文件。在Spiders文件夹下,又有两个文件,一个是_init_.py文件,一个是tc.py。_init_.py文件是我们创建项目时默认生成的一个py文件,我们用不到这个py文件,因此我们可以忽略它,另一个tc.py文件是我们爬虫的核心文件,后续的大部分代码都会写入这个文件,因此它是至关重要的py文件。

2️⃣_init_.py文件:它和上面提到的Spiders文件夹下的_init_.py一样,都是不被使用的py文件,无需理会。

3️⃣ items.py文件:这文件定义了数据结构,这里的数据结构与算法中的数据结构不同,它指的是爬虫目标数据的数据组成结构,例如我们需要获取目标网页的图片和图片的名称,那么此时我们的数据组成结构就定义为 图片、图片名称。后续会专门安排对scrapy框架定义数据结构的学习。

4️⃣ middleware.py文件:这py文件包含了scrapy项目的一些中间构件,例如代理、请求方式、执行等等,它对于项目来说是重要的,但对于我们爬虫基础学习来说,可以暂时不考虑更改它的内容。

5️⃣ pipelines.py文件:这是我们之前在工作原理中提到的scrapy框架中的管道文件,管道的作用是执行一些文件的下载,例如图片等,后续会安排对scrapy框架管道的学习,那时会专门研究这个py文件。

6️⃣ settings.py文件:这文件是整个scrapy项目的配置文件,里面是很多参数的设置,我们会偶尔设计到修改该文件中的部分参数,例如下一部分提到的ROBOTS协议限制,就需要进入该文件解除该限制,否则将无法实现爬取。

本次笔记重点针对于pipelines.py文件与items.py文件的使用进行讲解,并辅以实战。首先我们简单做一下实战的需求分析:

我们打开当当网的首页,之后任意点击一种图书,例如这里点击【青春文学】: 

之后可以再选择一个子类,例如我们选择【爱情/情感】:

之后我们的实战需求是把最终打开的这个页面中的图书图片、价格、图片名称一起下载下来,要求必须同步下载数据(开启多管道),并且能实现多页下载。 

于是我们可以做简单的分析:

首先,我们需要创建项目文件,生成爬虫文件;接下来,从response中提取我们需要的数据;最后是把数据放入管道下载,这是一个简单的思路,省略了一些步骤,但总体上来看思路是可行的。

最后我们应该能获得:


II. 接口的获取与scrapy项目的创建

接下来,我们开始创建项目

打开之前创建过的文件夹,用终端进入这个文件夹(如果之前笔记没有看过的朋友,直接新建一个空文件夹即可,之后用终端进入该文件夹):

运行项目生成指令:

scrapy startproject dangdang

之后我们不着急新建爬虫文件,因为此时我们缺少目的地的url,于是我们需要简单的抓一下接口:

这个接口不需要通过F12检查网页,只需要直接复制刚才进入的【爱情/情感】页面的url即可(通过F12也可以找到接口,接口就是这个页面的url,因此无需用F12)

http://category.dangdang.com/cp01.01.02.00.00.00.html

拿到之后,我们可以生成爬虫文件:

首先终端进入Spiders文件夹

cd scrapy_dangdang/scrapy_dangdang/spiders

运行爬虫文件生成指令
 

scrapy genspider dangdangwang http://category.dangdang.com/cp01.01.02.00.00.00.html

其中 "dangdangwang" 是生成的爬虫文件的文件名,大家可以任意起名

正确生成后,我们可以用pycharm打开项目文件,打开后我们点击爬虫文件dangdangwang.py,先把默认多生成的http://头和html后面的斜线都删去,否则影响它的工作。 

最后我们的基础操作还剩下一步:上次笔记提到了我们有一个参数:allowed_domains,这是我们整个项目爬取的url的范围,由于本次我们的需求是爬去三种数据,无法固定某一个url,因此我们修改allowed_domains的参数为:

allowed_domains = ['category.dangdang.com']

此时它代表了整个的域名,也就是域名下的其他子域名或者子url都被包括,这个操作在大型项目中也经常会遇到,我们只需要把allowed_domains的参数修改成目的网站的域名即可


III.items数据结构文件配置

接下来,我们需要配置一下items数据结构文件,此时我们先不要尝试理解它,而是先去做:

打开items.py文件:

我们刚才提到了一共要下载三样东西:图片、图片的名称以及图片对应图书的单价,于是我们可以理解成我们需要下载的目标数据结构有三种类型:图片(src)、图片名称和单价。

那么我们可以在items.py文件中写入:

    # 图片的url
    src = scrapy.Field()
    # 图片名称
    name = scrapy.Field()
    # 价格
    price = scrapy.Field()

它的格式是:数据类型的名称 = scrapy.Field(),理解起来可能初学不太容易,但是我们可以先这么写,这些内容统统写在 class ScrapyDangdangItem(scrapy.Item): 下面


IV. 爬虫文件的书写

定义数据结构之后,我们可以开始准备写核心爬虫文件,在此之前,我们还需要一项工作:

分析页面源码,并得出xpath解析语句,解析之前提到的三种数据

我们回到刚才的当当网页面,按F12解析页面,选择元素检查,把鼠标放到第一本书的图片上:

可以看出,我们需要的图片src和名称分别在img标签的src和alt属性中,于是xpath语法是这样的:

//ul[@id = "component_59"]/li//img/@src
//ul[@id = "component_59"]/li//img/@alt

但是在当当网中,有个小陷阱,那就是当我们把鼠标放在第二张图时,可以发现它的图片的src并不在src属性下,而是在data-original中

于是针对第一张图片的src,我们可以采用上面的xpath语法,其他的页面的图片src,我们用下面这句语法

//ul[@id = "component_59"]/li//img/@data-original

图书的价格,我们发现可以用这句语法拿到:

//ul[@id = "component_59"]/li//p[@class = "price"]/span[1]/text()

xpath语句准备好之后,我们开始编写爬虫文件:

打开dangdangwang.py文件,代码书写的区域应该聚焦在parse(self,response)这个函数中(原因不做赘述,可以参考上一篇笔记)

我们先简化一下目标,把多页爬取先简化成爬取第一页的上述三种数据,那么关于响应与提取数据部分代码应该是这样的

 def parse(self, response):
        # 所有的selector对象都可以调用xpath方法
        li_list = response.xpath('//ul[@id = "component_59"]/li')

        # 判断一下,第一张图是src,第二张图开始在data-original里
        for li in li_list:
            src = li.xpath('.//img/@data-original').extract_first()
            if src:
                src = src
            else:
                src = li.xpath('.//img/@src').extract_first()

            name = li.xpath('.//img/@alt').extract_first()

            price = li.xpath('.//p[@class = "price"]/span[1]/text()').extract_first()

这部分需要做一个简单的注解:

按照传统的思路,例如我们要在scrapy框架下,用xpath解析目的地的图片,那么我们应该直接写上:

src = response.xpath('//ul[@id = "component_59"]/li//img/@src或@data-original')

但是这里我们没有采用这种直接的方式,而是首先用一个xpath语句:

li_list = response.xpath('//ul[@id = "component_59"]/li')

然后对这个li_list进行二次xpath解析。这是在scrapy框架下的一种特有的写法,它的依据是在scrapy框架下,.xpath()方法并不会直接返回我们的数据列表,而是会返回一个selector对象(上一篇笔记中有解释),而对于一个selector对象,可以再次xpath,于是有了这种写法:先总体xpath,解析后对每一个部分,继续在剩余的部分中进行xpath解析。


V. 管道的配置

完成xpath解析后,拿到了需要的数据。根据我们的整体思路,这些数据中的一部分需要放到管道中执行下载。回想scrapy框架的工作原理,管道负责下载一些文件,这里我们实战中,这个文件指的是图书的图片、图书的价格和名称(保存在json文件)!接下来,我们开始学习管道的配置

1️⃣ 首先,我们需要封装一下管道文件:

打开pipelines.py,并把注意力放在这个class中,更确切地说是process_item()函数中: 

这函数是干什么的呢?是在爬虫文件调用了管道(调用的代码后面会再解释,这里先知道是当调用的时候)时,会执行的一个函数,我们可以简单理解为被爬虫文件调用的某个函数。这个函数的主要功能就是下载

那我们首先先解决把图片的src、图片名称和图片价格三个数据写入json文件中并下载json文件这项任务,下载图片后续再处理。

class ScrapyDangdangPipeline:
    def open_spider(self, sipder):
        self.fp = open('book.json','w',encoding = 'utf-8')

    def process_item(self, item, spider):
        self.fp.write(str(item))
        return item

    def close_spider(self,spider):
        self.fp.close()

这可以由上面的代码实现。解释一下上面的部分:

首先,我们原来只有一个process_item()函数,但是如果只有一个下载的函数,我们每次向文件写入后都要执行文件关闭,下一次执行文件的打开,这会导致频繁的操作文件的打开与关闭,不利于我们的目的,于是我们采用另一种方法:在管道文件中,除了process_item()函数外,还有两个函数,分别是:open_spider()和close_spider(),这两个函数不会被初始化生成,但是我们可以手动添加这两个函数,它们的特点是:

open_spider()函数和close_spider()函数分别在项目的启动和终止时各自被调用一次。

于是我们通过这个特点,在open_spider()函数中打开文件,在close_spider()中关闭文件,在process_item()中执行写入操作,即可避免频繁的文件打开关闭。(使用self.fp能保证三个函数操作的是同一个文件对象)

比葫芦画瓢,由于要下载的还有图片,我们在初始的class下面自行定义一个新的class,并在class下定义process_item()函数,在这个process_item()函数中写上图片的下载操作

import urllib.request
# 图片下载管道:
class DangDangDownloadImgPipeline:
    def process_item(self, item, spider):
        url = 'http:' + item.get('src') # 这里是因为item本身是获取的json数据,存在字典里,所以我们用字典的get函数获取
        filename = './books/' + item.get('name') + '.jpg'
        urllib.request.urlretrieve(url = url,filename = filename)
        return item

别忘了导入urllib.request库,我们是通过这个库下载图片的(这个如果不知道的话,可以去看之前的笔记)。

所以现在的pipelines.py文件应该是这样的


2️⃣ 修改settings.py文件,使管道可以被使用:

第一步中,我们为下载图片,新定义了一个class,那么可能会有一个疑惑点:我们新建的class会被项目运行吗?这里给出明确的答案如果没有第二步修改settings.py文件,那么新建的class下的代码"不会"被执行,而且甚至初始的class也不会被执行。所以第二步是必不可少的,也是容易被忽略的。

我们打开settings.py文件找到有一个被注释了的ITEM_PIPELINES字典,这就是定义管道的地方,我们首先需要解除它的注释: 

这个字典中,键值对的值是一个300,它的意思是管道的优先级,也就是下载的优先级 ,这个值在1-1000中,值越小,优先级越高

我们复制这个字典对象,然后在它下方粘贴一下把pipeline.ScrapyDangdangPipeline改成我们自己起的类名

到这里,我们完成了管道文件的配置,不过还没有完成全部内容,我们还需要填一个坑:在爬虫文件中调用管道文件


3️⃣ 在爬虫文件中调用管道文件:

首先,我们需要在爬虫文件中导入之前定义的items数据结构导入的格式是这样的:

from 项目名.items import items.py文件中的类名

对于本项目,应该是这样的:

from scrapy_dangdang.items import ScrapyDangdangItem

导入后 是这样的:

这之后,可能会爆红线错误,但是我们不需要理会,继续做就好。

接下来,在爬虫文件中紧挨着刚才写的xpath解析的下一行,写上两句代码:

book = ScrapyDangdangItem(src = src,name = name,price = price)

# 获取一个book对象,就传入pipeline进行下载
yield book

第一句代码,是生成一个数据结构对应的对象,我们需要调用刚才导入的items文件下的ScrapyDangdangItem()类,这部分对于学过面向对象编程的朋友来说不难理解。

第二句代码是真正调用了管道,这句代码执行后,管道就被调用去下载文件

因此此时我们的爬虫文件是这样的:


VI. 多页下载处理

最后,接近本次实战的尾声,我们对多页下载需求进行一个满足

多页时,我们首先需要在爬虫文件中修改allowed_domains为当当网的域名,这一点之前已经提到,也已经修改了,于是不做赘述。

接下来,我们发现当当网除了第一页之外,其他页面的url有下面的规律

http://category.dangdang.com/pg页码-cp01.01.02.00.00.00.html

也就是说我们下载了第一页之后,后面的页面的url可以由http://category.dangdang.com/pg + 页码 + -cp01.01.02.00.00.00.html生成。

于是我们首先在爬虫文件最上面定义全局变量:初始url段和页码page

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

在parse()函数的for循环外,写上下面的代码:

        if self.page < n:
            self.page = self.page + 1

            url = self.base_url + str(self.page) + '-cp01.01.02.00.00.00.html'
        # 调用parse函数
        # scrapy.Request是scrapy的get请求:
        # url是请求地址,callback是要执行的函数,不需要加圆括号
            yield  scrapy.Request(url = url,callback = self.parse)

解释一下这段代码:

我们在for循环外判断当前的page是否小于某个数nn代表我们想要下载多少页,之后我们拼接出url,用yield关键字,我们可以调用一个叫Request的函数这个函数是在执行了第一次Request请求后会调用的函数,可理解成是一个回调函数,在这个回调函数中,传参是url和需要回调的具体函数,我们很自然填入拼接后的url,回调函数我们就继续回调本函数parse即可。

也即这样的逻辑:项目第一次执行Request请求 - - - > 请求成功,回调了scrapy.Request()参数中的callback函数 - - - > callback函数仍然会发起第二次Request请求,直至page等于n结束。


VII. 完整源码

最后附上本次实战的源码:

1️⃣ 爬虫文件dangdang.py:

import scrapy
from scrapy_dangdang.items import ScrapyDangdangItem

class DangdangwangSpider(scrapy.Spider):
    name = 'dangdangwang'

    allowed_domains = ['category.dangdang.com']
    start_urls = ['http://http://category.dangdang.com/cp01.01.02.00.00.00.html/']
    
    base_url = 'http://category.dangdang.com/pg'
    
    page = 1

    def parse(self, response):
        li_list = response.xpath('//ul[@id = "component_59"]/li')
        # 判断一下,第一张图是src,第二张图开始在data-original里
        for li in li_list:
            src = li.xpath('.//img/@data-original').extract_first()
            if src:
                src = src
            else:
                src = li.xpath('.//img/@src').extract_first()
            
            name = li.xpath('.//img/@alt').extract_first()
           
            price = li.xpath('.//p[@class = "price"]/span[1]/text()').extract_first()
           
            book = ScrapyDangdangItem(src = src,name = name,price = price)

            # 获取一个book对象,就传入pipeline进行下载
            yield book

            # 多页爬取时,单页的逻辑是相通的,只需要把页的页码请求再次调用parse()函数即可
        if self.page < 5:
            self.page = self.page + 1

            url = self.base_url + str(self.page) + '-cp01.01.02.00.00.00.html'

            yield  scrapy.Request(url = url,callback = self.parse)

2️⃣ settings.py文件:

# Scrapy settings for scrapy_dangdang project
#
# For simplicity, this file contains only settings considered important or
# commonly used. You can find more settings consulting the documentation:
#
#     https://docs.scrapy.org/en/latest/topics/settings.html
#     https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
#     https://docs.scrapy.org/en/latest/topics/spider-middleware.html

BOT_NAME = 'scrapy_dangdang'

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


# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = 'scrapy_dangdang (+http://www.yourdomain.com)'

# Obey robots.txt rules
ROBOTSTXT_OBEY = True

# Configure maximum concurrent requests performed by Scrapy (default: 16)
#CONCURRENT_REQUESTS = 32

# Configure a delay for requests for the same website (default: 0)
# See https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
#DOWNLOAD_DELAY = 3
# The download delay setting will honor only one of:
#CONCURRENT_REQUESTS_PER_DOMAIN = 16
#CONCURRENT_REQUESTS_PER_IP = 16

# Disable cookies (enabled by default)
#COOKIES_ENABLED = False

# Disable Telnet Console (enabled by default)
#TELNETCONSOLE_ENABLED = False

# Override the default request headers:
#DEFAULT_REQUEST_HEADERS = {
#   'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
#   'Accept-Language': 'en',
#}

# Enable or disable spider middlewares
# See https://docs.scrapy.org/en/latest/topics/spider-middleware.html
#SPIDER_MIDDLEWARES = {
#    'scrapy_dangdang.middlewares.ScrapyDangdangSpiderMiddleware': 543,
#}

# Enable or disable downloader middlewares
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
#DOWNLOADER_MIDDLEWARES = {
#    'scrapy_dangdang.middlewares.ScrapyDangdangDownloaderMiddleware': 543,
#}

# Enable or disable extensions
# See https://docs.scrapy.org/en/latest/topics/extensions.html
#EXTENSIONS = {
#    'scrapy.extensions.telnet.TelnetConsole': None,
#}

# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html

ITEM_PIPELINES = {
   'scrapy_dangdang.pipelines.ScrapyDangdangPipeline': 300,
}

ITEM_PIPELINES = {
   'scrapy_dangdang.pipelines.DangDangDownloadImgPipeline': 300,
}
# Enable and configure the AutoThrottle extension (disabled by default)
# See https://docs.scrapy.org/en/latest/topics/autothrottle.html
#AUTOTHROTTLE_ENABLED = True
# The initial download delay
#AUTOTHROTTLE_START_DELAY = 5
# The maximum download delay to be set in case of high latencies
#AUTOTHROTTLE_MAX_DELAY = 60
# The average number of requests Scrapy should be sending in parallel to
# each remote server
#AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
# Enable showing throttling stats for every response received:
#AUTOTHROTTLE_DEBUG = False

# Enable and configure HTTP caching (disabled by default)
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
#HTTPCACHE_ENABLED = True
#HTTPCACHE_EXPIRATION_SECS = 0
#HTTPCACHE_DIR = 'httpcache'
#HTTPCACHE_IGNORE_HTTP_CODES = []
#HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'

3️⃣ 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

class ScrapyDangdangPipeline:
    def open_spider(self, sipder):
        self.fp = open('book.json','w',encoding = 'utf-8')

    def process_item(self, item, spider):
        self.fp.write(str(item))
        return item

    def close_spider(self,spider):
        self.fp.close()
        
import urllib.request
# 图片下载管道:
class DangDangDownloadImgPipeline:
    def process_item(self, item, spider):
        url = 'http:' + item.get('src') # 这里是因为item本身是获取的json数据,存在字典里,所以我们用字典的get函数获取
        filename = './books/' + item.get('name') + '.jpg'
        urllib.request.urlretrieve(url = url,filename = filename)
        return item

到此,本次实战完结

标签:文件,py,src,Python,self,30,爬虫,item,scrapy
来源: https://blog.csdn.net/qq_52736131/article/details/123087598

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有