Scrapy入门篇

免责声明

        本文的爬虫知识仅用于合法和合理的数据收集,使用者需遵守相关法律法规及目标网站的爬取规则,尊重数据隐私,合理设置访问频率,不得用于非法目的或侵犯他人权益。因使用网络爬虫产生的任何法律纠纷或损失,由使用者自行承担风险和责任。

1、初识Srapy

概述:Scrapy 是一个用于抓取网站和提取结构化数据的应用程序框架,可用于各种有用的应用程序,如数据挖掘、信息处理或历史存档。尽管 Scrapy 最初是为网络抓取而设计的,但它也可用于使用 API提取数据或用作通用网络爬虫。

优势

  • 可以容易构建大规模的爬虫项目
  • 内置re、xpath、css选择器
  • 可以自动调整爬行速度
  • 开源和免费的网络爬虫框架
  • 可以快速导出数据文件: JSON,CSV和XML
  • 可以自动方式从网页中提取数据(自己编写规则)
  • Scrapy很容易扩展,快速和功能强大
  • 这是一个跨平台应用程序框架(在Windows,Linux,Mac OS)
  • Scrapy请求调度和异步处理

Scrapy架构

处理流程:①、Spider将要爬取的url发送给Engine;

②、Engine将接收到的url发功给Scheduler;

③、Scheduler将接收到的url做去重处理并将其封装成Request对象,Scheduler会将封装好的Request对象返回给Engine;

④、Engine再将接收到的Request对象发送给Downloader;

⑤、Downloader会根据Request对象访问对应的网络服务器,并将响应的内容返回给Engine;⑥、Engine再将收到的响应内容发送给Spider;

⑦、Spider再对响应内容进行提取工作,提取到的内容分为两类,分别是url和数据。如果提取到的是url,就会重新从第①步开始;如果提取到的是数据,Spider就会将数据发送给Engine,然后Engine再将数据发送给Item Pipeline,Item Pipeline再将数据保存到对应的数据库中。

最简单的单个网页爬取流程: spiders > scheduler > downloader > spiders > item pipeline

引擎(engine):引擎用于处理整个系统的数据流处理,触发事务(框架核心)。

调度器(Scheduler):接受引擎发过来的请求,并将请求压入队列中, 在引擎再次请求的时候返回,可以将其想象成一个URL(抓取网页的网址或者说是链接)的优先队列,由它来决定下一个要抓取的网址是什么, 同时去除重复的网址。

下载器(Downloader):下载器用于下载网页内容,并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)。

爬虫(Spiders):爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息,即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面。

项目管道(Pipeline):项目管道负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。

下载器中间件(Downloader Middlewares):下载器中间件位于Scrapy引擎和下载器之间的框架,主要是处理Scrapy引擎与下载器之间的请求及响应。

爬虫中间件(Spider Middlewares):爬虫中间件介于Scrapy引擎和爬虫之间的框架,主要工作是处理蜘蛛的响应输入和请求输出。

调度中间件(Scheduler Middewares):调度中间件介于Scrapy引擎和调度之间的中间件,从Scrapy引擎发送到调度的请求和响应。

安装:在Pycharm的终端(Alt + F12)输入如下代码并运行

pip install scrapy


2、创建第一个爬虫

①、安装Scrapy(在Pycharm的终端(Alt + F12)输入如下代码并运行)

pip install scrapy

②、创建Scrapy项目(在Pycharm的终端(Alt + F12)输入如下代码并运行)

# myscrapy01是项目名,可以自定义
scrapy startproject myscrapy01

运行结果如下:

自动创建了一个名为myscrapy01的目录:

文件说明

名称作用
scrapy.cfg项目的配置信息,主要为Scrapy命令行工具提供一个基础的配置信息。(真正爬虫相关的配置信息在settings.py文件中)
items.py设置数据存储模板,用于结构化数据,如:Django的Model
pipelines数据处理行为,如:一般结构化的数据持久化
settings.py配置文件,如:递归的层数、并发数,延迟下载等
spiders爬虫目录,如:创建文件,编写爬虫规则

③、创建第一个爬虫(在Pycharm的终端(Alt + F12)输入如下代码并运行)

cd .\myscrapy01\
# Baidu是爬虫名,baidu.com是爬取页面的地址,一般写域名即可(不用写全路径)
scrapy genspider Baidu baidu.com

运行结果如下:

根据结果提示的路径打开对应路径并找到文件:


3、Scrapy项目的启动

概述:Scrapy项目有两种启动方式。第一种是通过命令运行;第二种是通过运行Python脚本启动。


3.1、命令启动爬虫

概述:scrapy框架提供了对项目的命令scrapy 。

①、方法一

# 这里的爬虫名是爬虫文件中name属性的值
scrapy crawl 爬虫名

注意:要在命令行中切换到scrapy目录后再运行上方的命令才能实现启动。

②、方法二

# spider_file.py指我们要运行的爬虫文件名
scrapy runspider spider_file.py

注意:使用此方法启动scrapy项目前需要提前切换到spiders目录(如果不切换到spiders目录可能会找不到文件)。


3.2、脚本启动爬虫

概述:如果我们已经创建了一个名为scrapy01的项目在指定目录中,创建脚本时,创建脚本的路径为scrapy01\scrapy01\启动脚本.py。

3.2.1、脚本的编写

3.2.1.1、cmdline
# 在scrapy项目目录下与scrapy项目名同名的目录中创建脚本并输入如下内容
# 爬虫名字即爬虫文件中name属性对应的值
from scrapy.cmdline import execute
execute(['scrapy', 'crawl', '爬虫名字'])

3.2.1.2、CrawlerProcess
# 在scrapy项目目录下与scrapy项目名同名的目录中创建脚本并输入如下内容
from scrapy.crawler import CrawlerProcess
# 这里是引入spiders目录下要运行的爬虫文件,这里的BaiduSpider是baidu.py文件中的类名
from spiders.baidu import BaiduSpider# 创建启动对象
process = CrawlerProcess()
# 增加爬虫任务
process.Crawl(BaiduSpider)
# 启动爬虫
process.Start()

3.2.1.3、CrawlerRunner
# 在scrapy项目目录下与scrapy项目名同名的目录中创建脚本并输入如下内容# 这里表示引入多个要执行的爬虫文件
from spiders.baidu import BaiduSpider
from spiders.taobao import TaoBaoSpider# 引入CrawlerRunner
from scrapy.crawler import CrawlerRunner
# 引入configure_logging
from scrapy.utils.log import configure_logging
# 引入reactor
from twisted. Internet import reactor# 开启日志输出
configure_logging()
# 创建启动对象
runner = CrawlerRunner()
# 增加爬虫任务
runner. Crawl(BaiduSpider)
runner. Crawl(TaoBaoSpider)
# 启动爬虫
d = runner. Join()
d.addBoth(lambda _: reactor.stop())
reactor. Run()

3.2.2、脚本的运行

概述:脚本编写完成后有以下两种方式运行:

①、命令行输入如下语句

python 脚本.py

②、右键脚本编辑区空白处 => run '要运行的脚本文件.py' (运行'脚本文件.py'):

