爬虫学习笔记12-scrapy框架构造发送请求和管道的使用

1、 翻页请求的思路

(1)request请求的思路
找到下一页的URl——调用request.get(url)
(2)scrapy实现翻页
找到下一页的URL——构造URL地址的请求对象——传递给引擎

2、构造request对象并发送请求

(1)实现步骤
①确定url地址
②构造请求,scrapy.Request(url,callback)
③把请求交给引擎:yield scrapy.Request(url,callback)

callback:指定解析函数名称,表示该请求返回的响应使用哪一个函数进行解析

(2)案例实现:网易招聘爬虫(爬取网易招聘的页面的招聘信息)
地址:https://hr.163.com/position/list.do
①创建项目:scrapy startproject wangyi
②数据建模:items.py

import scrapy

class WangyiItem(scrapy.Item):
    # define the fields for your item here like:
    name = scrapy.Field()  # 职位名称
    link = scrapy.Field()  # 职位详情
    depart = scrapy.Field()  # 所属部门
    category = scrapy.Field()  # 职位类别
    type = scrapy.Field()  # 工作类型
    address = scrapy.Field()  # 工作地点
    num = scrapy.Field()  # 招聘人数
    date = scrapy.Field()  # 发布时间

③完成爬虫
创建爬虫项目:

cd wangyi
scrapy genspider job 163.com

编辑爬虫

import scrapy
# 把建模的数据导入进来
from wangyi.items import WangyiItem


class JobSpider(scrapy.Spider):
    name = 'job'
    # 2. 检查域名
    allowed_domains = ['163.com']
    # 1. 修改起始url
    start_urls = ['https://hr.163.com/position/list.do']

    # 3. 在parse方法中实现爬取逻辑
    def parse(self, response):
        # 1.提取数据
        # ①提取所有职位节点列表
        node_list = response.xpath('//*[@class="position-tb"]/tbody/tr')
        # ②遍历职位节点列表
        for num,node in enumerate(node_list):
            # 设置过滤,将目标节点获取出来
            if num % 2 == 0:
                item = WangyiItem()
                item['name'] = node.xpath('./td[1]/a/text()').extract_first()
                # response.urljoin()用于拼接相对路径的URL
                item['link'] = response.urljoin(node.xpath('./td[1]/a/@href').extract_first())
                item['depart'] = node.xpath('./td[2]/text()').extract_first()
                item['category'] = node.xpath('./td[3]/text()').extract_first()
                item['type'] = node.xpath('./td[4]/text()').extract_first()
                item['address'] = node.xpath('./td[5]/text()').extract_first()
                # strip() 消除空格
                item['num'] = node.xpath('./td[6]/text()').extract_first().strip()
                item['date'] = node.xpath('./td[7]/text()').extract_first()
                yield item


        # 2.模拟翻页
        part_url = response.xpath('/html/body/div[2]/div[2]/div[2]/div/a[last()]/@href').extract_first()
        # 判断终止条件
        if part_url !='javascript:void(0)':
            next_url = response.urljoin(part_url)
            # 构造请求对象,并且返回给引擎
            yield scrapy.Request(url=next_url,callback=self.parse)

④保存数据
./pipelines

import json

class WangyiPipeline:
    def __init__(self):
        self.file = open('Amen.json','w')

    def process_item(self, item, spider):
        item = dict(item)
        # 将字典数据序列化成字符串
        str_data = json.dumps(item,ensure_ascii=False)+',\n'
        # 将数据写入文件
        self.file.write(str_data)

        return item

    def __del__(self):
        self.file.close()

./setting

ITEM_PIPELINES = {
   'wangyi.pipelines.WangyiPipeline': 300,
}

⑤运行程序:scrapy crawl job
注意:

  • 可以在settings中设置ROBOTS协议
# False表示忽略网站的robots.txt协议,默认为True
ROBOTSTXT_OBEY = False
  • 可以在settings中设置User-Agent:
# scrapy发送的每一个请求的默认UA都是设置的这个User-Agent
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36'

在这里插入图片描述

3、scrapy.Request()的其他参数

scrapy.Request(url[,callback,method="GET",headers,body,cookies,meta,dont_filter=False])

参数解释

  1. 中括号里的参数为可选参数
  2. callback:表示当前的url的响应交给哪个函数去处理
  3. meta:实现数据在不同的解析函数中传递,meta默认带有部分数据,比如下载延迟,请求深度等
  4. dont_filter:默认为False,会过滤请求的url地址,即请求过的url地址不会继续被请求,对需要重复请求的url地址可以把它设置为Ture,比如贴吧的翻页请求,页面的数据总是在变化;start_urls中的地址会被反复请求,否则程序不会启动
  5. method:指定POST或GET请求
  6. headers:接收一个字典,其中不包括cookies
  7. cookies:接收一个字典,专门放置cookies
  8. body:接收json字符串,为POST的数据,发送payload_post请求时使用
  • meta参数的使用
    meta的作用:meta可以实现数据在不同的解析函数中的传递
    在爬虫文件的parse方法中,提取详情页增加之前callback指定的parse_detail函数:
	def parse(self,response):
	    ...
	    yield scrapy.Request(detail_url, callback=self.parse_detail,meta={"item":item})
	...
	
	def parse_detail(self,response):
	    #获取之前传入的item
	    item = resposne.meta["item"]
  1. meta参数是一个字典
  2. meta字典中有一个固定的键proxy,表示代理ip,关于代理ip的使用我们将在scrapy的下载中间件的学习中进行介绍

案例解析:对前面的网易招聘进行完善详情页面的爬取拼接数据
①建模:

