Py爬虫之Scrapy框架

Py爬虫之Scrapy框架

Scrapy是适用于Python的一个快速、高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据。Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试.

Scrapy基本使用

理论  

* 引擎

用来处理整个系统的数据流,触发事务(框架核心)

(爬虫、管道、下载器、调度器中的通讯,信号、数据传递等)

* 调度器

用来接收引擎发过来的请求,将其放到队列中,并在引擎再次请求的时候返回,可以想象成一个URL (抓取网页的网址或者链接)的优先队列,由它来决定下一个抓取的网址是什么,同时去除重复的网址

* 下载器

负责下载引擎发送的所有请求,并将获取到的响应内容交还给引擎,由引擎交给爬虫处理

(Scrapy下载器是建立在这个高效的异步模型上的)

* 爬虫 (蜘蛛)

负责处理所有的响应内容,用于从特定网页中提取自己需要的信息,即所谓的实体(Item),用户可以将需要跟进的URL提交给引擎,再次进入调度器等待爬取

* 管道

负责处理爬虫获取到的实体(Item),并进行后期的处理(分析、过滤、存储等)

(当页面被爬虫解析后,将被发送到管道中并经过几个特定的次序处理数据)

* 下载中间件

可以当作是一个可自定义拓展下载功能的组件

* 爬虫中间件

可以当作是一个可自定义拓展和操作引擎与蜘蛛中间通信的功能组件

(如:进入爬虫的响应内容和从蜘蛛出去的请求)

* 运作流程

爬虫将要爬取的URL提交给引擎,引擎将这个URL的请求提交给调度器,调度器将请求整理排序后放到队列,调度器再将处理好的请求提交给引擎,引擎将请求提交给下载器,下载器按照下载中间件的设置下载这个请求,下载器再将下载好的数据提交给引擎(如果下载失败则将消息提交给引擎,引擎将消息提交给调度器,调度器再将请求放到队列中等待再次下载),引擎再将下载好的数据提交给爬虫,爬虫将数据处理完毕后再将需要跟进的URL与实体数据(Item)提交给引擎,引擎再将实体数据(Item)提交给管道,管道将需要跟进的URL提交给调度器,管道再将实体数据(Item)进行处理(分析、过滤、存储等)。

只有调度器中的请求为空时整个程序才会结束

环境安装(Windows)   

直接安装scrapy(推荐)

直接安装后就不用再执行下面的步骤了

pip install scrapy

安装wheel

pip install wheel

安装twisted

下载地址: ~gohlke/pythonlibs/#twisted

本机环境为: python3.7.6  win64

# pip install 下载的文件名pip install Twisted-20.3.0-cp37-cp37m-win_amd64.whl

安装scrapy下载地址与上面一样

# pip install 下载的文件名pip install Scrapy-2.5.0-py3-none-any.whl

基本使用  

创建项目

# 创建项目# scrapy startproject 项目名scrapy startproject hello_test# 创建爬虫源文件cd hello_test# 进入到创建文件夹# scrapy genspider 爬虫名 爬取的域名(后续还可以更改)scrapy genspider spider_test www.wk20.cn# 执行项目scrapy crawl 爬虫名

目录结构

hello_test/  scrapy.cfg  # 项目配置文件  hello_test/   # 项目主要代码文件夹    items.py      # 项目目标文件    pipelines.py  # 项目管道文件    settings.py   # 项目设置文件    middlewares.py  # 中间件文件    spiders/    # 存储爬虫的文件夹      spider_test.py   # 刚才创建的爬虫文件

基本使用

基本代码介绍   

setting.py 设置文件

常用参数

# UA伪装USER_AGENT = 指定UA内容# 是否遵循爬虫协议 robots.txtROBOTSTXT_OBEY = True# 只输出普通错误日志(需要自己写)LOG_LEVEL = ERROR# 不然每次都会输出一大堆日志# 指定导出文件的编码(需要自己写)FEED_EXPORT_ENCODING = gb18030  # 当导出文件乱码时使用该编码

spider_test.py 爬虫文件

import scrapyclass SpiderTestSpider(scrapy.Spider):name = spider_test# 爬虫名(用于执行项目)    # allowed_domains = [www.wk20.cn]   # 允许存在的域名,如URL域名不包含在内则不被使用(一般注释掉)start_urls = []# 列表中存储的URL都会被进行GET请求的发送    """数据解析"""# parse方法调用的次数取决于请求的次数    def parse(self, response):      # response为服务器返回的响应对象        pass