实操1:通过cmdline的方式运行一个scrapy爬虫文件(其他启动方式只在于脚本文件的内容存在区别)。

①、通过ALT+F12打开Pycharm终端,然后进入自己的Scrapy根目录(这个根目录是自己定的,本文所有项目都将写在这个根目录中)

②、创建scrapy项目:

scrapy startproject scrapy01

运行结果如下:

③、进入创建的项目并创建爬虫文件:

scrapy genspider Baidu www.baidu.com

运行结果如下:

④、关闭遵守网站的robots.txt文件(因为baidu的robots.txt规定不允许爬虫访问,可参考baidu.com/robots.txt),打开scrapy01项目中的settings.py文件:

将文件第20行的内容注释掉即可(ROBOTSTXT_OBEY = True)

⑤、在与项目名同名的子目录中创建一个启动脚本文件(理论上在项目下的任何一个目录创建启动脚本都可以)

注意:避免将脚本文件取名为start,可能会引起报错。

⑥、修改创建的爬虫文件baidu.py(主要是为了让执行的效果更明显,将parse函数里的内容替换成显示效果明显的内容即可)

⑦、编写脚本文件:

from scrapy.cmdline import execute
execute(["scrapy","crawl","Baidu"])

运行结果如下,大功告成:


4、数据提取

概述:Scrapy有自己的数据提取机制。它们被称为选择器。我们可以通过使用选择器re、xpath、css提取数据。

注意

①、Scrapy不用安装与引入Xpath和BS4。

②、通过xpath和bs4提取数据后,还要通过get()/getall()方法获取具体的数据,但通过re提取数据后就无需进一步提取数据。

Reseponse对象获取选择器

# 正常使用
response.selector.xpath('//span/text()').get()
response.selector.css('span::text').get()
response.selector.re('<span>')
# 快捷使用
response.xpath('//span/text').get()
response.css('span::text').get()

自定义对象获取选择器

# 导包
from scrapy.selector import Selector
# 通过text参数初始化
body = '<html><body><span>good</span></body></html>'
# 这里除了可以使用xpath方法外,还能用bs4或re
Selector(text=body).xpath('//span/text()').get()
# 通过response参数进行初始化
from scrapy.selector import Selector
from scrapy.http import HtmlResponse
response = HtmlResponse(url='http://example.com', body=body)
# 这里除了能使用xpath进行提取外还能使用bs4和re
Selector(response=response).xpath('//span/text()').get()

选择器的方法

S.N.方法 & 描述
extract()、getall()它返回一个unicode字符串以及所选数据
extract_first()、get()它返回第一个unicode字符串以及所选数据
re()它返回Unicode字符串列表,当正则表达式被赋予作为参数时提取
xpath()它返回选择器列表,它代表由指定XPath表达式参数选择的节点
css()它返回选择器列表,它代表由指定CSS表达式作为参数所选择的节点

实操2:新建爬虫项目并获取baidu的标题标签中的文本信息。

①、Pycharm控制台(Alt+F12)进入我们的scrapy根目录:

②、新建爬虫项目,运行结果如下:

③、进入创建的爬虫项目新建爬虫文件,运行结果如下:

④、关闭遵守网站的robots.txt文件(不关闭就拿不到数据),打开scrapy02/scrapy02/settings.py文件:

注释20行左右的内容(ROBOTSTXT_OBEY=TRUE):

⑤、编辑爬虫文件,打开scrapy02/scrapy02/spiders/baidu.py文件:

from scrapy.selector import Selector
import scrapyclass BaiduSpider(scrapy.Spider):name = "baidu"allowed_domains = ["www.baidu.com"]start_urls = ["https://www.baidu.com"]def parse(self, response):print("----------------------------------------通过response对象获取选择器----------------------------------------")# 通过xpath获取一个包含匹配元素的选择器列表对象title_xpath = response.xpath("//title/text()")# 获取的所有标签print(f"response.xpath获取的匹配标签为:{title_xpath}")# 提取数据print(f"response.xpath匹配的标签文本为:{title_xpath.getall()}")# 通过bs4获取一个包含匹配元素的选择器列表对象title_bs4 = response.css("title::text")# 获取的所有标签print(f"response.css获取的匹配标签为:{title_bs4}")# 提取数据print(f"response.css匹配的标签文本为:{title_bs4.getall()}")# response对象通过re获取数据时较特殊,无需提取数据即可得到数据本身# 通过re获取标签文本title_re = response.selector.re("<title>(.*)</title>")# 打印数据print(f"response.re获取的匹配标签中的文本为:{title_re}")print("----------------------------------------通过自定义对象获取选择器----------------------------------------")title = Selector(response=response).xpath("//title/text()")# 获取的所有标签print(f"自定义对象.xpath获取的匹配标签为:{title}")# 提取数据print(f"自定义对象.xpath匹配的标签文本为:{title.getall()}")

⑥、运行爬虫文件:

运行结果如下:


5、ScrapyShell

概述:Scrapy Shell是一个交互式shell,可以在不运行spider项目时,快速调试 scrapy 代码。

提示:ScrapyShell一般用于测试xpath或css表达式,查看它们是否能提取想要的数据。

启动语法

scrapy shell "目标网页的地址"

注意:当从命令行运行Scrapy Shell时,总是用引号括住url,否则url包含参数(即 & 字符)将不起作用。

实操3:通过ScrapyShell查找baidu的导航栏相关信息。

①、开启ScrapyShell:

scrapy shell "https://www.baidu.com"

运行结果如下:

②、通过xpath查找导航栏文本信息:

# 获取baidu导航栏的文本信息
response.xpath('//div[@id="u1"]/a/text()').getall()# 获取baidu导航栏的链接
response.xpath('//div[@id="u1"]/a/@href').getall()

运行结果如下:

补充:如果需要传递参数或自定义请求头时ScrapyShell就不能按实操演示的这样操作,目前可以通过调试模式去实现(需要提前通过终端进入指定爬虫文件的目录,否则会报错)。


6、Scrapy将数据保存到文件

6.1、Python保存数据

概述:在Python进阶学习记录一文中就提到了利用with结合open()方法实现数据保存到文件,在Scrapy中同样适用。

with open("movie.txt", 'wb') as f:for n, c in zip(movie_name, movie_core):str = n+":"+c+"\n"f.write(str.encode())

6.2、Scrapy内置方法

概述:scrapy 内置主要有四种:JSON,JSON lines,CSV,XML,最常用的导出结果为JSON,命令如下:

scrapy crawl dmoz -o douban.json -t json 

参数说明

dmoz :是一个爬虫名(可以自定义),要与爬虫文件的name属性的值对应上。

-o : 后面跟导出文件名;

-t : 后面跟导出的文件类型,如果 -o 已经指明了导出文件的类型 -t 就可以省略。

注意:需要提前解析数据,将解析的数据 (解析后的数据保存为dict类型或item类型) 返回后才能保存到文件。

# 以下是返回数据的两种方法
yield data
return data

实操4:通过scrapy获取某瓣TOP250的电影名和对应的链接,将获取的数据保存到"top250.csv"文件中。

①、创建项目并在项目中创建爬虫文件:

