python爬虫--scrapy框架

news2024/11/24 5:41:42

请添加图片描述
Scrapy

一 介绍

Scrapy简介

1.Scrapy是用纯Python实现一个为了爬取网站数据、提取结构性数据而编写的应用框架,用途非常广泛

2.框架的力量,用户只需要定制开发几个模块就可以轻松的实现一个爬虫,用来抓取网页内容以及各种图片,非常之方便


Scrapy主要包括了以下组件:

1.引擎(Scrapy)
	用来处理整个系统的数据流处理, 触发事务(框架核心)
	
2.调度器(Scheduler)
    用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址
    
3.下载器(Downloader)
	用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)
	
4.爬虫(Spiders)
	爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面
	
5.项目管道(Pipeline)
	负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
	
6.下载器中间件(Downloader Middlewares)
	位于Scrapy引擎和下载器之间的框架,主要是处理Scrapy引擎与下载器之间的请求及响应。
	
7.爬虫中间件(Spider Middlewares)
	介于Scrapy引擎和爬虫之间的框架,主要工作是处理蜘蛛的响应输入和请求输出。
	
8.调度中间件(Scheduler Middewares)
	介于Scrapy引擎和调度之间的中间件,从Scrapy引擎发送到调度的请求和响应。

Scrapy运行流程

1.引擎从调度器中取出一个链接(URL)用于接下来的抓取,包括过滤器和对列,过滤后的url交给对列
2.引擎把URL封装成一个请求(Request)传给下载器
3.下载器把资源下载下来,并封装成应答包(Response)
4.爬虫解析Response
5.解析出实体(Item),则交给引擎,在提交到管道进行进一步的处理(持久化存储处理)
6.解析出的是链接(URL),则把URL交给调度器等待抓取
都会经过引擎进行调度

二 安装

#Windows平台
    1、pip3 install wheel #安装后,便支持通过wheel文件安装软件,wheel文件官网:https://www.lfd.uci.edu/~gohlke/pythonlibs
    3、pip3 install lxml
    4、pip3 install pyopenssl
    5、下载并安装pywin32:https://sourceforge.net/projects/pywin32/files/pywin32/
    6、下载twisted的wheel文件:http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
    7、执行pip3 install 下载目录\Twisted-17.9.0-cp36-cp36m-win_amd64.whl
    8、pip3 install scrapy

#Linux平台
    1、pip3 install scrapy

三 命令行工具

介绍

#1 查看帮助
    scrapy -h
    scrapy <command> -h

#2 有两种命令:其中Project-only必须切到项目文件夹下才能执行,而Global的命令则不需要
    Global commands: #全局命令
        startproject #创建项目
        genspider    #创建爬虫程序 
           # 示例 scrapy genspider  baidu     www.baidu.com 
                           
                    
        settings     #如果是在项目目录下,则得到的是该项目的配置
        runspider    #运行一个独立的python文件,不必创建项目
        shell        #scrapy shell url地址  在交互式调试,如选择器规则正确与否
        fetch        #独立于程单纯地爬取一个页面,可以拿到请求头
        view         #下载完毕后直接弹出浏览器,以此可以分辨出哪些数据是ajax请求
        version      #scrapy version 查看scrapy的版本,scrapy version -v查看scrapy依赖库的版本
        
    Project-only commands: #项目文件下
        crawl        #运行爬虫,必须创建项目才行,确保配置文件中ROBOTSTXT_OBEY = False
        check        #检测项目中有无语法错误
        list         #列出项目中所包含的爬虫名
        edit         #编辑器,一般不用
        parse        #scrapy parse url地址 --callback 回调函数  #以此可以验证我们的回调函数是否正确
        bench        #scrapy bentch压力测试

#3 官网链接
    https://docs.scrapy.org/en/latest/topics/commands.html

示例

#1、执行全局命令:请确保不在某个项目的目录下,排除受该项目配置的影响
scrapy startproject MyProject(项目名)

cd MyProject (切换到项目下)
scrapy genspider baidu www.baidu.com  #创建爬虫程序
					      #(baidu)爬虫名    对应相关域名(www.baidu.com可以先随便写个www.xxx.com ) 
                            #代表这个爬虫程序只能爬取www.baidu.com 这个域名或者百度的子域名

scrapy settings --get XXX #如果切换到项目目录下,看到的则是该项目的配置

scrapy runspider baidu.py #执行爬虫程序

scrapy shell https://www.baidu.com
    response
    response.status
    response.body
    view(response)
    
scrapy view https://www.taobao.com #如果页面显示内容不全,不全的内容则是ajax请求实现的,以此快速定位问题

scrapy fetch --nolog --headers https://www.taobao.com

scrapy version #scrapy的版本

scrapy version -v #依赖库的版本

#2、执行项目命令:切到项目目录下
scrapy crawl baidu
scrapy check
scrapy list
scrapy parse http://quotes.toscrape.com/ --callback parse
scrapy bench

四 项目结构以及爬虫应用简介

目录结构

project_name/
   scrapy.cfg
   project_name/
       __init__.py
       items.py
       pipelines.py
       settings.py
       spiders/
           __init__.py
           爬虫1.py
           爬虫2.py
           爬虫3.py

应用说明

scrapy.cfg:爬虫项目的配置文件。

__init__.py:爬虫项目的初始化文件,用来对项目做初始化工作。

items.py:爬虫项目的数据容器文件,用来定义要获取的数据。