scrapy数据解析

scrapy中的xpath与etree中的xpath用法不同

scrapy中的xpath是直接将定位到的标签中存储的值或者属性取出,返回Selecter对象

相关数据存储在Selecter对象中的data属性中,需要调用extract()或extract_first()取出关键数据

extract()    # 取出data中的内容extract_first()# 取出第一个data中的内容# 省略部分代码"""数据解析"""def parse(self, response):# response为服务器返回的响应对象    div_list = response.xpath(//div[@class="post-excerpt no-padding"])     # 定位到div标签for div in div_list:        """取出段子标题"""        # title = article.xpath(./h3/a/text())   # lxml中的xpath用法        # 输出: [<Selector xpath=./h3/a/text() data=你捡,你离地近。>]# title = div.xpath(./h3/a/text())[0].extract()# 取出标题title = div.xpath(./h3/a/text()).extract_first()# 取出标题title = f"《{title}》"# 标题"""取出段子内容"""content = ""# 存储段子内容content_list = div.xpath(./div/span/p/text()).extract()# 取出文本内容(返回列表)for temp_content in content_list:content += temp_content# 拼接内容print(title)print(content)

手动请求发送  

手动请求发送方法

yield scrapy.Request(url,callback)    # Get请求yield scrapy.FormRequest(url,callback,formdata)# Post请求# 参数url       请求的URL地址callback  指定解析函数,用于数据解析formdata  请求参数(字典格式)

* 为什么start_urls列表中的url会被自动执行

因为列表中的url是被stasrt_requests这个父类方法实现的get请求

def start_requests(self):for u in self.start_urls:yield scrapy.Request(url=u, callback=self.parse)

让start_urls中的url默认进行POST请求的发送

# 重写start_requests方法def start_requests(self):for u in self.start_urls:yield scrapy.FormRequest(url=u, callback=self.parse)

手动请求发送示例

spider_test.py

import scrapyfrom ..items import HelloTestItemclass SpiderTestSpider(scrapy.Spider):name = spider_test# 爬虫名(用于执行项目)start_urls = []# 要爬取的URL列表url = "?page=%d" # 定义一个通用的URL模板page_num = 2"""数据解析"""def parse(self, response):# response为响应内容div_list = response.xpath(//div[@class="post-excerpt no-padding"]) # 定位到div标签for div in div_list:"""取出段子标题"""title = div.xpath(./h3/a/text()).extract_first()# 取出标题 (extract_first 将列表中的第一个元素的data值取出来)title = f"《{title}》"# 标题"""取出段子内容"""content = ""# 存储段子内容content_list = div.xpath(./div/span/p/text()).extract()# 取出文本内容(返回列表)for temp_content in content_list:content += temp_content# 拼接内容"""持久化存储"""item = HelloTestItem()# 实例化一个Item类型对象,将解析到的数据存储到该对象item[title] = titleitem[content] = contentyield item# 将Item对象提交给管道"""手动请求发送"""if self.page_num <= 5:# 爬取前5页new_url = format(self.url % self.page_num)# 拼接其他页面URL# print(new_url)self.page_num += 1                yield scrapy.Request(url=new_url,callback=self.parse)   # 对新页面进行请求发送(手动发送GET请求)

重写start_requests()加速

上面的代码是按顺序进行请求发送的,等第一页请求完毕后第二页才开始发送请求

通过重写start_requests()方法一次性将URL提交给调度器

(可以在管道中计算使用时间)

# spider_test.py 部分代码class SpiderTestSpider(scrapy.Spider):name = spider_test# 爬虫名(用于执行项目)start_urls = []# 要爬取的URL列表url = "?page=%d" # 定义一个通用的URL模板"""重写父类"""def start_requests(self):for page in range(1,5):new_url = format(self.url % page)yield scrapy.Request(url=new_url,callback=self.parse) """数据解析"""def parse(self, response):    # 省略部分代码

持久化存储

基于终端指令的持久化存储  

该方法只可以将parse方法返回的值存储到本地指定文本格式中

