day9-scrapy框架

一、什么是 scrap?

爬虫中封装好的一个明星框架。
功能:高性能的持久化存储,异步的数据下载,高性能的数据解析,分布式

二、scrap框架的基本使用

  1. 环境的安装
  • mac or Linux: pip install scrap
  • windows
    • pip install wheel
    • 下载twisted:下載地址为http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
    • 安装twisted:pip install Twisted-17.1.0-cp36-cp36m-win amd64 whl
    • pip install pywin32
    • pip install scrapy
      测试:在终端里录入 scrap指令,没有报错即表示安装成功!
  1. 创建一个工程:
scrapy startproject   xxxpro(文件名)
cd xxxpro

在spiders子目录中创建一个爬虫文件

scrapy genspider spiderName wwww.xxx.com

执行工程

scrapy crawl spiderName

三、scrapy数据解析

实例:以爬取糗事百科的段子为例
1、先创建项目
在这里插入图片描述
在这里插入图片描述
2、创建爬虫文件
在这里插入图片描述

在这里插入图片描述

爬虫文件的代码如下:

import scrapy


class QiushiSpider(scrapy.Spider):
    name = 'qiushi'
    # allowed_domains = ['https://www.qiushibaike.com/text/']
    start_urls = ['https://www.qiushibaike.com/text/']

    def parse(self, response):
        #解析:作者的名称+段子内容
        div_list = response.xpath('//*[@class="col1 old-style-col1"]/div')
        for div in div_list:
            #xpath返回的是列表,但是列表元素一定是select类型的对象
            #extract可以将select对象中的data参数存储的字符串提取出来
            author = div.xpath('./div[1]/a[2]/h2/text()')[0].extract()
            #列表调用了extract之后,则表示将列表中的每一个select对象中的data对应的字符串提取了出来
            content = div.xpath('./a[1]/div/span//text()').extract()
            content = ''.join(content)

            print(author,content)
            break

运行代码前修改settings.py 文件
在这里插入图片描述
运行代码
在这里插入图片描述

四、scrapy 持久化存储

  1. 基于终端指令:
    • 要点:只可以将parse方法返回的值存储到本地的文本之中
    • 注意:持久化存储对应的文本文件的类型只可以为:(‘json’, ‘jsonlines’, ‘jl’, ‘csv’, ‘xml’, ‘marshal’, ‘pickle’)
    • 指令:scrapy crawl xxx(爬虫文件名) -o filename(存储文件的名称)
    • 好久:简介高效便捷
    • 缺点:局限性比较强(数据只可以存储到指定后缀的文本文件中)
      爬虫文件代码:
import scrapy


class QiushiSpider(scrapy.Spider):
    name = 'qiushi'
    # allowed_domains = ['https://www.qiushibaike.com/text/']
    start_urls = ['https://www.qiushibaike.com/text/']

    def parse(self, response):
        #解析:作者的名称+段子内容
        div_list = response.xpath('//*[@class="col1 old-style-col1"]/div')
        all_data = []
        for div in div_list:
            #xpath返回的是列表,但是列表元素一定是select类型的对象
            #extract可以将select对象中的data参数存储的字符串提取出来
            author = div.xpath('./div[1]/a[2]/h2/text()')[0].extract()
            #列表调用了extract之后,则表示将列表中的每一个select对象中的data对应的字符串提取了出来
            content = div.xpath('./a[1]/div/span//text()').extract()
            content = ''.join(content)

            dic = {
                'author':author,
                'content':content
            }
            all_data.append(dic)
            return all_data
  1. 基于管道:
    • 编码流程:
      • 数据解析

      • 在item类中定义相关的属性

      • 将解析的数据封装存储到item类型的对象
        在这里插入图片描述

      • 将item类型的对象提交给管道进行持久化存储的操作
        在这里插入图片描述

      • 在管道类的process_item中要将其接受到item对象中存储的数据进行持久化存储操作
        在这里插入图片描述

      • 在配置文件中开启管道
        在这里插入图片描述

    • 好处
      -通用性强