# 进入scrapy根目录
cd D:\scrapyProject\# 创建名为scrapy03的项目
scrapy startproject scrapy03# 进入新建的scrapy项目并创建爬虫文件
cd ./scrapy03
scrapy genspider Douban douban.com

运行结果如下:

②、不遵守robot.text文件的规则(一般遵守就拿不到数据,在scrapy03/scrapy03/settings.py文件中,屏蔽20行的内容即可)

③、为爬虫文件添加请求头(在scrapy03/scrapy03/settings.py文件中,将17行的User-Agent属性前的井号去掉并补全User-Agent的值,值从浏览器获取):

④、编辑爬虫文件内容 并运行(编辑scrapy03/scrapy03/spiders/douban.py文件,并通过在终端输入命令的方式运行爬虫文件)

# 用原生Python保存数据到文件(不推荐)
# 通过 scrapy crawl douban 在控制台运行
import scrapy
import csvclass DoubanSpider(scrapy.Spider):name = "douban"allowed_domains = ["douban.com"]start_urls = ["https://movie.douban.com/top250"]def parse(self, response):names = response.xpath('//div[@class="info"]/div[@class="hd"]/a/span[1]/text()').getall()hrefs = response.xpath('//div[@class="info"]/div[@class="hd"]/a/@href').getall()with open("./top250.csv","w",encoding="utf-8") as f :# 创建csv文件写入器writer = csv.writer(f)# 写入表头writer.writerow(['Name', 'URL'])# 遍历电影名和链接并逐行写入for name, href in zip(names, hrefs):writer.writerow([name, href])
# 用 scrapy 内置的 yield 返回数据
# 用 scrapy crawl Douban -o ./top250.csv -t csv 运行爬虫文件 (-t csv可省略)
import scrapyclass DoubanSpider(scrapy.Spider):name = "douban"allowed_domains = ["douban.com"]start_urls = ["https://movie.douban.com/top250"]def parse(self, response):names = response.xpath('//div[@class="info"]/div[@class="hd"]/a/span[1]/text()').getall()hrefs = response.xpath('//div[@class="info"]/div[@class="hd"]/a/@href').getall()for name,href in zip(names,hrefs):yield {"name":name,"href":href}
# 用 scrapy 内置的 return 返回数据
# 用 scrapy crawl douban -o ./top250.csv 运行爬虫文件
import scrapyclass DoubanSpider(scrapy.Spider):name = "douban"allowed_domains = ["douban.com"]start_urls = ["https://movie.douban.com/top250"]def parse(self, response):names = response.xpath('//div[@class="info"]/div[@class="hd"]/a/span[1]/text()').getall()hrefs = response.xpath('//div[@class="info"]/div[@class="hd"]/a/@href').getall()data = []for name, href in zip(names, hrefs):data.append({"name": name,"href": href})return data

运行后终端如下:

运行后在scrapy03目录下自动创建了一个名为top250.csv的文件,文件内容如下:


7、Item Pipeline

概述:当数据在Spider中被收集之后,可以传递到Item Pipeline中统一进行处理

说明:每个item pipeline就是一个普通的python类,包含的方法名如下:

方法名含义是否必须实现
process_item(self,item,spider)用于处理接收到的item
open_spider(self,spider)表示当spider被开启的时候调用这个方法
close_spider(self,spider)当spider关闭时候这个方法被调用

功能说明:

①、接收item:在process_item方法中保存;

②、是否要保存数据:取决于是否编写代码用于保存数据;

③、决定此Item是否进入下一个pipeline

  • return item 数据进入下一个pipeline
  • drop item 抛弃数据

实操5:通过该实操了解Item Pipeline的用法(本实操在实操4的基础上进行)。

①、我们的爬虫文件douban.py文件内容如下(实操4中提到的任何一个方法都可以):

import scrapyclass DoubanSpider(scrapy.Spider):name = "douban"allowed_domains = ["douban.com"]start_urls = ["https://movie.douban.com/top250"]def parse(self, response):names = response.xpath('//div[@class="info"]/div[@class="hd"]/a/span[1]/text()').getall()hrefs = response.xpath('//div[@class="info"]/div[@class="hd"]/a/@href').getall()data = []for name, href in zip(names, hrefs):data.append({"name": name,"href": href})return data

②、开启settings.py文件中的Item Pipline(把井号去掉即可):

③、编辑/scrapy03/scrapy03/pipeitem.py文件:

from itemadapter import ItemAdapter
import csvclass Scrapy03Pipeline:# 定义open_spider和close_spider能避免程序频繁地打开和关闭文件def open_spider(self,spider):self.file = open("douban.csv","a",encoding="utf-8")def process_item(self, item, spider):# 一定要将数据组织成列表的类型写入csv.writer(self.file).writerow([f"name:{item.get('name')} href:{item.get('href')}"])def close_spider(self,spider):self.file.close()

④、运行爬虫文件(在终端输入如下内容):

# 进入scrapy03项目
cd .\scrapy03\# 运行爬虫文件
scrapy crawl douban

(控制台)运行结果如下:

(douban.csv)文件内容如下:


8、ImagePipeline

8.1、ImagePipeline保存图片

概述:Scrapy提供了一个 ImagePipeline,用来下载图片这条管道,图片管道ImagePipeline还具有额外特性的功能:

①、避免重新下载最近已经下载过的图片;

②、将所有下载的图片转换成通用的格式(JPG)和模式(RGB);

③、缩略图生成;

④、检测图像的宽/高,确保它们满足最小限制。

使用图片管道

# 替换settings.py文件中ITEM_PIPELINES的属性值
scrapy.pipelines.images.ImagesPipeline

图片管道的基本工作流程

①、在一个爬虫中,把图片的URL放入 image_urls组内(image_urls是个列表);

②、URL从爬虫内返回,进入图片管道;

③、当图片对象进入 ImagesPipeline,image_urls 组内的URLs将被Scrapy的调度器和下载器安排下载;

④、settings.py文件中配置保存图片路径参数 IMAGE_STORE;

⑤、开启管道。

实操6:创建一个scrapy项目,并通过该项目保存一个指定图片。

①、回到scrapy根目录创建爬虫项目scrapy04,并在该项目中创建一个名为img.py的爬虫文件:

②、在scrapy根目录下创建一个名为imgs的目录用于保存图片(这个路径可以自行发挥)

③、修改settings.py文件(分为三个操作):

ITEM_PIPELINES = {# 引入imagePipeline,设置权重为300'scrapy.pipelines.images.ImagesPipeline':300,
}
# 设置保存路径
IMAGES_STORE = "D:\scrapyProject\imgs"

# 不遵守目标网站的规则
# ROBOTSTXT_OBEY = True

# 设置USER_AGENT
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0"

④、修改爬虫文件:

import scrapyclass ImgSpider(scrapy.Spider):# name表示爬虫名,运行爬虫文件时需要用到name = "img"# 将要爬取的网页域名添加到列表中,否则无法获取数据allowed_domains = ["keaitupian.cn"]start_urls = ["https://www.keaitupian.cn/fengjing/16657.html"]def parse(self, response):# scrapy通过get()方法获取selector对象中的内容url = response.xpath("//article/p/a/img/@src").get()yield {# image_urls属性名是固定的"image_urls":[url]}