# 允许的后缀json, jsonlines, jl, csv, xml,marshal, pickle# 导出命令scrapy crawl 脚本名 -o 输出的文件名# 运行时指定编码格式scrapy crawl spider_test -o dz.csv -s FEED_EXPORT_ENCODING=gb18030# 在settings.py中指定导出文件的编码格式FEED_EXPORT_ENCODING = gb18030scrapy crawl spider_test -o dz.csv

示例

# spider_test.pyimport scrapyclass SpiderTestSpider(scrapy.Spider):name = spider_test# 爬虫名(用于执行项目)start_urls = []# 要爬取的URL列表"""数据解析"""def parse(self, response):# response为响应内容div_list = response.xpath(//div[@class="post-excerpt no-padding"]) # 定位到div标签all_data = [] # 存储所有数据for div in div_list:"""取出段子标题"""title = div.xpath(./h3/a/text()).extract_first()# 取出标题 (extract_first 将列表中的第一个元素的data值取出来)title = f"《{title}》"# 标题"""取出段子内容"""content = ""# 存储段子内容content_list = div.xpath(./div/span/p/text()).extract()# 取出文本内容(返回列表)for temp_content in content_list:content += temp_content# 拼接内容data = {title:title,content:content}all_data.append(data) # 将字典存储到列表中return all_data

基于管道的持久化存储(常用)  

items.py

先在items.py中定义相关属性

解析出几个字段的数据就定义几个属性

import scrapyclass HelloTestItem(scrapy.Item):    """定义属性"""title = scrapy.Field()# 标题content = scrapy.Field()# 内容

spider_test.py

在爬虫文件中将解析到的数据存储封装到Item类型的对象中

并将Item类型对象提交给管道

# 这里要注意先导入库# 导入items.py中的Item类# from ..items import 类名from ..items import HelloTestItem# 在实例化Item类型对象时,类名不要写错了# 类名是items.py中Item类的类名item = HelloTestItem()import scrapyfrom ..items import HelloTestItemclass SpiderTestSpider(scrapy.Spider):name = spider_test# 爬虫名(用于执行项目)start_urls = []# 要爬取的URL列表"""数据解析"""def parse(self, response):# response为响应内容div_list = response.xpath(//div[@class="post-excerpt no-padding"]) # 定位到div标签for div in div_list:"""取出段子标题"""title = div.xpath(./h3/a/text()).extract_first()# 取出标题 (extract_first 将列表中的第一个元素的data值取出来)title = f"《{title}》"# 标题"""取出段子内容"""content = ""# 存储段子内容content_list = div.xpath(./div/span/p/text()).extract()# 取出文本内容(返回列表)for temp_content in content_list:content += temp_content# 拼接内容"""持久化存储"""item = HelloTestItem()# 实例化一个Item类型对象,将解析到的数据存储到该对象item[title] = titleitem[content] = contentyield item# 将Item对象提交给管道

pipelines.py

管道接收到爬虫提交过来的Item类型对象,对其进行任意形式的持久化存储

方法

open_spider(self,spider)# 只在程序开头执行一次process_item(self, item, spider) # 用于进行持久化存储close_spider(self,spider)      # 只在程序结束前执行一次class HelloTestPipeline:fp = None # 文件"""在爬虫开始时执行一次,一般用于连接数据库、打开文件等操作"""def open_spider(self,spider):self.fp = open("dz.txt","w",encoding="utf-8") # 打开文件"""用于进行持久化存储"""def process_item(self, item, spider):title = item[title] # 标题content = item[content] # 内容data = f"{title} : {content}\n" # 要写入文本中的数据self.fp.write(data) # 写入文件return item"""在爬虫结束时执行一次,一般用于关闭连接、关闭文件等操作"""def close_spider(self,spider):self.fp.close() # 关闭文件

settings.py

在设置文件中开启管道

# 需要修改或添加的点如下# 开启管道(取消注释即可)# 300表示管道的优先级,数值越小优先级越高ITEM_PIPELINES = {hello_test.pipelines.HelloTestPipeline: 300,}# 设置只输出错误日志(需要手打)LOG_LEVEL = ERROR# 不遵循robots.txt爬虫协议ROBOTSTXT_OBEY = False# 设置User-AgentUSER_AGENT = Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36

执行项目

scrapy crawl spider_test# 会将爬取到的数据保存在指定文件中

将数据存储到MySql  

理论

一个管道类对应一种形式的持久化存储操作。如果数据在不同的载体就需要使用多个管道类