五、全站数爬取

 以爬取校花网所有的图片
 爬虫的代码如下(settings里面的设置和上面一样):
import scrapy


class XhSpider(scrapy.Spider):
    name = 'xh'
    # allowed_domains = ['www.xx.com']
    start_urls = ['http://www.521609.com/meinvxiaohua/list122.html']

    #生成一个通用的url模板
    url = 'http://www.521609.com/meinvxiaohua/list12%d.html'
    page_num = 2
    def parse(self, response):
        li_list = response.xpath('//*[@id="content"]/div[2]/div[2]/ul/li')
        for li in li_list:
            image_name = li.xpath('./a[2]/text() | ./a[2]/b/text()').extract_first()
            print(image_name)

        if self.page_num <11:
            self.page_num += 1
            new_url = format(self.url%self.page_num)
            #手动发送请求:callback回调函数作用于数据解析
            yield scrapy.Request(url=new_url,callback=self.parse)

回调函数:
在这里插入图片描述
在Python中,已经没有指针这个说法了,一般都是说函数名。简单来说就是定义一个函数,然后将这个函数的函数名传递给另一个函数做参数,以这个参数命名的函数就是回调函数。

def my_callbcak(args):
    print(*args)

def caller(args, func):
    func(args)

caller((1,2), my_callbcak)

结果:
# 1 2

其中:my_callback是回调函数,因为它作为参数传递给了caller

六、五大核心组件

在这里插入图片描述

  1. 请求传参—以爬取boss职称和简介为例

爬虫文件下的代码:

import scrapy
from bosspro.items import BossproItem

class BossSpider(scrapy.Spider):
    name = 'boss'
    # allowed_domains = ['www.xxx.com']
    start_urls = ['https://www.zhipin.com/job_detail/?query=python&city=101280600&industry=&position=']

    url = 'https://www.zhipin.com/c101280600/?query=python&page=%d'
    page_nem = 2

    #回调函数
    def parse_deital(self,response):
        item = response.meta['item']
        job_desc = response.xpath('//*[@id="main"]/div/div[3]/div/div[2]/div[2]/div[1]/div//text()').extract_first()
        job_desc = ''.join(job_desc)
        item['job_desc'] = job_desc

    def parse(self, response):

        li_list = response.xpath('//*[@id="main"]/div/div[3]/ul/li')
        for li in li_list:
            item = BossproItem()
            job_name = li.xpath('.//div[@class="info-primary"]/h3/a/div[1]/text()').extract_first()
            item['job_name'] = job_name
            detail_url = li.xpath('.//div[@class="info-primary"]/h3/a/@href').extract_first()
            # 对详情页发起请求,获取页面源码数据
            # 手动请求的发送
            #请求参数:meta={},可以将meta字典传递给请求对应的回调函数
            yield scrapy.Request(detail_url,callback=self.parse_deital,meta={'item':item})


        #进行分页处理
        if self.page_nem <= 3:
            new_url = format(self.url%self.page_nem)
            self.page_nem += 1
            yield scrapy.Request(new_url,callback=self.parse)
  1. scrapy爬取图片(懒加载)
    • 使用ImagesPipeline:只需要将img的src的属性值进行解析,提交到管道,管道就会对图片的src进行请求发送获取图片的二进制
    • 需求:爬取站在素材中的高清图片
    • 使用流程
      • 数据解析(图片的地址)
      • 将存储图片地址的item提交到指定的管道类
      • 在管道文件中自定制一个基于ImagePipeline的一个管道类,重写以下方法
        • get _meia_request
        • file_path
        • item_completed
      • 在配置文件中
        • 指定图片存储的目录:IMAGES_STORE = ‘./imgs_cc’ (自定义)
        • 指定开启的管道:自定制的管道类

爬虫文件代码:

import scrapy
from imgpro.items import ImgproItem

class ImgSpider(scrapy.Spider):
    name = 'img'
    # allowed_domains = ['www.xx.com']
    start_urls = ['https://sc.chinaz.com/tupian/']

    def parse(self, response):
        img_list = response.xpath('//*[@id="container"]/div')
        for img in img_list:
            #使用伪属性,因为图片是懒加载的,图片加载出来后才是src,加载前是src2(只针对此需求而言)
            src ='https:' + img.xpath('./div/a/img/@src2').extract_first()
            item = ImgproItem()
            item['src'] = src
            yield item