⑤、下载pillow(不下载可能会报错)

⑥、编写运行脚本文件并运行(在scrapy04项目下的任意目录新建一个begin.py文件):

from scrapy.cmdline import execute
execute(["scrapy","crawl","img"])

运行结果如下:


8.2、自定义ImagePipeline

概述:在上一个小节中学习了scrapy内置的ImagePipeline的使用方法,但是依然存在一定痛点,例如:①、保存的图片文件名称是一个加密后的字符串,可读性差;②、存储图片时一定要使用image_urls参数进行保存。为了解决这些痛点,可以通过自定义ImagePipeline解决。

自定义ImagePipeline的步骤

①、继承 scrapy.pipelines.images import ImagesPipeline

②、实现 get_media_requests(self, item, info) 方法:

  • 发送请求,下载图片
  • 转发文件名

③、实现 file_path(self,request,response=None,info=None,*,item=None) 

  • 修改文件名与保存路径

实操7:创建一个新的scrapy项目,并在该项目中创建一个新的爬虫文件,用于获取某瓣的图片信息,其电影名称作为图片的名称(图片通过自定义ImagePipeline实现)。(注意:内容存在超纲,了解一下即可)

①、新建爬虫项目并在该项目中创建爬虫文件:

# 创建一个名为scrapy05的项目
scrapy startproject scrapy05
# 进入创建的项目内并创建一个名为douban的爬虫文件
cd .\scrapy05\
scrapy genspider Douban douban.com

②、修改settings.py文件:

# 1、将遵循网站爬虫规则关闭 (将已有内容前加上井号)
# ROBOTSTXT_OBEY = True# 2、设置请求头
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0"# 开启ITEM_PIPELINES(将被注释的内容前面的井号去掉即可)
ITEM_PIPELINES = {# 由于要使用自定义ImagePipeline,所以这里也要将名字替换成自己的Pipeline的名字"scrapy05.pipelines.MyImagePipeline": 100,
}
# 设置保存路径
IMAGES_STORE = r"D:\scrapyProject\imgs"

③、编辑items.py文件:

import scrapyclass Scrapy06Item(scrapy.Item):# 定义要爬取的字段image_urls = scrapy.Field()names = scrapy.Field()images = scrapy.Field()

④、编辑爬虫文件:

import scrapy
from scrapy05.items import Scrapy05Itemclass DoubanSpider(scrapy.Spider):name = "douban"allowed_domains = ["movie.douban.com", "douban.com"]# 添加所有可能出现的域名for i in range(20):allowed_domains.append(f'img{i}.doubanio.com')start_urls = ["https://movie.douban.com/top250"]def parse(self, response):urls = response.xpath('//div[@class="pic"]/a/img/@src').getall()names = response.xpath('//div[@class="hd"]/a/span[1]/text()').getall()for url, name in zip(urls, names):item = Scrapy05Item()item['names'] = nameitem['image_urls'] = [url]yield item

⑤、编辑pipelines.py文件:

from itemadapter import ItemAdapter
from typing import Any, Optional
# 导入ImagePipeline
from scrapy.pipelines.images import ImagesPipeline
from scrapy.exceptions import DropItem
from scrapy import Requestclass Scrapy06Pipeline:def process_item(self, item, spider):return item
# 添加一个自定义类并继承ImagePipeline
class MyImagePipeline(ImagesPipeline):# 下面的函数都是对ImagesPipeline已有方法的重写# 该函数用于获取图片并返回给Enginedef get_media_requests(self, item, info):for image_url in item['image_urls']:yield Request(image_url, meta={'names': item['names']})# 该函数用于处理图片路径及其名称def file_path(self, request, response: Optional[Any] = None, info: Optional[Any] = None):image_guid = request.meta['names']  # 使用名称作为文件名print(f'{image_guid}.jpg')return f'{image_guid}.jpg'def item_completed(self, results, item, info):# 获取成功下载的图片路径列表image_paths = [x['path'] for ok, x in results if ok]# 检查成功下载的图片路径是否存在if not image_paths:raise DropItem("Item contains no images")# 存储成功下载的图片路径item['images'] = image_paths# 返回修改后的爬取项return item

⑥、编辑启动脚本

from scrapy.cmdline import execute
execute(["scrapy", "crawl", "Douban"])

运行结果如下:


9、settings.py文件详解

概述:Scrapy允许通过settings.py文件自定义设置所有Scrapy组件的行为,包括核心、扩展、管道和spider本身。

参考文档:Scrapy 2.11.2 文档 

参数说明

1、BOT_NAME:自动设置项目名,一般无需手动修改;

2、CONCURRENT_ITEMS:用于设置Item Processor(即 Item Pipeline) 同时处理(每个response的)item的最大值;

3、CONCURRENT_REQUESTS:用于设置Scrapy downloader 并发请求(concurrent requests)的最大值;

4、CONCURRENT_REQUESTS_PER_DOMAIN:用于设置对单个网站进行并发请求的最大值;

5、CONCURRENT_REQUESTS_PER_IP:对单个IP进行并发请求的最大值。如果非0,则忽略 CONCURRENT_REQUESTS_PER_DOMAIN 设定, 使用该设定。 即并发限制将针对IP,而不是网站;

6、FEED_EXPORT_ENCODING:用于设置导出时文件的编码;

7、DEFAULT_REQUEST_HEADERS:用于设置默认的请求头;

8、DOWNLOADER_MIDDLEWARES:保存项目中启用的下载中间件及其顺序的字典;

9、DOWNLOAD_DELAY:用于指定下载器在下载同一个网站下一个页面前需要等待的时间。该选项可以用来限制爬取速度, 减轻服务器压力,同时也支持浮点数;

10、DOWNLOAD_TIMEOUT:用于设置下载器超时时间(单位: 秒);

11、ITEM_PIPELINES:保存项目中启用的pipeline及其顺序的字典。该字典默认为空,值(value)任意,代表优先级(值越低优先级越高)。 不过值(value)习惯设定在0-1000范围内;

12、DEPTH_LIMIT:允许为任何站点爬行的最大深度(能否从网页a的链接进入网页b,再从网页b的链接进入网页c,以此类推,叫做爬取深度)。如果值为零,则不会施加任何限制;

13、LOG_ENABLED:是否启用日志(如果不做修改启动爬虫文件在控制台中就会打印日志信息),默认为True;

14、LOG_LEVEL:用于设置显示的日志等级,可选的级别有: CRITICAL、 ERROR、WARNING、INFO、DEBUG。默认等级是DEBUG,DEBUG是日志等级中最低的级别;

15、LOG_ENCODING:用于设置日志使用的编码;

16、LOG_FILE:用于将日志输出到指定文件中,不再将日志打印到控制台中;

17、LOG_FORMAT:用于格式化日志消息的字符串,默认为'%(asctime)s [%(name)s] %(levelname)s: %(message)s';

18、ROBOTSTXT_OBEY:用于指定是否遵循robots协议,默认为True。

19、USER_AGENT:用于设置爬取的默认User-Agent。

知识点补充