class WangyiItem(scrapy.Item):
   ……
   duty = scrapy.Field()  # 岗位描述
   require = scrapy.Field()  # 岗位要求

②完善爬取

class JobSpider(scrapy.Spider):
	def parse(self, response):
	        ……
	        for num,node in enumerate(node_list):
	            # 设置过滤,将目标节点获取出来
	            if num % 2 == 0:
	                ……
	                # yield item
	                # 构建详情页面的请求
	                yield scrapy.Request(url=item['link'],callback=self.parse_detail,meta={'item':item})
	……
	def parse_detail(self):
	            item = response.meta['item']
	            item['duty'] = response.xpath('/html/body/div[2]/div[2]/div[1]/div/div/div[2]/div[1]/div/text()').extract()
	            item['require'] = response.xpath('/html/body/div[2]/div[2]/div[1]/div/div/div[2]/div[2]/div/text()').extract()
	            yield item

在这里插入图片描述

4、管道的使用

(1)管道中常用的方法
①process_item(self,item,spider):

  • 管道类中必须有的函数
  • 实现对item数据的处理
  • 必须return item

②open_spider(self, spider): 在爬虫开启的时候仅执行一次
③close_spider(self, spider): 在爬虫关闭的时候仅执行一次
(2)三个方法中的spider参数的作用:用于判断执行哪个爬虫执行哪个管道
(3)案例分析:网易招聘
①重新建一个数据模

class WangyiItem2(scrapy.Item):
    # define the fields for your item here like:
    name = scrapy.Field()  # 职位名称
    link = scrapy.Field()  # 职位详情
    depart = scrapy.Field()  # 所属部门
    category = scrapy.Field()  # 职位类别
    type = scrapy.Field()  # 工作类型
    address = scrapy.Field()  # 工作地点
    num = scrapy.Field()  # 招聘人数
    date = scrapy.Field()  # 发布时间

②新建一个网易爬虫job2,没有详情页面的情况:

import scrapy
# 导入新建的数据模
from wangyi.items import WangyiItem2

class Job2Spider(scrapy.Spider):
    name = 'job2'
    # 2. 检查域名
    allowed_domains = ['163.com']
    # 1. 修改起始url
    start_urls = ['https://hr.163.com/position/list.do']

    # 3. 在parse方法中实现爬取逻辑
    def parse(self, response):
        # 1.提取数据
        # ①提取所有职位节点列表
        node_list = response.xpath('//*[@class="position-tb"]/tbody/tr')
        # ②遍历职位节点列表
        for num, node in enumerate(node_list):
            # 设置过滤,将目标节点获取出来
            if num % 2 == 0:
                item = WangyiItem2()
                item['name'] = node.xpath('./td[1]/a/text()').extract_first()
                # response.urljoin()用于拼接相对路径的URL
                item['link'] = response.urljoin(node.xpath('./td[1]/a/@href').extract_first())
                item['depart'] = node.xpath('./td[2]/text()').extract_first()
                item['category'] = node.xpath('./td[3]/text()').extract_first()
                item['type'] = node.xpath('./td[4]/text()').extract_first()
                item['address'] = node.xpath('./td[5]/text()').extract_first()
                # strip() 消除空格
                item['num'] = node.xpath('./td[6]/text()').extract_first().strip()
                item['date'] = node.xpath('./td[7]/text()').extract_first()
                yield item


        # 2.模拟翻页
        part_url = response.xpath('/html/body/div[2]/div[2]/div[2]/div/a[last()]/@href').extract_first()
        # 判断终止条件
        if part_url != 'javascript:void(0)':
            next_url = response.urljoin(part_url)
            # 构造请求对象,并且返回给引擎
            yield scrapy.Request(url=next_url, callback=self.parse)

③现在存在两个爬虫,但是管道保存的通道只有一个,进行判断管道保存数据

class WangyiPipeline:
    def open_spider(self,spider):
        if spider.name == 'job':
            self.file = open('Amen.json','w')

    def process_item(self, item, spider):
        if spider.name == 'job':
            item = dict(item)
            # 将字典数据序列化成字符串
            str_data = json.dumps(item,ensure_ascii=False)+',\n'
            # 将数据写入文件
            self.file.write(str_data)

        return item

    def close_spider(self,spider):
        if spider.name == 'job':
            self.file.close()


class Wangyi2Pipeline:
    def open_spider(self,spider):
        if spider.name == 'job2':
            self.file = open('Amen2.json','w')

    def process_item(self, item, spider):
        if spider.name == 'job2':
            item = dict(item)
            # 将字典数据序列化成字符串
            str_data = json.dumps(item,ensure_ascii=False)+',\n'
            # 将数据写入文件
            self.file.write(str_data)

        return item

    def close_spider(self,spider):
        if spider.name == 'job2':
            self.file.close()

④在setting中开启管道

ITEM_PIPELINES = {
   'wangyi.pipelines.WangyiPipeline': 300,
   'wangyi.pipelines.Wangyi2Pipeline': 301,
   }

(4)管道数据保存到数据库
①创建一个数据保存管道,连接上MongoDB数据库

class MongoPipeline:
    def open_spider(self,spider):
        self.client = MongoClient('127.0.0.1', 27017)
        self.db = self.client['wangyidata']
        self.col = self.db['wangyi']

    def process_item(self, item, spider):
        data = dict(item)
        self.col.insert(data)

        return item

    def close_spider(self,spider):
        self.client.close()

②开启管道

ITEM_PIPELINES = {
  	……
   'wangyi.pipelines.MongoPipeline': 302,
}

在这里插入图片描述
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值