item不会依次提交给多个管道类,只会被提交给优先级最高的管道类

如果有多个管道类,管道类中一定要 return item

实践

在原来的基础上新增将数据存储到MySQL数据库中

实现: 将数据一份存储到mysql,一份以txt的形式存储到本地文件

Mysql数据库创建

# 在sql控制台执行create database py_data;use py_data;create table duanzi (title varchar(100), content varchar(5000));

pipelines.py

在原来的基础上再定义一个管道类用于存储数据到数据库

如果有多个管道类,管道类中一定要 return item

import pymysql"""用于将数据存储到本地文件"""class HelloTestPipeline:fp = None # 文件对象"""在爬虫开始时执行一次,一般用于连接数据库、打开文件等操作"""def open_spider(self,spider):self.fp = open("dz.txt","w",encoding="utf-8") # 打开文件"""用于进行持久化存储"""def process_item(self, item, spider):title = item[title] # 标题content = item[content] # 内容data = f"{title} : {content}\n" # 要写入文本中的数据self.fp.write(data) # 写入文件return item"""在爬虫结束时执行一次,一般用于关闭连接、关闭文件等操作"""def close_spider(self,spider):self.fp.close() # 关闭文件"""用于将数据存储到数据库"""class MysqlPipeline(object):conn = None # 数据库连接对象curser = None # 游标对象"""连接数据库"""def open_spider(self,spider):# 数据库连接信息db_config = {"host":"127.0.0.1",# Mysql主机地址"user":"root", # 用户名"password":"", # 密码"database":"py_data" # 数据库名}self.conn = pymysql.connect(**db_config)# 连接数据库"""写入数据"""def process_item(self,item,spider):self.curser = self.conn.cursor()# 创建游标对象sql = "INSERT INTO duanzi(title,content) VALUES(%s,%s)" % (item[title], item[content])# 定义SQL语句,用于插入数据try:self.curser.execute(sql)# 执行数据库语句self.conn.commit()# 提交数据except Exception as e:print(f"Error: {e}")# 执行错误输出异常self.conn.rollback()# 重新连接"""关闭连接"""def close_spider(self,spider):print("写入数据库成功")self.conn.close()        self.curser.close()

settings.py

在设置文件中添加Mysql的管道类