①、scrapy对某些内部组件进行了默认设置,这些组件通常情况下是不能被修改的,但是我们在自定义了某些组件以后,比如我们设置了自定义的middleware中间件,需要按照一定的顺序把他添加到组件之中,这个时候需要参考scrapy的默认设置,因为这个顺序会影响scrapy的执行;

②、如果需要关闭下载处理器,为其赋值为 None 即可;

③、有时添加了一些自定义的组件,无法应用到效果,可以从执行顺序方面入手,值越小,优先级越高。


10、Scrapy小说实战

实操8:获取某小说网中指定小说的章节标题和章节内容。

①、创建工程和爬虫文件:

②、修改设置文件settings.py

# 1、不遵循爬取规则
# ROBOTSTXT_OBEY = True# 2、设置请求头
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0"# 3、开启Pipeline
ITEM_PIPELINES = {"scrapy07.pipelines.Scrapy07Pipeline": 300,
}# 4、设置爬取深度为49,减少对目标网站的请求
DEPTH_LIMIT = 20# 5、每3秒爬取一个页面的内容,降低请求频率,降低目标服务器压力
DOWNLOAD_DELAY = 3# 6、设置等待时间
DOWNLOAD_TIMEOUT = 15# 7、配置请求环境
# 7.1、关闭cookie,统一在DEFAULT_REQUEST_HEADERS中
COOKIES_ENABLED = False
# 7.2、配置具体的参数(所有参数都是通过浏览器工具中的网络标签中获取)
DEFAULT_REQUEST_HEADERS = {"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6","Accept-Encoding":"gzip, deflate, br, zstd","cookie":"cf_clearance=Wfqw83.7lfBHYIpNRuGW01dcc.t73aIqr_64HxBVbBI-1721948450-1.0.1.1-RLLubHBIA5SGSjU9AP0dD7CNoB8.ReBbPlLZafeM2kW5PWca5rKcTkXcWXHorGnF.HmrbvTOReyx7dE.0I2IwA","Sec-Ch-Ua":'"Not/A)Brand";v="8", "Chromium";v="126", "Microsoft Edge";v="126"',"Sec-Ch-Ua-Full-Version-List":'"Not/A)Brand";v="8.0.0.0", "Chromium";v="126.0.6478.183", "Microsoft Edge";v="126.0.2592.113"'
}

③、编辑爬虫文件biqooge.py文件:

import scrapyclass XddzwSpider(scrapy.Spider):name = "xddzw"allowed_domains = ["xddzw.com"]start_urls = ["https://www.xddzw.com/694/694982/3708366.html"]def parse(self, response):# 获取标题title_fic = response.xpath('//div[@class="bookname"]/h1/text()').get()# 获取内容content_fic = response.xpath('//div[@id="content"]/p/text()').getall()# 获取下一章链接next_link = response.xpath('//div[@class="bottem2"]/a[4]/@href').get()# 将获取到的内容推送到Pipeline,让Pipeline保存内容yield {"title": title_fic,"content": content_fic}# 让scrapy自动请求下一个章节(发送完next_link的请求后,让parse函数去解析内容,但parse函数中又会出现新的下一章的链接)# 注意省略parse函数的括号,带括号表示直接执行yield scrapy.Request('https://www.xddzw.com' + next_link, callback=self.parse)

④、编辑pipelines.py文件:

class Scrapy07Pipeline:# 创建保存数据的文件def open_spider(self, spider):self.file = open("D:\scrapyProject\scrapy07\吞噬星空.txt", "w", encoding="utf-8")# 将数据写入文件def process_item(self, item, spider):self.file.write("".join(item["title"]) + "\n" * 2)# 网站返回的内容是列表,需要将列表转换为字符串self.file.write("".join(item["content"]).replace("。","。\n") + '\n' * 3)return item# 关闭文件def close_spider(self, spider):self.file.close()

⑤、创建并编辑启动脚本:

from scrapy.cmdline import execute
execute(["scrapy","crawl","xddzw"])

控制台运行结果如下:

文件内容如下:


11、CrawlSpider

概述:在Scrapy中Spider是所有爬虫的基类,而CrawSpiders就是Spider的派生类。适用于先爬取start_url列表中的网页,再从爬取的网页中获取link并继续爬取的工作。

创建CrawlSpider语法

scrapy genspider -t crawl 爬虫名 (allowed_url)


11.1、CrawlSpider的核心类对象

11.1.1、Rule

概述:Rule类与CrawlSpider类都位于scrapy.contrib.spiders模块中。

class scrapy.contrib.spiders.Rule( link_extractor,         callback=None,cb_kwargs=None,follow=None,process_links=None,process_request=None) 

参数解析

link_extractor:为LinkExtractor,用于定义需要提取的链接;

callback:当link_extractor获取到链接时参数所指定的值作为回调函数(注意:回调函数尽量不要用parse方法,crawlspider已使用了parse方法);

follow:指定了根据该规则从response提取的链接是否需要跟进。当callback为None,默认值为True;

process_links:主要用来过滤由link_extractor获取到的链接;

process_request:主要用来过滤在rule中提取到的request 。

11.1.2、LinkExtractors

概述:LinkExtractors即链接提取器,其作用是从response对象中获取链接,并且该链接接下来会被爬取 ,每个LinkExtractor有唯一的公共方法是 extract_links(),它接收一个 Response 对象,并返回一个 scrapy.link.Link 对象。

class scrapy.linkextractors.LinkExtractor(allow = (),deny = (),allow_domains = (),deny_domains = (),deny_extensions = None,restrict_xpaths = (),tags = ('a','area'),attrs = ('href'),canonicalize = True,unique = True,process_value = None
)

参数解析

allow:满足括号中“正则表达式”的值会被提取,如果为空,则全部匹配。

deny:与这个正则表达式(或正则表达式列表)不匹配的URL一定不提取。

allow_domains:会被提取的链接的domains。

deny_domains:一定不会被提取链接的domains。

restrict_xpaths:使用xpath表达式,和allow共同作用过滤链接(只选到节点,不选到属性)

restrict_css:使用css表达式,和allow共同作用过滤链接(只选到节点,不选到属性)

实操9:利用ScrapyShell练习LinkExtractors(均在终端中操作)。

# 启动scrapy shell
scrapy shell "https://www.xddzw.com/694/694982/3708366.html"# 导包
from scrapy.linkextractors import LinkExtractor# 通过指定规则获取页面中所有a标签的链接
link = LinkExtractor(restrict_xpaths=('//a'))link.extract_links(response)

运行结果如下:


11.2、CrawlSpider实战

实操10:在实操8的基础上练习CrawlSpider,观察CrawlSpider与我们前面写的爬虫文件有什么不同点。

①、终端进入项目中并创建一个新的爬虫文件:

②、修改新建的爬虫文件:

import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Ruleclass MycrawlspiderSpider(CrawlSpider):name = "myCrawlSpider"allowed_domains = ["xddzw.com"]# 这里修改成了第一章的父级目录链接start_urls = ["https://www.xddzw.com/694/694982/index.html"]rules = (# Rule(LinkExtractor(allow=r"Items/"), callback="parse_item", follow=True),# 获取第一章节链接(由于LinkExtractor的特性使得无法获取第一章的内容,所以要从第一章的链接开始获取)Rule(LinkExtractor(restrict_xpaths='//div[@id="list"]/dl/dd[13]'), callback="parse_item", follow=True),# 自动获取下一章链接Rule(LinkExtractor(restrict_xpaths='//div[@class="bottem2"]/a[4]'),callback="parse_item", follow=True))def parse_item(self, response):# 获取标题title_fic = response.xpath('//div[@class="bookname"]/h1/text()').get()# 获取内容content_fic = response.xpath('//div[@id="content"]/p/text()').getall()# 将获取到的内容推送到Pipeline,让Pipeline保存内容yield {"title": title_fic,"content": content_fic}

③、修改启动脚本文件begin.py:

from scrapy.cmdline import execute
execute(["scrapy","crawl","myCrawlSpider"])

运行结果如下:

运行后的文件内容如下:


12、Request对象

12.1、初识Request

概述:爬虫中请求与响应是最常见的操作,Request对象在爬虫程序中生成并传递到下载器中,下载器执行请求并返回一个Response对象。

提示:一个Request对象表示一个HTTP请求,它通常是在爬虫生成,并由下载器执行,从而生成Response。

常用语法

class scrapy.http.Request(url,[callback, method='GET', headers, body, cookies, meta, encoding='utf-8', priority=0, dont_filter=False, errback])

参数解析

url(string):用于指定目标网站的地址,是Request的必填参数;

callback(callable):用于指定回调函数,如果请求没有指定回调,parse()作为默认的回调函数。如果在处理期间引发异常,则会调用errback;

method(string):标记此请求的HTTP方法。默认是 'GET' 。可设置为"GET", "POST", "PUT"等,且需保证字符串大写;

meta(dict):用于在不同的请求之间传递数据,属性的初始值Request.meta;

body(str或unicode):用于定义请求体。如果unicode传递了,那么它被编码为 str 使用传递的编码(默认为utf-8)。如果 body没有给出,则存储一个空字符串。不管这个参数的类型,存储的最终值将是一个str(不会是unicode或None);

headers(dict): 用于定义请求头。dict值可以是字符串(对于单值标头)或列表(对于多值标头)。如果 None作为值传递,则不会发送HTTP头;

encoding:用于指定请求的编码,默认使用 'utf-8' ;

dont_filter:用于指定是否过滤重复的URL地址,默认为会过滤的(False);

cookie(dict或list):用于设置请求cookie。可以以如下两种方式发送:

  • 使用dict:
request_with_cookies = Request(url="http://www.example.com",cookies={'currency': 'USD', 'country': 'UY'},meta={'dont_merge_cookies': True})
  • 使用list:
 request_with_cookies = Request(url="http://www.example.com",cookies=[{'name': 'currency','value': 'USD','domain': 'example.com','path': '/currency'}])


12.2、meta的传递

概述:meta用于在不同的请求之间传递数据。简单说就是当一个完整的数据需要通过多次请求才能获取到时,就可以将前面获取到的数据通过meta参数传入后面调用的函数,方便最后一执行的函数整合所有数据。

实操11:在实操8的基础上完成实操11,通过该实操带你了解meta属性的作用(大概思路分为三步:①、通过某小说的首页获取该小说的标题和第一章的链接;②、再次请求获取的第一章的链接,并将标题传递到该请求的回调函数中;③、在回调函数中获取小说第一章的内容和第②步中传递过来的标题,并且最后将所有内容返回给引擎处理)。

①、在实操8的项目中创建一个新的爬虫文件:

②、编写爬虫文件:

import scrapyclass MymetaSpider(scrapy.Spider):name = "myMeta"allowed_domains = ["xddzw.com"]start_urls = ["https://www.xddzw.com/694/694982/index.html"]def parse(self, response):# 获取第一章的标题title = response.xpath('//div[@id="list"]/dl/dd[13]/a/text()').get()# 获取第一章的链接url = response.xpath('//div[@id="list"]/dl/dd[13]/a/@href').get()# urljoin()方法会自动提取域名,并将提取的域名与传入的url自动进行拼接whole_url = response.urljoin(url)# 发送请求(并将前面获取的标题通过meta参数进行传递)yield scrapy.Request(whole_url,callback=self.get_info,meta={"title":title})# 该函数是发送请求的回调函数,即发送请求后响应数据又该函数处理def get_info(self,response):mycontent = response.xpath('//div[@id="content"]/p/text()').getall()# 获取前一个函数中传递过来的数据mytitle = response.request.meta["title"]yield {"title":mytitle,"content":mycontent}

③、修改启动脚本:

from scrapy.cmdline import execute
execute(["scrapy","crawl","myMeta"])

运行结果如下:

文件内容如下:


13、FormRequest对象

概述:FormRequest是Request对象的扩展类,增加如下功能:①、发送请求时,可以携带参数,如表单数据;②、可以从Response中获取表单的数据。

知识点补充:FormRequest类可以携带参数主要原因是因为增加了新的构造函数的参数formdata,formdata参数类型为字典,其它参数与Request类相同。

实操11:本实操用于练习FormRequest对象的用法(以下是爬虫文件的内容)。

import scrapy
from scrapy.http import FormRequest, Responseclass MycookieSpider(scrapy.Spider):name = "form"allowed_domains = ["目标网站域名"]# start_urls = []# start_requests函数用于演示FormRequest的用法def start_requests(self) :# 请求参数formdata = {# formdata用于放请求时发送的参数(具体参数通过浏览器开发工具==》网络==》找到发送登录请求的信息==》载荷中获取,并且要模拟登录才会出现这些参数)}# 用return返回会报错yield FormRequest("发送post请求的地址(需要通过浏览器开发工具查看)",formdata=formdata)# parse函数和parse_info函数用于验证是否会自动保存cookie(省流:会自动保存cookie,但是验证时需要设置关闭过滤dont_filter=True)def parse(self, response):info = response.xpath('//div[@class="login_unames-out"]/span[@class="login_unames"]/text()').get()print(f"第一次获取到的用户名为:{info}")# 由于parse函数和parse_info函数获取的内容相同,所以需要不过滤相同的内容yield scrapy.Request("登录后的某个地址",callback=self.parse_info(),dont_filter=True)# parse_info函数用于直接解析获取的内容,如果能解析出来就说明自动保存了cookiedef parse_info(self, response):info = response.xpath('//div[@class="login_unames-out"]/span[@class="login_unames"]/text()').get()print(f"第一次获取到的用户名为:{info}")


14、下载中间件

14.1、初识下载中间件

概述:下载中间件是Scrapy请求/响应处理的钩子框架。这是一个轻、低层次的应用。通过可下载中间件,可以处理请求之前和请求之后的数据。

注意:每个中间件组件都是一个Python类,每个类定义了一个或多个方法,下面来介绍可能会用到的方法。

process_request(self, request, spider)

概述:当每个request通过下载中间件时,该方法被调用。