pipelines.py:爬虫项目的管道文件,用来对items中的数据进行进一步的加工处理。

settings.py:爬虫项目的设置文件,包含了爬虫项目的设置信息。

middlewares.py:爬虫项目的中间件文件,


pycharm中运行爬虫程序

在项目目录先创建entrypoint.py文件,文件名不能变
from scrapy.cmdline import execute
execute(['scrapy','crawl','baidu','--nolog']) #百度为爬虫名,列表的前两项不变 --nolog可写可不写,作用是不在打印其他配置项,只打印需要的内容

五 Spiders

1.介绍

1.Spider是由一系列类(定义了一个网址一组网址将别爬取)组成,具体包括了如何执行爬取任务并且如何从页面中提取结构化的数据
2.Spider是你为了一个特定的网址或一组网址自定义爬取或解析页面行为的地方

2.Spider会循环做的事情

#1、生成初始的Requests来爬取第一个URLS,并且标识一个回调函数
第一个请求定义在start_requests()方法内默认从start_urls列表中获得url地址来生成Request请求,默认的回调函数是parse方法。回调函数在下载完成返回response时自动触发

#2、在回调函数中,解析response并且返回值
返回值可以4种:
        包含解析数据的字典
        Item对象
        新的Request对象(新的Requests也需要指定一个回调函数)
        或者是可迭代对象(包含Items或Request)

#3、在回调函数中解析页面内容
通常使用Scrapy自带的Selectors,但很明显你也可以使用Beutifulsoup,lxml或其他你爱用啥用啥。