items下的代码:

# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html

import scrapy


class ImgproItem(scrapy.Item):
    # define the fields for your item here like:
    src = scrapy.Field()
    pass

pipeline下的代码:

from scrapy.pipelines.images import ImagesPipeline
import scrapy

class ImgsPipeline(ImagesPipeline):

    #根据图片地址进行图片数据的请求
    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]
        return img_name

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

settings下的代码:


BOT_NAME = 'imgpro'

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

LOG_LEVEL = 'ERROR'

USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)' \
             ' Chrome/89.0.4389.90 Safari/537.36'

ROBOTSTXT_OBEY = False

ITEM_PIPELINES = {
   'imgpro.pipelines.ImgsPipeline': 300,
}

#指定图片存储的路径
IMAGES_STORE = './img_cc'

六、中间件

  1. 拦截响应
    需求:爬取网易新闻中的新闻数据(标题和内容)
    爬虫代码:
import scrapy
from selenium import webdriver
from middle.items import MiddleItem



class MidSpider(scrapy.Spider):
    name = 'mid'
    # allowed_domains = ['www.xx.com']
    start_urls = ['https://news.163.com/']


    model_urls = []
    #实例化一个浏览器对象
    def __init__(self):
        self.bro = webdriver.Chrome()

    def parse(self, response):
        li_list = response.xpath('//*[@id="index2016_wrap"]/div[1]/div[2]/div[2]/div[2]/div[2]/div/ul/li')
        alist = [3,4]  #自己要爬取的模块对应的坐标值
        for index in alist:
            model_url= li_list[index].xpath('./a/@href').extract_first()   #获取每个版本的url地址
            self.model_urls.append(model_url)

        #依次对每一个板块对应页面进行请求
        for url in self.model_urls:
            yield scrapy.Request(url,callback=self.parse_model)

    #解析每一个板块对应的新闻的标题和新闻详情页的url
    def parse_model(self,response):
        div_list = response.xpath('/html/body/div/div[3]/div[4]/div[1]/div[1]/div/ul/li/div/div')
        for div in div_list:
            title = div.xpath('./div/div[1]/h3/a/text()').extract_first()
            new_detail_url = div.xpath('./div/div[1]/h3/a/@href').extract_first()

            item = MiddleItem()
            item['title'] = title

            #对新闻详情页的url发起请求
            yield scrapy.Request(url=new_detail_url,callback=self.parse_detail,meta={'item':item})

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

    def closed(self,spider):
        self.bro.quit()

items代码:

import scrapy


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

middlewares代码:



from scrapy import signals
from itemadapter import is_item, ItemAdapter
import random
from scrapy.http import HtmlResponse
import time


class MiddleDownloaderMiddleware:
    #拦截请求
    def process_request(self, request, spider):
        #UA伪装
       # request.headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)' \
       #      ' Chrome/89.0.4389.90 Safari/537.36'

    #拦截响应
    def process_response(self, request, response, spider):  #spider爬虫对象
        bro = spider.bro  # 获取在爬虫类中定义的浏览器对象

        #挑选出指定的响应对象进行篡改
        #通过指定url指定request
        #通过request指定response
        if request.url in spider.model_urls:
            bro.get(request.url)  #对指定模块进行请求
            time.sleep(2)
            page_text = bro.page_source  #包含了动态加载的新闻数据

            #response 指定请求的响应对象
            #针对定位到的response进行篡改
            #实例化一个新的响应对象,替换原来旧的响应对象
            new_reponse = HtmlResponse(url=request.url,body=page_text,encoding='utf-8',request=request)
            return new_reponse
        else:
            #其他请求的响应对象
            return response

    #拦截发生异常的请求
    def process_exception(self, request, exception, spider):
        # 为了验证代理的操作是否生效
        #request.meta['proxy'] = '123.156.182.33:8888'