ITEM_PIPELINES = {hello_test.pipelines.HelloTestPipeline: 300,# 存储到本地的管道类hello_test.pipelines.MysqlPipeline: 301,# 存储到Mysql数据库的管道类}

执行项目

scrapy crawl spider_test# 查询数据库select * from duanzi;

中间件

下载器中间件  

理论

中间件的作用: 批量拦截请求和响应

拦截请求: 篡改请求URL、伪装请求头(UA、Cookie)、设置请求代理

拦截响应: 篡改响应数据

设置请求代理必须使用中间件才能实现

实践

在middlewares.py中一共有两个中间件,这里以下载器中间件为例

class xxxSpiderMiddleware    # 爬虫中间件class xxxDownloaderMiddleware# 下载器中间件

middlewares.py

"""下载器中间件"""class HelloTestDownloaderMiddleware:"""拦截所有请求"""# 参数: request拦截到的请求 spider爬虫实例化的对象def process_request(self, request, spider):"""指定请求头"""# 中间件的UA伪装为局部的,而settings.py中UA伪装为全局的request.headers[User-Agent] = "xxx" # UA伪装request.headers[Cookie] = "xxx" # 指定Cookieprint("I am process_request")return None"""拦截所有响应对象"""# 参数: request响应对象对应的请求对象,response拦截到的响应对象def process_response(self, request, response, spider):print("I am process_response")return response"""拦截异常请求"""# request拦截到的发生异常的请求# 作用: 想要将异常的请求进行修正,将其变成正常的请求,然后进行重新发送def process_exception(self, request, exception, spider):# 当IP被服务器禁掉时该请求就会标称一个异常请求# 可以在此设置代理后再次爬取# request.meta[proxy] = ":PORT" # 设置代理print("I am process_exception")        return request      # 将异常请求修正后进行重新发送

settings.py

543表示中间件的优先级,数值越小优先级越高

# 爬虫中间件# SPIDER_MIDDLEWARES = {#hello_test.middlewares.HelloTestSpiderMiddleware: 543,# }# 下载器中间件DOWNLOADER_MIDDLEWARES = { hello_test.middlewares.HelloTestDownloaderMiddleware: 543,}开发中间件  

* 开发代理中间件

在scrapy中如果想要使用代理只能在中间件中实现

只要爬虫每次经过这个中间件时都会换上新的代理IP

# middlewares.pyimport random"""代理中间件"""class ProxyMiddleware(object):def process_request(self,request,spider):# 新建一个代理池proxy_pool = [:6666, # 示例:5555, # 示例:8888, # 示例]proxy = random.choice(proxy_pool) # 从代理池中随机选择一个代理# request.meta[proxy] = proxy # 使用代理print(f"当前代理为: {proxy}")# settings.pyDOWNLOADER_MIDDLEWARES = { hello_test.middlewares.ProxyMiddleware: 543,}

* 开发UA中间件

与代理池的原理一样,但UA不会失效

# middlewares.pyimport random"""UA中间件"""class UAMiddleware(object):def process_request(self,request,spider):# 新建一个UA池us_pool = ["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/ Firefox/90.0","Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727)","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36","Android50-AndroidPhone-8000-76-0-Statistics-wifi","Dalvik/1.6.0 (Linux; U; Android 4.4.4; MI 3 MIUI/V7.2.1.0.KXCCNDA)","Dalvik/1.6.0 (Linux; U; Android 4.4.2; Lenovo A3800-d Build/LenovoA3800-d)"]ua = random.choice(us_pool) # 从UA池中随机选择一个UA        # request.headers[User-Agent] = ua     # 使用UA        print(f"当前UA为: {ua}")# setting.pyDOWNLOADER_MIDDLEWARES = {   hello_test.middlewares.ProxyMiddleware: 543,  # 代理中间件   hello_test.middlewares.UAMiddleware: 544,     # UA中间件}

* 开发Cookie中间件

当某个用户在网站中高频的访问网页或下载数据时,网站可能会对该用户进行限制或封禁

可以在该网站中注册多个用户,再使用Selenium模拟登录并将Cookie信息上传到数据库中

中间件再从数据库中获取Cookie实现模拟多用户登录

利用多个用户爬取网站时大大减小了被服务器发现的可能

(注意Cookie是会过期的,所以要不定期获取新Cookie)

# middlewares.pyimport random"""Cookie中间件"""class CookieMiddleware(object):def process_request(self,request,spider):# 假装从数据库查询到Cookie后保存到列表中cookie_pool = ["I am Cookie0","I am Cookie1","I am Cookie2","I am Cookie3",]cookie = random.choice(cookie_pool) # 从Cookie池中随机选择一个cookie# request.headers[Cookie] = cookie# 使用Cookieprint(f"当前Cookie为: {cookie}")# setting.pyDOWNLOADER_MIDDLEWARES = { hello_test.middlewares.ProxyMiddleware: 543, hello_test.middlewares.UAMiddleware: 544, hello_test.middlewares.CookieMiddleware: 545,}

请求传参

请求传参  

使用场景: 爬取的数据不在同一页面中(首页数据+详情页数据)

在scrapy中如果没有请求传参则无法进行持久化存储数据

实现方法

srcapy.Request(url,callback,meta)    # meta是一个字典,可以将meta传递给callback    yield scrapy.Request(url=mv_url,callback=self.parse_callback,meta={item:item})

实践

目的: 将电影名与导演名保存到本地文件中

步骤: 将网站中的电影标题与详情页地址取出,电影导演名存放在详情页中

需要对详情页发起请求,把item对象传递给解析详情页的回调函数中

详情页回调函数解析完数据后将item对象提交到管道中

管道将提交过来的数据存储到本地

spider_test.py

import scrapyfrom ..items import HelloTestItemclass SpiderTestSpider(scrapy.Spider):name = spider_test# 爬虫名(用于执行项目)start_urls = []# 要爬取的URL列表url = "%d.html" # 通用url模板page_num = 2"""数据解析"""def parse(self, response):# response为响应内容li_list = response.xpath(//ul[@class="stui-vodlist clearfix"]/li)for li in li_list:title = li.xpath(./div/a/@title).extract_first()# 电影标题mv_url = "" + li.xpath(./div/a/@href).extract_first() # 电影详情页# 实例化一个Item类型对象,将解析到的数据存储到该对象item = HelloTestItem()item[title] = title"""对详情页发起请求"""# meta可以将meta字典传递给callbackyield scrapy.Request(url=mv_url,callback=self.parse_callback,meta={item:item})"""回调函数,用于解析详情页"""def parse_callback(self,response):item = response.meta[item]# 接收传递过来的meta数据director = response.xpath(//div[@class="stui-content__detail"]/p[3]/a/text()).extract_first() # 定位到导演item[director] = directoryield item# 将item对象提交到管道

items.py

import scrapyclass HelloTestItem(scrapy.Item):title = scrapy.Field()# 标题director = scrapy.Field() # 导演

pipelines.py

import time"""用于将数据存储到本地文件"""class HelloTestPipeline:fp = None # 文件对象start_time = time.time()"""在爬虫开始时执行一次,一般用于连接数据库、打开文件等操作"""def open_spider(self,spider):self.fp = open("movies.txt","w",encoding="utf-8") # 打开文件"""用于进行持久化存储"""def process_item(self, item, spider):title = f"《{item[title]}》" # 标题content = item[director]# 导演data = f"{title} 导演: {content}\n" # 要写入文本中的数据self.fp.write(data) # 写入文件return item"""在爬虫结束时执行一次,一般用于关闭连接、关闭文件等操作"""def close_spider(self,spider):print("总耗时: %s" % (time.time() - self.start_time))self.fp.close() # 关闭文件

settings.py

USER_AGENT = Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36ROBOTSTXT_OBEY = FalseLOG_LEVEL = ERRORITEM_PIPELINES = {    hello_test.pipelines.HelloTestPipeline: 301,  # 存储到本地的管道类}

大文件下载

大文件下载  

大文件中的数据是在管道中请求到的

只要把文件地址与文件名传递到管道中即可

# pipelines.pyfrom scrapy.pipelines.images import ImagesPipeline# 数据下载功能(固定)# 重写该管道的三个方法get_media_requests()  # 对文件地址发起请求file_path()  # return返回文件名即可item_completed() # return返回item,将其返回给下一个即将被执行的管道类

spider_test.py

import scrapyfrom ..items import HelloTestItemclass SpiderTestSpider(scrapy.Spider):name = spider_test# 爬虫名(用于执行项目)start_urls = []# 要爬取的URL列表url = "index_%d.html" # 通用url模板"""重写start_requests方法加速"""def start_requests(self):for page in range(2,5):new_url = format(self.url % page)yield scrapy.Request(url=new_url,callback=self.parse)"""解析主页数据"""def parse(self, response):# response为响应内容info_urls = response.xpath(//ul[@class="clearfix"]/li/a/@href)for info_url in info_urls:info_url = "" + info_url.extract() # 详情页地址"""对详情页发起请求"""yield scrapy.Request(url=info_url,callback=self.parse_callback)"""回调函数,用于解析详情页"""def parse_callback(self,response):img_src = "" + response.xpath(//div[@class="photo-pic"]/a/img/@src).extract_first()# 图片地址filename = img_src.split(/)[-1] # 文件名# 实例化一个Item类型对象,将解析到的数据存储到该对象item = HelloTestItem()item[filename] = filename# 文件名item[img_src] = img_src # 文件下载地址yield item

items.py

import scrapyclass HelloTestItem(scrapy.Item):filename = scrapy.Field()# 文件名img_src = scrapy.Field() # 文件地址

pipelines.py

import scrapyfrom scrapy.pipelines.images import ImagesPipeline# 数据下载功能(固定)"""用于将数据存储到本地文件"""class BigfilePipeline(ImagesPipeline):"""对图片发起请求"""def get_media_requests(self, item, info):yield scrapy.Request(url=item[img_src],meta={item:item})"""指定文件存储路径"""def file_path(self, request, response=None, info=None, *, item=None):item = request.meta[item]# print(f"正在下载: {item[filename]}")return item[filename]"""返回Item"""def item_completed(self, results, item, info):return item

settings.py

USER_AGENT = Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36ROBOTSTXT_OBEY = FalseLOG_LEVEL = ERRORITEM_PIPELINES = {hello_test.pipelines.BigfilePipeline: 301,# 存储到本地的管道类}IMAGES_STORE = ./download    # 指定下载的文件夹,没有则自动创建

--END--