#4、最后,针对返回的Items对象将会被持久化到数据库
通过Item Pipeline组件存到数据库:https://docs.scrapy.org/en/latest/topics/item-pipeline.html#topics-item-pipeline)
或者导出到不同的文件(通过Feed exports:https://docs.scrapy.org/en/latest/topics/feed-exports.html#topics-feed-exports)

3.爬取格式

entrypoint.py

from scrapy.cmdline import execute
execute(['scrapy','crawl','amazon1','-a','keywords=iphone8手机','--nolog'])
加参数的格式

# -*- coding: utf-8 -*-
import scrapy
from urllib.parse import urlencode

class Amazon1Spider(scrapy.Spider):
    name = 'amazon1'
    allowed_domains = ['www.amazon.cn']   #也可以不用,写的话就会对请求的url进行限制,下边的start_urls就只能请求allowed_domains中url
    start_urls = ['https://www.amazon.cn/'] #可以放多个请求的url
	#不写allowed_domains,就可以在start_urls请求列表中写多个url
    
    # 自定义的配置,可以加请求头系列的配置,先从这里找,没有去settings中找
    custom_settings = {
        'REQUEST_HEADERS':{
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36',

        }
    }
    
    # 规定外部传进来的参数
    def __init__(self,keywords,*args,**kwargs):
        super(Amazon1Spider,self).__init__(*args,**kwargs)
        self.keywords=keywords

    # 发送请求
    def start_requests(self):
        url = 'https://www.amazon.cn/s?%s&ref=nb_sb_noss_1' %(urlencode({'k':self.keywords}))
        yield scrapy.Request(url=url,
                             callback=self.parse,
                             dont_filter=True
                             ) #请求方式GET
        #post请求 : scrapy.FormRequest(url,formdata=data,callback)
        
    # 解析
    def parse(self, response):
        detail_url = response.xpath('//*[@id="search"]/div[1]/div[2]/div/span[4]/div[1]/div[1]/div/span/div/div/div[2]/div[3]/div/div/h2/a/@href') #如果这里得到多个url,商品详情url求,通过for循环再次对详情的url返送请求,每次返送请求必须有对应的回调函数
        for url in detail_url: 
            yiled yield scrapy.Request(url=url,
                             callback=self.parse_detail
                             dont_filter=True
                             )
        print(detail_url)
        
    def parse_detail(self,response):
        print(response) #详情页的响应结果
        
    def close(spider, reason): #解析之后执行close
        print('结束')


4.示例

爬取三国演义的文章标题以及每篇文章的内容
from scrapy.cmdline import execute
execute(['scrapy','crawl','sang','--nolog'])

# -*- coding: utf-8 -*-
import scrapy
class SangSpider(scrapy.Spider):
    name = 'sang'
    allowed_domains = ['www.shicimingju.com/book/sanguoyanyi.html']
    start_urls = ['http://www.shicimingju.com/book/sanguoyanyi.html/']

    custom_settings = { #加一些请求头信息
        'REQUEST_HEADERS':{
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36',
        }
    }
    def start_requests(self):
        url = 'http://www.shicimingju.com/book/sanguoyanyi.html/'
        yield scrapy.Request(url=url,
                             callback=self.parse,#解析主页面的回调函数
                             ) #向主页面发起请求

    def parse(self, response): #解析主页面的响应信息
        all_urls = response.xpath('/html/body/div[4]/div[2]/div[1]/div/div[4]/ul/li/a/@href').extract()#所有的文章的url 
        all_title = response.xpath('/html/body/div[4]/div[2]/div[1]/div/div[4]/ul/li/a/text()').extract()#所有标题

        for url in all_urls:
            # print(response.urljoin(url)) #拼接url 
            detail_urls = 'http://www.shicimingju.com'+url
            print(detail_urls)
            yield scrapy.Request(url=detail_urls,  
                                 callback=self.parse_detail,#解析详情页面的回调函数
                                 dont_filter=True
                                 ) #向详细文章发送请求
    def parse_detail(self,response):
        content = response.xpath('/html/body/div[4]/div[2]/div[1]/div[1]/div/p/text()') #解析详情文章的响应
        print(content)


5.数据解析

- scrapy中封装的xpath的方式进行数据解析。
- scrapy中的xpath和etree中的xpath的区别是什么?
	etree中的xpath返回的是字符串或列表
	scrapy的xpath进行数据解析后返回的列表元素为Selector对象,然后必须通过extract或者extract_first这两个方法将Selector对象中的对应数据取出

extract_first:将列表元素中第0个selector对象提取
extract:取出列表中的每一个selector对象提取

6.数据持久化存储

基于终端的指令存储
特性:只能将parse方法的返回值存储到本地的磁盘文件中
指令:scrapy crawl 爬虫程序名 -o 文件名
局限性:只能存储到磁盘中,存储的文件名有限制,只能用提供的文件名

示例
# 爬取糗事百科笑话的标题和内容
import scrapy
class QiubSpider(scrapy.Spider):
    name = 'qiub'
    start_urls = ['http://www.lovehhy.net/Joke/Detail/QSBK/']

    def parse(self, response):
        all_data = []
        #response.xpath拿到的是个列表里面是Selector对象
        title = response.xpath('//*[@id="footzoon"]/h3/a/text()').extract()
        content = response.xpath('//*[@id="endtext"]//text()').extract() #因为在内容中有对个br标签进行分割,所以用//text()
        title=''.join(title)
        content=''.join(content)
        dic = {
            'title':title,
            'content':content
        }
        all_data.append(dic)

        return all_data #必须有返回值才能用基于终端指令的存储
终端存储指令:scrapy crawl qiub -o qiubai.csv(csv是提供的文件类型)    

基于管道存储
实现流程
- 基于管道:实现流程
    1.数据解析
    2.在item类中定义相关的属性
    3.将解析的数据存储或者封装到一个item类型的对象(items文件中对应类的对象)
    4.向管道提交item
    5.在管道文件的process_item方法中接收item进行持久化存储
    6.在配置文件中开启管道
    
管道只能处理item类型的对象

文件形式
示例

qiub.py

# -*- coding: utf-8 -*-
import scrapy
from qiubai.items import QiubaiItem

class QiubSpider(scrapy.Spider):
    name = 'qiub'

    start_urls = ['http://www.lovehhy.net/Joke/Detail/QSBK/']
    def parse(self, response):
        all_data = []
        title = response.xpath('//*[@id="footzoon"]/h3/a/text()').extract()
        content = response.xpath('//*[@id="endtext"]//text()').extract()
        title=''.join(title)
        content=''.join(content)
        dic = {
            'title':title,
            'content':content
        }
        all_data.append(dic)
        item = QiubaiItem() #实例化一个item对象
        item['title'] = title #封装好的数据结构
        item['content'] = content
        yield item #向管道提交item,提交给优先级最高的管道类

items.py

import scrapy
class QiubaiItem(scrapy.Item):
    # define the fields for your item here like:
    title = scrapy.Field() #Fieid可以接受任意类型的数据格式/类型
    content = scrapy.Field()

pipelines.py

class QiubaiPipeline(object):
    f= None
    print('开始爬虫.....')
    def open_spider(self,spider): # 重写父类方法,开启文件
        self.f = open('qiubai.txt','w',encoding='utf-8')

    def close_spider(self,spider):# 重写父类方法,关闭文件
        print('结束爬虫')
        self.f.close()

    def process_item(self, item, spider):
        #item是管道提交过来的item对象
        title = item['title']  #取值
        content = item['content']
        self.f.write(title+':'+content+'\n') #写入文件
        return item  

settings.py

# 开启管道,可以开启多个管道
ITEM_PIPELINES = {
   'qiubai.pipelines.QiubaiPipeline': 300, #300表示的优先级
}

将同一份数据持久化到不同的平台
1.管道文件中的一个管道类负责数据的一种形式的持久化存储
2.爬虫文件向管道提交的item只会提交给优先级最高的那一个管道类
3.在管道类的process_item中的return item表示的是将当前管道接收的item返回/提交给下一个即将被执行的管道类

数据库(mysql)
示例
import pymysql
# 负责将数据存储到mysql
class MysqlPL(object):
    conn = None
    cursor = None

    def open_spider(self, spider):
        self.conn = pymysql.Connect(host='127.0.0.1', port=3306, user='root', password='123', db='spider',charset='utf8')
        print(self.conn)

    def process_item(self, item, spider):
        author = item['author']
        content = item['content']
        sql = 'insert into qiubai values ("%s","%s")' % (author, content)
        self.cursor = self.conn.cursor()
        try:
            self.cursor.execute(sql) #执行正确的话就提交事务
            self.conn.commit()
        except Exception as e:
            print(e)
            self.conn.rollback()  #回滚,如果出现错误就回滚
        return item
	
    def close_spider(self, spider):
        self.cursor.close()
        self.conn.close()

7.爬取校花网图片

ImagesPipeline的简介
专门爬取图片的管道

1.爬取一个Item,将图片的URLs放入image_urls字段
2.从Spider返回的Item,传递到Item Pipeline
3.当Item传递到ImagePipeline,将调用Scrapy 调度器和下载器完成4.image_urls中的url的调度和下载。
5.图片下载成功结束后,图片下载路径、url和校验和等信息会被填充到images字段中。

ImagesPipeline的使用:
from scrapy.pipelines.images import ImagesPipeline
import scrapy
# 通过重写父类方法
class SpiderImgPipeline(ImagesPipeline):
   
    # 对某一个媒体资源进行请求发送
    # item是提交过来的item(src)
    def get_media_requests(self, item, info): 
        yield scrapy.Request(item['src'])

    # 制定媒体数据存储的名称
    def file_path(self, request, response=None, info=None):
        img_name = request.url.split('/')[-1]
        print(img_name+'正在爬取')
        return img_name

    # 将item传递个下一个即将被执行的管道类
    def item_completed(self, results, item, info):
        return item

# 在配置中添加图片的存储路径
IMAGES_STORE = './imgslib'

爬取示例:

settings.py

IMAGES_STORE = './imgslib' #存储爬取到的图片
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36'
ROBOTSTXT_OBEY = False
LOG_LEVEL = 'ERROR'
ITEM_PIPELINES = {
   'spider_img.pipelines.SpiderImgPipeline': 300,
}

img.py

# -*- coding: utf-8 -*-
import scrapy
from spider_img.items import SpiderImgItem

class ImgSpider(scrapy.Spider):
    name = 'img'
    start_urls = ['http://www.521609.com/daxuexiaohua/']
    url= 'http://www.521609.com/daxuexiaohua/list3%d.html' #通用的url模板,需要加page页数,每个页数的模板
    page_num = 1
    def parse(self, response):
        li_list = response.xpath('//*[@id="content"]/div[2]/div[2]/ul/li')
        for li in li_list:
            img_src = 'http://www.521609.com/' + li.xpath('./a[1]/img/@src').extract_first() #获取的所有的图片的url,extract_first获取的字符串类型,extract获取的是list,里面存了selector对象
            item = SpiderImgItem() #实例化item对象
            item['src'] = img_src  #把数据添加到item中
            yield item #提交到item

        if self.page_num < 3: #爬取的前两页
            self.page_num += 1
            new_url = format(self.url%self.page_num)  #其他页数的url
            yield scrapy.Request(new_url,callback=self.parse)  #对页数发请求

items.py

import scrapy
class SpiderImgItem(scrapy.Item):
    src = scrapy.Field()
    pass

piplines.py

from scrapy.pipelines.images import ImagesPipeline
import scrapy

class SpiderImgPipeline(ImagesPipeline):
    # 对某一个媒体资源进行请求发送
    # item是提交过来的item(src)
    def get_media_requests(self, item, info):
        yield scrapy.Request(item['src'])
	
    # 制定媒体数据存储的名称
    def file_path(self, request, response=None, info=None):
        img_name = request.url.split('/')[-1]
        print(img_name+'正在爬取')
        return img_name

    # 将item传递个下一个即将被执行的管道类
    def item_completed(self, results, item, info):
        return item

8.请求传参(深度爬取)

示例:爬取4567tv电影网的电影信息

movie.py

# 需求:爬取电影网的电影名称和电影的简介
# 分析:去电影网的首页请求会看到所有的电影,但不能获取简介,需要现获取所有的电影的url,在对每个电影的url反请求,对电影详情发请求获取简介

import scrapy
from movie_spider.items import MovieSpiderItem

class MovieSpider(scrapy.Spider):
    name = 'movie'
    start_urls = ['https://www.4567tv.tv/index.php/vod/show/class/%E5%8A%A8%E4%BD%9C/id/1.html']
    page_num = 1
    def parse(self, response):
        movie_list = response.xpath('/html/body/div[1]/div/div/div/div[2]/ul/li')
        for movie_li in movie_list:
            movie_title = movie_li.xpath('./div[1]/a/@title').extract_first()
            movie_url = 'https://www.4567tv.tv'+movie_li.xpath('./div[1]/a/@href').extract_first()
            item = MovieSpiderItem()
            item['movie_title'] = movie_title
            yield scrapy.Request(movie_url,callback=self.parse_movie_detail,meta={'item':item})#请求传参

        if self.page_num < 5:
            self.page_num += 1
            new_url = f'https://www.4567tv.tv/index.php/vod/show/class/%E5%8A%A8%E4%BD%9C/id/1/page/{self.page_num}.html'
			
            yield scrapy.Request(url=new_url,callback=self.parse,meta={'item':item})

    def parse_movie_detail(self,response):
        item = response.meta['item']#取出item,接收
        movie_about = response.xpath('/html/body/div[1]/div/div/div/div[2]/p[5]/span[2]/text()').extract_first()
        item['movie_about'] = movie_about #电影的简介
        yield item

# 深度爬取(传参):在电影首页获取不到电影的简介,在最后提交item时,电影的标题和电影的简介在不同的解析回调函数中,所以先把标题放在item中(但是不提交),传递给下一个回调函数,继续用item,向item中存入要提交的数据,!!!

# 为什么不能设置为全局?
	# 因为在最后提交yield item时会产生数据覆盖现象
    
item['movie_title'] = movie_title #存
meta={'item':item}   # meta参数传递
item = response.meta['item'] #取

items.py

import scrapy

class MovieSpiderItem(scrapy.Item):
    # define the fields for your item here like:
    movie_title = scrapy.Field()
    movie_about = scrapy.Field()
    pass

pipelines.py

import pymysql
class MovieSpiderPipeline(object):
    conn = None
    cursor = None

    def open_spider(self, spider):
        self.conn = pymysql.Connect(host='127.0.0.1', port=3306, user='root', password='123', db='movie_spider',charset='utf8')

    def process_item(self, item, spider):
        movie_title = item['movie_title']
        movie_about = item['movie_about']
        sql = 'insert into movie_info values ("%s","%s")' % (movie_title, movie_about)
        self.cursor = self.conn.cursor()
        try:
            self.cursor.execute(sql)  # 执行正确的话就提交事务
            self.conn.commit()
        except Exception as e:
            print(e)
            self.conn.rollback()  # 回滚,如果出现错误就回滚
        return item

    def close_spider(self, spider):
        self.cursor.close()
        self.conn.close()

9.scrapy中间件的应用

下载中间件
  • 作用:批量拦截请求和响应

  • 拦截请求:

    ​ 1.UA伪装

    ​ 2.代理操作

  • 拦截响应:

    ​ 1.篡改响应数据,篡改不满足需求的响应对象,例如有动态加载的数据,直接请求是请求不到的

    ​ 2.直接更换响应对象

- process_request  request通过下载中间件时,该方法被调用
- process_response 下载结果经过中间件时被此方法处理
- process_exception 下载过程中出现异常时被调用


# process_request(request, spider) :

  当每个request通过下载中间件的时候, 该方法被调用, 该方法必须返回以下三种中的任意一种:

    1. None: Scrapy将继续处理该request, 执行其他的中间件的响应方法, 知道何时的下载器处理函数(download handler)被调用, 该request被执行(其resposne被下载)

    2. Response对象: Scrapy将不会调用人其他的process_request()或者process_exception()方法, 或者响应的下载函数; 其将返回response. 已安装的中间件的process_response()方法胡子爱每个res[onse返回时被调用

    3. Request对象或raise异常: 

      返回Request对象时: Scrapy停止调用process_request方法并重新调度返回的request. 当新的request被执行之后, 相应的中间件将会根据下载的response被调用.

      当raises异常时: 安装的下载中间件的process_exception()方法会被调用. 如果没有任何一个方法处理该异常, 则request的errback(Request.errback)方法被调用. 如果没有代码处理抛出的异常, 则该异常被忽略且不被记录.

# process_response(request, response, spider) :

  process_response的返回值也是有三种:

    1. response对象: 如果返回的是一个Resopnse(可以与传入的response相同, 也可以是全新的对象), 该response会被链中的其他中间件的process_response()方法处理.

    2. Request对象: 如果其返回一个Request对象, 则中间件链停止, 返回的request会被重新调度下载. 处理类似于process_request()返回request所做的那样.

    3. raiseu异常: 如果其抛出一个lgnoreRequest异常, 则调用request的errback(Request.errback). 如果没有代码处理抛出的异常, 则该异常被忽略且不记录.

process_exception(request, exception, spider) :

  当下载处理器(downloader handler)或者process_request()(下载中间件)抛出异常(包括lgnoreRequest异常)时, Scrapy调用process_exception().

# process_exception()也是返回三者中的一个:

    1. 返回None: Scrapy将会继续处理该异常, 接着调用已安装的其他中间件的process_exception()方法,知道所有的中间件都被调用完毕, 则调用默认的异常处理.

    2. 返回Response: 已安装的中间件链的process_response()方法被调用. Scrapy将不会调用任何其他中间件的process_exception()方法.

    3. 返回一个Request对象: 返回的额request将会被重新调用下载. 浙江停止中间件的process_exception()方法的执行, 就如返回一个response那样. 相当于如果失败了可以在这里进行一次失败的重试, 例如当访问一个网站出现因为频繁爬取被封ip就可以在这里设置增加代理继续访问.

爬虫中间件
案例

爬虫程序

# -*- coding: utf-8 -*-
import scrapy
import requests
from wangyi.items import WangyiItem
from selenium import webdriver
class WySpider(scrapy.Spider):
    name = 'wy'
    start_urls = ['https://news.163.com/']

    un_url = []
    bro = webdriver.Chrome(executable_path=r'C:\pycahrm文件\chromedriver.exe')

    def parse(self, response):
        li_list = response.xpath('//*[@id="index2016_wrap"]/div[1]/div[2]/div[2]/div[2]/div[2]/div/ul/li')
        model_indexs = [3, 4, 6, 7, 8]
        for index in model_indexs:
            li_tag = li_list[index]
            # 解析出了每一个板块对应的url
            model_url = li_tag.xpath('./a/@href').extract_first()
            self.un_url.append(model_url)  # 里面的内容是动态生成的,所以是不满足条件的url,需要在中间件中进行进一步处理
            yield scrapy.Request(model_url,callback=self.news_parse)

    def news_parse(self, response):
        div_list = response.xpath('/html/body/div/div[3]/div[4]/div[1]/div/div/ul/li/div/div')
        for div in div_list:
            title = div.xpath('./div/div[1]/h3/a/text()').extract_first()

            # title = div.xpath('./div/div/div[1]/h3/a/text()').extract_first()
            news_url = div.xpath('./div/div[1]/h3/a/@href').extract_first()
            item = WangyiItem()
		
		   #对得到结果进行去除空格换行
            item['title'] = title.replace('\n','')
            item['title'] = title.replace('\t','')
            item['title'] = title.replace(' ','')
            item['title'] = title.replace('\t\n','')


            yield scrapy.Request(news_url,callback=self.detail_parse,meta={'item':item})


    def detail_parse(self,response):
        item = response.meta['item']
        content = response.xpath('//*[@id="endText"]//text()').extract()
        content = ''.join(content)

        #对得到结果进行去除空格换行
        item['content'] = content.replace('\n','')
        item['content'] = content.replace('\t','')
        item['content'] = content.replace(' ','')
        item['content'] = content.replace('\t\n','')
        item['content'] = content.replace('                    \n','')


        yield item

下载中间件

from scrapy import signals
from scrapy.http import HtmlResponse
import time


class WangyiDownloaderMiddleware(object):

    def process_request(self, request, spider):

        return None

    def process_response(self, request, response, spider):

        if request.url in spider.un_url:
            spider.bro.get(request.url)
            time.sleep(3)
            spider.bro.execute_script('window.scrollTo(0,document.body.scrollHeight)')
            time.sleep(2)
            spider.bro.execute_script('window.scrollTo(0,document.body.scrollHeight)')
            time.sleep(2)
            page_text = spider.bro.page_source

            # 新的响应的构造方法
            new_response = HtmlResponse(url=request.url,body=page_text,encoding='utf-8',request=request)


            return new_response  #拦截响应后并篡改后,并重新发送新的响应对象,响应的构造方法
        else:
            return response

    def process_exception(self, request, exception, spider):

        pass



10.CrawlSpider(全站数据爬取)

创建一个基于CrawlSpider的爬虫文件:scrapy genspider -t crawl sun www.xxx.com

CrawlSpider使用

爬取阳光热线问政平台所有页码中的内容标题以及状态http://wz.sun0769.com/html/top/report.shtml

阳光问政平台共151711条记录,好几百页,实现这几百页的数据爬取
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor #链接提取器
from scrapy.spiders import CrawlSpider, Rule# rule规则解析器
# 链接提取器:提取链接,可以根据指定的规则进行指定的连链接的提取
#     提取规则:allow='正则表达式'
    

# 规则解析器:获取链接提取到的链接,然后对其进行请求发送,根据指定的规则(callback)对请求的页面源码数据进行数据解析

class SunSpider(CrawlSpider):
    name = 'sun'
    start_urls = ['http://wz.sun0769.com/index.php/question/report?page=']
    link = LinkExtractor(allow=r'page=\d+')
    rules = (
        # 实例化一个rule对象,基于linkextractor
        Rule(link, callback='parse_item', follow=True),
    )   # fllow=true  将链接提取器继续作用到链接提取器提取的页码链接所对应 的页面中.如果fllow=Flase,得到的页面数据只是当前页面的显示的页码数据
    

    def parse_item(self, response):
        tr_list = response.xpath('/html//div[8]/table[2]//tr')
        for tr in tr_list:
            title = tr.xpath('./td[3]/a[1]/@title').extract_first()
            status = tr.xpath('./td[4]/span/text()').extract_first()
            print(title,status)

基于CrawlSpider的深度爬取:爬取所有页码中的详情数据

sun.py

import scrapy
from scrapy.linkextractors import LinkExtractor #链接提取器
from scrapy.spiders import CrawlSpider, Rule# rule规则解析器
from sun_spider.items import SunSpiderItem,SunSpiderItem_second

class SunSpider(CrawlSpider):
    name = 'sun'
    start_urls = ['http://wz.sun0769.com/index.php/question/report?page=']
    # href="http://wz.sun0769.com/html/question/201912/437515.shtml"
    link = LinkExtractor(allow=r'page=\d+')
    link_detail = LinkExtractor(allow=r'question/\d+/\d+/.shtml')


    rules = (
        # 实例化一个rule对象,基于linkextractor
        Rule(link, callback='parse_item', follow=True),
        Rule(link_detail, callback='parse_detail'),
    )   # fllow=true  将链接提取器继续作用到链接提取器提取的页码链接所对应 的页面中

    def parse_item(self, response):
        tr_list = response.xpath('/html//div[8]/table[2]//tr')
        for tr in tr_list:
            title = tr.xpath('./td[3]/a[1]/@title').extract_first()
            status = tr.xpath('./td[4]/span/text()').extract_first()
            num = tr.xpath('./td[1]/text()').extract_first()
            item = SunSpiderItem_second()
            item['title'] = title
            item['status'] = status
            item['num'] = num #用于mysql数据库的条件存储,num就是存储的条件
            if  num:
                yield item


    def parse_detail(self,response):
        content = response.xpath('/html/body/div[9]/table[2]/tbody/tr[1]/td//text()').extract()
        num = response.xpath('/html/body/div[9]/table[1]/tbody/tr/td[2]/span[2]/text()').extract_first()
        num = num.split(':')[-1]
        if num:
            content = ''.join(content)
            item = SunSpiderItem()
            item['content'] = content
            item['num'] = num
            yield item

items.py

import scrapy


class SunSpiderItem(scrapy.Item):
    # define the fields for your item here like:
    content = scrapy.Field()
    num = scrapy.Field()
class SunSpiderItem_second(scrapy.Item):
    # define the fields for your item here like:
    title = scrapy.Field()
    status = scrapy.Field()
    num = scrapy.Field()

pipelines.py

# -*- coding: utf-8 -*-

# 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

import pymysql
class SunSpiderPipeline(object):
    conn = None
    cursor = None

    def open_spider(self, spider):
        self.conn = pymysql.Connect(host='127.0.0.1', port=3306, user='root', password='123', db='spider',
                                    charset='utf8')

    def process_item(self, item, spider):

        if item.__class__.__name__ == 'SunSpiderItem':
            content = item['content']
            num = item['num']
            sql = f'insert into spider_crawl_sun values {(content)} where num={num}'
            print(num)
            # sql = 'insert into spider_crawl_sun values ("%s") where num=%s'%(content,num)
            self.cursor = self.conn.cursor()
            try:
                self.cursor.execute(sql)  # 执行正确的话就提交事务
                self.conn.commit()
            except Exception as e:
                self.conn.rollback()
                return item

        elif item.__class__.__name__=='SunSpiderItem_second':
            title = item['title']
            status = item['status']
            num = item['num']
            sql = 'insert into spider_crawl_sun values ("%s","%s","%s")'%(title,status,num)
            print(sql)
            self.cursor = self.conn.cursor()
            try:
                self.cursor.execute(sql)  # 执行正确的话就提交事务
                self.conn.commit()
            except Exception as e:

                self.conn.rollback()
                return item

    def close_spider(self, spider):
        self.cursor.close()
        self.conn.close()


11.提升scrapy爬取数据的效率

需要将如下五个步骤配置在配置文件中即可
增加并发:
    默认scrapy开启的并发线程为32个,可以适当进行增加。在settings配置文件中修改CONCURRENT_REQUESTS = 100值为100,并发设置成了为100。

降低日志级别:
    在运行scrapy时,会有大量日志信息的输出,为了减少CPU的使用率。可以设置log输出信息为INFO或者ERROR即可。在配置文件中编写:LOG_LEVEL = ‘INFO’

禁止cookie:
    如果不是真的需要cookie,则在scrapy爬取数据时可以禁止cookie从而减少CPU的使用率,提升爬取效率。在配置文件中编写:COOKIES_ENABLED = False

禁止重试:
    对失败的HTTP进行重新请求(重试)会减慢爬取速度,因此可以禁止重试。在配置文件中编写:RETRY_ENABLED = False

减少下载超时:
    如果对一个非常慢的链接进行爬取,减少下载超时可以能让卡住的链接快速被放弃,从而提升效率。在配置文件中进行编写:DOWNLOAD_TIMEOUT = 10 超时时间为10s

本文仅做项目练习,切勿商用!

由于文章篇幅有限,文档资料内容较多,需要这些文档的朋友,可以加小助手微信免费获取,【保证100%免费】,中国人不骗中国人。
请添加图片描述
今天就分享到这里,感谢大家收看。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

模拟物理弧线轨道运动(模拟飞盘,子弹运动)

模拟物理弧线运动&#xff08;模拟飞盘&#xff09; 介绍实现代码总结 介绍 模拟弧线的运动&#xff0c;并且对象始终朝向运动的方向&#xff0c;模拟飞盘子弹的运动轨迹。这里我是没有加重力这么一个概念的&#xff0c;当然了重力其实比较简单可以参考我之前写的模拟抛物线运动…

2024十大首码地推拉新app平台,一手首码对接平台!

到了2024年&#xff0c;地推新应用的接单平台成为创业者们关注的焦点。对于地推行业的从业人员而言&#xff0c;选择一家拥有一手单资源的平台至关重要&#xff0c;因为这直接关系到他们的利益。 2024年如果想要进行app地推活动&#xff0c;却没有人脉渠道的困扰&#xff0c;建…

ABB机器人控制柜各模块指示灯状态说明

ABB机器人控制柜各模块指示灯状态说明 主计算机模块位于控制柜的正前方,负责机器人的各种运算处理,安全模块主要负责安全相关的信号处理,驱动单元模块用于接收上位机指令,驱动机器人运动,轴计算机模块用于接收主计算机的运动指令和串

VMware的具体使用

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 目录 一&#x1f324;️VMware的安…

直播怎么录制视频?直播视频,3种录制方法

“今晚我最喜欢的游戏博主要进行直播&#xff0c;但我可能还要加班。怎么办&#xff0c;不想错过直播的内容&#xff01;电脑怎么才能进行直播录制视频啊&#xff1f;谁能教教我&#xff1f;” 在数字化的今天&#xff0c;直播已经成为人们获取信息和娱乐的重要途径。有时&…

无线麦克风推荐哪些品牌,一文揭秘无线麦克风领夹哪个牌子好!

​究竟该如何选择麦克风呢&#xff1f;又该如何挑选无线麦克呢&#xff1f;询问我关于麦克风选择问题的人着实不少。对于那些仅仅是想要简单地自我娱乐的朋友而言&#xff0c;着实没必要去折腾&#xff0c;直接使用手机自带的麦克风便可以了。 但若是处于想要直播、拍摄短视频…

文本分类-RNN-LSTM

1.前言 本节介绍RNN和LSTM&#xff0c;并采用它们在电影评论数据集上实现文本分类&#xff0c;会涉及以下几个知识点。 1. 词表构建&#xff1a;包括数据清洗&#xff0c;词频统计&#xff0c;词频截断&#xff0c;词表构建。 2. 预训练词向量应用&#xff1a;下载并加载Glove的…

端到端图像分类算法开发实战:从 Arm 虚拟硬件到 Grove Vision AI Module V2 物理硬件

端到端图像分类算法开发实战&#xff1a;从 Arm 虚拟硬件到 Grove Vision AI Module V2 物理硬件 文章目录 1. 写在前面2. 产品简介2.1 Arm 虚拟硬件镜像产品简介2.2 Grove - Vision AI V2 产品简介 3. 实验前准备4. 实验步骤4.1 模型训练4.2 Arm 虚拟硬件镜像上的部署测试4.2…

【HarmonyOS NEXT】har 包的构建生成过程

Har模块文件结构 构建HAR 打包规则 开源HAR除了默认不需要打包的文件&#xff08;build、node_modules、oh_modules、.cxx、.previewer、.hvigor、.gitignore、.ohpmignore&#xff09;和.gitignore/.ohpmignore中配置的文件&#xff0c;cpp工程的CMakeLists.txt&#xff0c;…

【Python机器学习】自动化特征选择——迭代特征选择

在单变量测试中&#xff0c;没有使用模型&#xff1b;在基于模型的选择中&#xff0c;使用单个模型来选择特征。而在迭代特征选择中&#xff0c;将会构造一系列模型&#xff0c;每个模型都使用不同数量的特征。有两种基本方法&#xff1a; 1、开始时没有特征&#xff0c;然后逐…

【MySQL基础篇】概述及SQL指令:DDL及DML

数据库是一个按照数据结构来组织、存储和管理数据的仓库。以下是对数据库概念的详细解释&#xff1a;定义与基本概念&#xff1a; 数据库是长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集合。 数据库不仅仅是数据的简单堆积&#xff0c;而是遵循一定的规则…

聚合项目学习

首先建立一个总的工程目录&#xff0c;里边后期会有我们的父工程、基础工程(继承父工程)、业务工程&#xff08;依赖基础工程&#xff09;等模块 1、在总工程目录中&#xff08;open一个空的文件夹&#xff09;&#xff0c;首先建立一个父工程模块&#xff08;通过spring init…

Unity中模拟抛物线(非Unity物理)

Unity中模拟抛物线非Unity物理 介绍剖析问题以及所需公式重力加速度公式&#xff1a;h 1/2*g*t*t(h 1/2 * g * t ^ 2)速度公式&#xff1a;Vt V初 a * t 主要代码总结 介绍 用Unity物理系统去做的抛物线想要控制速度或者想要细微的控制一些情况是非常困难的。所以想要脱离U…

【Linux系列】Fedora40安装VMware Workstation Pro报错

问题描述 由于Fedora 40使用的Linux内核是6.9,导致安装VMware Workstation Pro 时,安装依赖无法成功,具体报错如下 ..................CC [M] /tmp/modconfig-a8Fcf5/vmnet-only/smac.oCC [M] /tmp/modconfig-a8Fcf5/vmnet-only/vnetEvent.oCC [M] /tmp/modconfig-a8Fcf…

《数据勒索防范手册(1.0版)》

当前&#xff0c;数据勒索攻击已成为全球最严重的数据安全威胁之一攻击方式呈现 APT 化、平台化、多重化、AI驱动化等发展趋势:据统计&#xff0c;近年来针对制造业、公共事业、卫生保健、电力、交通、能源等领域的勒索攻击显著增加。随着云计算、边缘计算等技术的不断发展&…

深入探究小型语言模型 (SLM)

使用 Microsoft Bing Image Creator 创建 大型语言模型 (LLM) 已经流行了一段时间。最近&#xff0c;小型语言模型 (SLM) 增强了我们处理和使用各种自然语言和编程语言的能力。但是&#xff0c;一些用户查询需要比在通用语言上训练的模型所能提供的更高的准确性和领域知识。此外…

大疆车载的第一款油车智驾:上汽大众途观L Pro的智能辅助驾驶系统

引言 在自驾行业中&#xff0c;有一个低调但迅速崭露头角的选手——大疆车载。自2016年成立以来&#xff0c;大疆车载&#xff08;现已更名为卓御&#xff09;通过其先进的智能驾驶技术&#xff0c;逐渐在市场上赢得了声誉。此次&#xff0c;上汽大众途观L Pro成为大疆车载首款…

如何科学减肥先从了解自己在到饮食运动

在这个以瘦为美的时代&#xff0c;许多人被肥胖所困扰着&#xff0c; 今天就来教大家如何科学减脂。 一、如何判断自己是否需要减脂&#xff1f; 第一步就是判断自己的体重指数&#xff08;BMI&#xff09;是否在正常标准。BMI是国际上衡量人体胖瘦程度及是否健康的一个常用指…

打破生态「孤岛」,Catizen将开启Telegram小游戏2.0时代?

Catizen&#xff1a;引领Telegram x TON生态的顶级猫咪链游 在区块链游戏领域&#xff0c;吸引玩家的首要因素往往是游戏的趣味性。然而&#xff0c;仅靠趣味性无法评估一个项目的长期价值和发展潜力。真正能在区块链游戏市场中取得长久成功的项目&#xff0c;无一例外都依靠扎…

软件自动化测试有哪些流程?可替代手工测试吗?

随着科技的不断发展&#xff0c;软件在我们生活中的地位越来越重要。然而&#xff0c;在软件开发过程中&#xff0c;必然会出现各种各样的问题和bug&#xff0c;为了提高软件的质量和稳定性&#xff0c;保证用户的使用体验&#xff0c;软件自动化测试应运而生。 那么&#xff…