pipeline代码:

class MiddlePipeline:
    def process_item(self, item, spider):
        print()
        return item

settings代码:


BOT_NAME = 'middle'
SPIDER_MODULES = ['middle.spiders']
NEWSPIDER_MODULE = 'middle.spiders'

ROBOTSTXT_OBEY = False

LOG_LEVEL = 'ERROR'

DOWNLOADER_MIDDLEWARES = {
   'middle.middlewares.MiddleDownloaderMiddleware': 543,
}

ITEM_PIPELINES = {
   'middle.pipelines.MiddlePipeline': 300,
}

七、CrawlSpider类

是属于Spider的一个子类

  1. CrawlSpider的使用

    • 创建一个工程
    • cd xx(工程目录)
    • 创建爬虫文件:scrapy genspider -t crawl xxx(爬虫文件名) www.xxx.com
    • 链接提取器:根据指定规则(allow=“正则”)进行指定链接的提取
    • 规则解析器:将链接提取器提取到的链接进行指定规则(callback) 的解析操作
    • follow=True:可以将链接提取器 继续作用到 链接提取器提取到的链接 所对应的页面中(例如:网站页面默认只显示5个页码,当为False的时候就只能提取到5个页码的链接信息,当为True的时候,就可以提取到所有页码的链接信息)
  2. 实例:爬取阳光热线的标题,内容和编号
    爬虫文件代码:

import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from yg.items import YgItem,YgDetail

class YgcSpider(CrawlSpider):
    name = 'ygc'
    # allowed_domains = ['www.xxx.com']
    start_urls = ['http://wz.sun0769.com/political/index/supervise?page=']

    link_detail = LinkExtractor(allow=r'id=\d+')

    # (LinkExtractor)链接提取器:根据指定规则(allow="正则")进行指定链接的提取
    # (rules)规则解析器:将链接提取器提取到的链接进行指定规则
    # follow=True:可以将链接提取器 继续作用到 链接提取器提取到的链接 所对应的页面中
    rules = (
        Rule(LinkExtractor(allow=r'page=\d+'), callback='parse_item', follow=True),
        Rule(link_detail,callback='parse_detail')
    )

    #解析新闻编号和新闻标题
    #如下两个解析方法中是不可以实现请求传参的
    #如法将两个解析的数据存储到同一个item中,可以以此存储到两个item
    def parse_item(self, response):
        #注意:xpath表达式中不可以出现tbody标签
        li_list = response.xpath('/html/body/div[2]/div[3]/ul/li')
        for li in li_list:
            new_num = li.xpath('./span[1]/text()').extract_first()
            new_title = li.xpath('./span[3]/a/text()').extract_first()
            item = YgItem()
            item['new_num'] = new_num
            item['new_title'] = new_title
            yield item


    #解析新闻内容和新闻编号
    def parse_detail(self,response):
        new_id = response.xpath('/html/body/div[3]/div[2]/div[2]/div[1]/span[4]/text()').extract_first()
        content= response.xpath('/html/body/div[3]/div[2]/div[2]/div[2]/pre/text()').extract_first()
        item = YgDetail()
        item['new_id'] = new_id
        item['content'] = content
        yield item

items代码:

# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html

import scrapy


class YgItem(scrapy.Item):
    # define the fields for your item here like:
    new_num = scrapy.Field()
    new_title = scrapy.Field()

class YgDetail(scrapy.Item):
    new_id = scrapy.Field()
    content = scrapy.Field()

pipeline代码:

from itemadapter import ItemAdapter


class YgPipeline:
    def process_item(self, item, spider):
        if item.__class__.__name__ == 'YgItem':
            print(item['new_num'],item['new_title'])
        else:
            print(item['new_id'],item['content'])
        return item

settings代码:

BOT_NAME = 'yg'

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

USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)' \
             ' Chrome/89.0.4389.90 Safari/537.36'

ROBOTSTXT_OBEY = False
LOG_LEVEL = 'ERROR'


ITEM_PIPELINES = {
   'yg.pipelines.YgPipeline': 300,
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值