免责声明

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

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 scrapy

class 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 csv

class 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 scrapy

class 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 scrapy

class 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 scrapy

class 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 csv

class 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 scrapy

class 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 scrapy

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

④、编辑爬虫文件:

import scrapy
from scrapy05.items import Scrapy05Item

class 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'] = name
            item['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 Request

class Scrapy06Pipeline:
    def process_item(self, item, spider):
        return item
# 添加一个自定义类并继承ImagePipeline
class MyImagePipeline(ImagesPipeline):
    # 下面的函数都是对ImagesPipeline已有方法的重写

    # 该函数用于获取图片并返回给Engine
    def 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 scrapy

class 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, Rule

class 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 scrapy

class 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, Response

class 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函数用于直接解析获取的内容,如果能解析出来就说明自动保存了cookie
    def 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 scrapy

class 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 UserAgent

class 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文件并查看结果

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