返回值(必须是以下的任意一个):

  • 返回 None

    • Scrapy 将继续处理该 request,执行其他的中间件的相应方法,直到合适的下载器处理函数(download handler)被调用,该 request 被执行(其 response 被下载)
  • 返回 Response 对象

    • Scrapy 将不会调用 任何 其他的 process_request()或 process_exception()方法,或相应地下载函数; 其将返回该 response。已安装的中间件的 process_response()方法则会在每个 response 返回时被调用
  • 返回 Request 对象

    • Scrapy 则停止调用 process_request 方法并重新调度返回的 request。当新返回的 request 被执行后, 相应地中间件链将会根据下载的 response 被调用
  • raise IgnoreRequest

    • 如果抛出 一个 IgnoreRequest 异常,则安装的下载中间件的 process_exception() 方法会被调用。如果没有任何一个方法处理该异常, 则 request 的 errback(Request.errback)方法会被调用。如果没有代码处理抛出的异常, 则该异常被忽略且不记录(不同于其他异常那样)

参数:

  • request (Request 对象) – 处理的request
  • spider (Spider 对象) – 该request对应的spider

process_response(self, request, response, spider)

概述:当下载器完成http请求,传递响应给引擎的时候调用。

process_response()返回一个 response 对象,或返回一个 request 对象亦或引发 IgnoreRequest异常情况。

  • 如果它返回 response(可能是相同的给定响应,也可能是全新的响应),该响应将继续使用 process_response() 链中的下一个中间件;

  • 如果它返回一个 Request 对象时,中间件链将暂停,返回的请求将重新计划为将来下载。这与从返回请求的行为相同 process_request()

  • 如果它引发了 IgnoreRequest 异常,请求的errback函数 (request.errback )。如果没有代码处理引发的异常,则忽略该异常,不记录该异常(与其他异常不同)。

  • 参数

    • request (is a Request object) -- 发起响应的请求
    • response (Responseobject) -- 正在处理的响应
    • spider (Spider object) -- 此响应所针对的蜘蛛

14.2、下载中间件设置UserAgent

注意:如果需要使用下载中间件需要提前在Scrapy的setting.py的配置文件中配置DOWNLOADER_MIDDLEWARES属性的值后才能正常使用。

开发UserAgent下载中间件

问题:

每次创建项目后,需要自己复制UserAgent到配置文件,操作繁琐。

解决方案:

开发下载中间件,设置UserAgent。

实操12:创建一个新的爬虫项目用于演示如何通过下载中间件设置UserAgent。

①、创建新的爬虫项目及其爬虫文件:

②、修改爬虫文件(1、打印响应结果;2、修改起始地址):

import scrapyclass UaSpider(scrapy.Spider):name = "ua"allowed_domains = ["httpbin.org"]start_urls = ["https://httpbin.org/get"]def parse(self, response):print(response. Text)

③、编辑启动脚本(在新建的scrapy08目录下新建一个python文件作为启动脚本):

from scrapy.cmdline import execute
execute(["scrapy","crawl","ua"])

④、启动脚本,获取Scrapy中UserAgent的位置:

⑤、找到scrapy.downloadermiddlewares.useragent.UserAgentMiddleware中的内容并复制process_request函数:

⑥、编辑middlewares.py文件(提前把文件内容全部清空后写入以下内容):

# 需要提前下载fake_useragent
# 为了能动态的生成UserAgent,需要用到fake_useragent模块
from fake_useragent import UserAgentclass MyMiddlewares:def process_request(self, request, spider):request.headers.setdefault(b"User-Agent", UserAgent().edge)

⑦、编辑settings.py文件(开启DOWNLOADER_MIDDLEWARES):

DOWNLOADER_MIDDLEWARES = {# 后面的权重一定要更改一个更小的值,否则会默认生效自带的UserAgent"scrapy08.middlewares.MyMiddlewares": 400,
}

⑧、再次启动爬虫脚本并查看结果:

实操小结:虽然我们通过模仿scrapy自带的useragent的写法通过下载中间件动态的设置了UserAgent,但是每次新建新的项目后都要设置这些内容未免有些繁琐,因此可以考虑使用第三方模块实现UserAgent的设置。

第三方模块参考地址:scrapy-fake-useragent · PyPI

实操13:使用第三方模块实现Scrapy的UserAgent动态设置(本实操在实操12的基础上进行)。

①、安装第三方模块(pycharm通过ALT+F12打开控制台):

②、替换settings.py配置文件中的DOWNLOADER_MIDDLEWARES并添加FAKEUSERAGENT_PROVIDERS(使用时直接将以下内容复制粘贴即可):

# 关闭内置的 UserAgentMiddleware 和 RetryMiddleware,并添加 RandomUserAgentMiddleware 和 RetryUserAgentMiddleware
DOWNLOADER_MIDDLEWARES = {'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,'scrapy.downloadermiddlewares.retry.RetryMiddleware': None,'scrapy_fake_useragent.middleware.RandomUserAgentMiddleware': 400,'scrapy_fake_useragent.middleware.RetryUserAgentMiddleware': 401,
}# 配置 UserAgent 类型
FAKEUSERAGENT_PROVIDERS = ['scrapy_fake_useragent.providers.FakeUserAgentProvider',  # 第一选择'scrapy_fake_useragent.providers.FakerProvider',  # 第二选择'scrapy_fake_useragent.providers.FixedUserAgentProvider',  # 第三选择
]

③、运行脚本并查看结果:


14.3、下载中间件设置代理

概述:爬虫设置代理就是让别的服务器或电脑代替自己的服务器去获取数据。

实操14:通过本实操了解下载中间件如何设置代理(本实操在实操13的基础上进行)。

①、修改middlewares.py文件(添加一个新类用于设置代理):

# 写法参考HttpProxyMiddleware
from scrapy.downloadermiddlewares.httpproxy import HttpProxyMiddleware
class MyProxyMiddleware:def process_request(self,request,spider):"""request.meta['proxy'] = 'type://ip:port'request.meta['proxy'] = 'type://name:pwd@ip:port'"""# 这里的ip和port是通过搜索免费代理找到的 request. Meta['proxy'] = 'http://47.92.219.102:3128'

②、修改settings.py配置文件(主要是修改DOWNLOADER_MIDDLEWARES的属性):

DOWNLOADER_MIDDLEWARES = {'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,'scrapy.downloadermiddlewares.retry.RetryMiddleware': None,'scrapy_fake_useragent.middleware.RandomUserAgentMiddleware': 400,'scrapy_fake_useragent.middleware.RetryUserAgentMiddleware': 401,# 添加如下内容(关闭内置代理并添加自己的代理)'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware':None,'scrapy08.middlewares.MyProxyMiddleware':402
}

③、启动脚本文件begin.py文件并查看结果

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用Java手写jvm之模拟方法调用指令invokexxx和方法返回指令xreturn

写在前面 源码 。 本文一起看下方法调用相关的指令invokexxx以及方法返回&#xff08;栈帧弹出线程栈&#xff09;相关的指令xReturn 。 1&#xff1a;正文 因为invokexxx指令和普通的指令不同&#xff0c;会创建一个新的栈帧&#xff0c;并压倒操作数栈中&#xff0c;所以我…

【python】Python中实现定时任务常见的几种方式原理分析与应用实战

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

【OpenCV C++20 学习笔记】腐蚀和膨胀

腐蚀和膨胀 形态学原理膨胀腐蚀 代码实现膨胀函数腐蚀函数运行结果 形态学原理 腐蚀和膨胀通常有以下用途&#xff1a; 去除噪音分离或合并图像中的元素找出图片上的强度的极大值区域和极小值区域 以下图作为原始图片&#xff1a; 膨胀 用核 B B B来扫描图像 A A A&#xff…

VBA学习(22):动态显示日历

这是在ozgrid.com论坛上看到的一个贴子&#xff0c;很有意思&#xff0c;本来使用公式是可以很方便在工作表中实现日历显示的&#xff0c;但提问者因其需要&#xff0c;想使用VBA实现动态显示日历&#xff0c;即根据输入的年份和月份在工作表中显示日历。 下面是实现该效果的VB…

喜报!DAP-seq文章6连发,总IF 95.2

2024年4月29日&#xff0c;河北农业大学林学院李保国山区产业开发与林果产业创新团队与园艺学院田义教授团队联合西北农林科技大学马锋旺教授团队及沈阳农业大学马跃教授团队在Plant Biotechnology Journal&#xff08;影响因子10.1&#xff09;上发表了题为“The MdVQ37-MdWRK…

2024最新版Python基础入门学习路线

Python基础入门学习路线可以概括为以下几个阶段&#xff0c;每个阶段都包含了关键的学习内容和目标&#xff1a; 一、Python语言基础 1. 初识Python语言 Python语言概述&#xff1a;了解Python的起源、特点、应用领域以及发展趋势。环境安装&#xff1a;学习如何在不同的操作系…

18987 随机数(测验)

这个问题可以通过使用集合&#xff08;set&#xff09;和排序来解决。集合是一种数据结构&#xff0c;它可以自动去除重复的元素。然后我们可以将集合中的元素转移到一个数组中&#xff0c;并对&#xfffd;&#xfffd;组进行排序。 以下是使用C的代码实现&#xff1a; #i…

浅谈哈希与哈希表(c++)

目录 一、哈希的基本概念&#xff08;一&#xff09;哈希函数的特性&#xff08;二&#xff09;哈希冲突 二、C 中的哈希表实现三、哈希表的性能分析四、哈希表的应用场景五、优化哈希表的策略六、例题讲解【模板】字符串哈希题目描述输入格式输出格式样例 #1样例输入 #1样例输…

工业5G路由器赋能户外组网远程监控及预警

随着物联网、大数据、云计算等技术的快速发展&#xff0c;工业领域对于远程监控、实时预警和数据传输的需求日益增长。特别是在户外复杂环境下&#xff0c;传统的有线网络组网方式面临着布线难度大、成本高、维护困难等问题。 工业5G路由器在户外组网远程监控预警应用基于高速…

Android开发之事件分发

#来自ウルトラマンゼロ&#xff08;哉阿斯&#xff09; 1 Activity 构成 平常布局展示在ContentView中。 2 事件分发 事件分发的本质其实就是把事件&#xff08;Touch&#xff09;封装成 MotionEvent 类&#xff0c;然后传递给 View 的层级处理。 MotionEvent 事件类型主要有…

RAG与Fine Tuning:如何选择正确的方法

今日份知识你摄入了么&#xff1f; 生成式人工智能有潜力改变你的业务和数据工程团队&#xff0c;但前提是要正确实施。那么&#xff0c;你的数据团队如何才能真正利用大型语言模型或生成式人工智能_&#xff08;GenAI&#xff09;_计划来驱动价值呢&#xff1f; 领先的组织通…

我在高职教STM32——I2C通信入门(1)

大家好,我是老耿,高职青椒一枚,一直从事单片机、嵌入式、物联网等课程的教学。对于高职的学生层次,同行应该都懂的,老师在课堂上教学几乎是没什么成就感的。正是如此,才有了借助CSDN平台寻求认同感和成就感的想法。在这里,我准备陆续把自己花了很多心思设计的教学课件分…

Sentinel-1 Level 1数据处理的详细算法定义(五)

《Sentinel-1 Level 1数据处理的详细算法定义》文档定义和描述了Sentinel-1实现的Level 1处理算法和方程,以便生成Level 1产品。这些算法适用于Sentinel-1的Stripmap、Interferometric Wide-swath (IW)、Extra-wide-swath (EW)和Wave模式。 今天介绍的内容如下: Sentinel-1 L…

Python爬虫新手指南及简单实战

网络爬虫是自动化获取网络信息的高效工具&#xff0c;Python因其强大的库支持和简洁的语法成为编写网络爬虫的首选语言。本教程将通过一个具体的案例&#xff08;基于Microsoft Edge浏览器的简单爬取&#xff09;&#xff0c;指导你使用Python实现一个完整的网络爬虫&#xff0…

NIO专题学习(一)

一、BIO/NIO/AIO介绍 1. 背景说明 在Java的软件设计开发中&#xff0c;通信架构是不可避免的。我们在进行不同系统或者不同进程之间的数据交互&#xff0c;或者在高并发的通信场景下都需要用到网络通信相关的技术。 对于一些经验丰富的程序员来说&#xff0c;Java早期的网络…

PXE 服务器搭建——启动界面设计实验

环境准备&#xff1a; 前期准备&#xff1a; 解决 kickstart 实验出现的 DHCP 的问题-CSDN博客 http://t.csdnimg.cn/5vZP0 当前准备&#xff1a; 两台虚拟机&#xff1a;RHEL7 OpenEuler(作为测试机器使用) ip&#xff1a;172.25.254.100 yum install syslinux.x…

【Web开发手礼】探索Web开发的秘密(十五)-Vue2(2)AJAX、前后端分离、前端工程化

主要介绍了AJAX、前后端分离所需的YApi、前端工程化所需要的环境安装&#xff01;&#xff01;&#xff01; 目录 前言 AJAX ​原生Ajax Axios Axios入门 案例 前后端分离开发 YApi ​前端工程化 环境准备 总结 前言 主要介绍了AJAX、前后端分离所需的YApi、前端工…

2024华数杯c题题解(一)

目录 原题背景 背景分析 问题一 思路 代码 问题二 思路 代码 原题背景 最近&#xff0c;“city 不 city”这一网络流行语在外国网红的推动下备受关注。随着我国过境免签政策的落实&#xff0c;越来越多外国游客来到中国&#xff0c;通过网络平台展示他们在华旅行的见闻…

出现 No mapping for DELETE/GET等

出现 No mapping for DELETE/GET等 错误一&#xff1a;请求url不对 修改前 如下图可知后端请求url为http://localhost:8080/user/addressBook 运行后控制台出现 发现后端请求url比前端请求url少了/ 改正&#xff1a; 在DeleteMapping后面加上 / DeleteMapping("/&quo…

mysql操作(进阶)

1.数据库约束 数据库自动对数据的合法性进行校验检查的一系列机制&#xff0c;目的是为了保证数据库中能够避免被插入或者修改一些非法数据。 &#xff08;1&#xff09;mysql中提供了以下的约束&#xff1a; a.NOT NULL&#xff1a;指定某列不能为null b.UNIQUE&#xff1…