数据收集-scrapy爬虫框架(一)

scrapy的概念和流程

Scrapy是一个Python编写的开源网络爬虫框架。它是一个被设计用于爬取网络数据、提取结构性数据的框架。
scrapy文档

  • 作用:少量的代码,就能够快速的抓取

爬虫流程
在这里插入图片描述

scrapy的流程

在这里插入图片描述
流程描述:

  1. 爬虫中起始的url构造成request对象–>爬虫中间件–>引擎–>调度器
  2. 调度器把request–>引擎–>下载中间件—>下载器
  3. 下载器发送请求,获取response响应---->下载中间件---->引擎—>爬虫中间件—>爬虫
  4. 爬虫提取url地址,组装成request对象---->爬虫中间件—>引擎—>调度器,重复步骤2
  5. 爬虫提取数据—>引擎—>管道处理和保存数据

内置对象

  • request请求对象:由url method post_data headers等构成
  • response响应对象:由url body status headers等构成
  • item数据对象:本质是个字典

模块的具体作用

在这里插入图片描述
scrapy中每个模块的作用:
引擎(engine):负责数据和信号在不腰痛模块间的传递
调度器(scheduler):实现一个队列,存放引擎发过来的request请求对象
下载器(downloader):发送引擎发过来的request请求,获取响应,并将响应交给引擎
爬虫(spider):处理引擎发过来的response,提取数据,提取url,并交给引擎
管道(pipeline):处理引擎传递过来的数据,比如存储
下载中间件(downloader middleware):可以自定义的下载扩展,比如设置代理ip
爬虫中间件(spider middleware):可以自定义request请求和进行response过滤,与下载中间件作用重复

初步使用

  • 安装scrapy
    命令:sudo apt-get install scrapy 或:pip/pip3 install scrapy
  • scrapy项目开发流程
  1. 创建项目:
    scrapy startproject mySpider
  2. 生成一个爬虫:
    scrapy genspider baidu baidu.com
  3. 提取数据:
    根据网站结构在spider中实现数据采集相关内容
  4. 保存数据:
    使用pipeline进行数据后续处理和保存

创建项目

  • 创建scrapy项目的命令:
    scrapy startproject <项目名字>
    示例:scrapy startproject myspider

生成的目录和文件结果如下:
在这里插入图片描述

创建爬虫

  • 命令:
    在项目路径下执行:
    scrapy genspider <爬虫名字> <允许爬取的域名>
  • 爬虫名字: 作为爬虫运行时的参数
  • 允许爬取的域名: 为对于爬虫设置的爬取范围,设置之后用于过滤要爬取的url,如果爬取的url与允许的域不通则被过滤掉。
  • 示例:
    cd myspider
    scrapy genspider itcast itcast.cn

完善爬虫

  1. 在/myspider/myspider/spiders/itcast.py中修改内容如下:
import scrapy

class ItcastSpider(scrapy.Spider):  # 继承scrapy.spider
	# 爬虫名字 
    name = 'itcast' 
    # 允许爬取的范围
    allowed_domains = ['itcast.cn'] 
    # 开始爬取的url地址
    start_urls = ['http://www.itcast.cn/channel/teacher.shtml']
    
    # 数据提取的方法,接受下载中间件传过来的response
    def parse(self, response): 
    	# scrapy的response对象可以直接进行xpath
    	names = response.xpath('//div[@class="tea_con"]//li/div/h3/text()') 
    	print(names)

    	# 获取具体数据文本的方式如下
        # 分组
    	node_list = response.xpath('//div[@class="tea_con"]//li') 
        # 遍历所有教师结点
        for node in node_list:
            # temp={}
            #实例化
            temp=MyspiderItem()
            # xpath方法返回的是选择器对象列表[]
            #.extract()用于从选择器对象提取数据,多个值的列表
            # 如果xpath结果是只含有一个值的列表,用extract_first()
            temp["name"]=node.xpath('./div[2]/h3/text()').extract_first()
            temp["title"]=node.xpath('./div[2]/h4/text()')[0].extract()
            temp["desc"]=node.xpath('./div[2]/p/text()')[0].extract()
            print(temp)
            yield temp  #爬虫中返回数据通常用yield,翻页方便。return没法继续走

注意:

  • scrapy.Spider爬虫类中必须有名为parse的解析
  • 如果网站结构层次比较复杂,也可以自定义其他解析函数
  • 在解析函数中提取的url地址如果要发送请求,则必须属于allowed_domains范围内,但是start_urls中的url地址不受这个限制,我们会在后续的课程中学习如何在解析函数中构造发送请求
  • 启动爬虫的时候注意启动的位置,是在项目路径下启动
  • parse()函数中使用yield返回数据,注意:解析函数中的yield能够传递的对象只能是:BaseItem, Request, dict, None
  1. 定位元素以及提取数据、属性值的方法
    解析并获取scrapy爬虫中的数据: 利用xpath规则字符串进行定位和提取
  • response.xpath方法的返回结果是一个类似list的类型,其中包含的是selector对象,操作和列表一样,但是有一些额外的方法
  • 额外方法extract():返回一个包含有字符串的列表
  • 额外方法extract_first():返回列表中的第一个字符串,列表为空没有返回None
  1. response响应对象的常用属性
  • response.url:当前响应的url地址
  • response.request.url:当前响应对应的请求的url地址
  • response.headers:响应头
  • response.requests.headers:当前响应的请求头
  • response.body:响应体,也就是html代码,byte类型
  • response.status:响应状态码

保存数据

利用管道pipeline来处理(保存)数据

  • 在pipelines.py文件中定义对数据的操作
  1. 定义一个管道类
  2. 重写管道类的process_item方法
  3. process_item方法处理完item之后必须返回给引擎
  import json
  
  class ItcastPipeline():
      # 爬虫文件中提取数据的方法每yield一次item,就会运行一次
      # 该方法为固定名称函数
      def process_item(self, item, spider):
          print(item)
          return item
  • 在settings.py配置启用管道
ITEM_PIPELINES = {
       'myspider.pipelines.ItcastPipeline': 400
   }

配置项中键为使用的管道类,管道类使用.进行分割,第一个为项目目录,第二个为文件,第三个为定义的管道类。
配置项中值为管道的使用顺序,设置的数值约小越优先执行,该值一般设置为1000以内。

运行

命令:在项目目录下执行scrapy crawl <爬虫名字>
示例:scrapy crawl itcast

总结

  1. scrapy的安装:pip install scrapy
  2. 创建scrapy的项目: scrapy startproject myspider
  3. 创建scrapy爬虫:在项目目录下执行 scrapy genspider itcast itcast.cn
  4. 运行scrapy爬虫:在项目目录下执行 scrapy crawl itcast
  5. 解析并获取scrapy爬虫中的数据:
    1. response.xpath方法的返回结果是一个类似list的类型,其中包含的是selector对象,操作和列表一样,但是有一些额外的方法
    2. extract() 返回一个包含有字符串的列表
    3. extract_first() 返回列表中的第一个字符串,列表为空没有返回None
  6. scrapy管道的基本使用:
    1. 完善pipelines.py中的process_item函数
    2. 在settings.py中设置开启pipeline
  7. response响应对象的常用属性
    1. response.url:当前响应的url地址
    2. response.request.url:当前响应对应的请求的url地址
    3. response.headers:响应头
    4. response.requests.headers:当前响应的请求头
    5. response.body:响应体,也就是html代码,byte类型
    6. response.status:响应状态码

数据建模与请求

建模

  • 建模原因
  1. 定义item即提前规划好哪些字段需要抓,防止手误,因为定义好之后,在运行过程中,系统会自动检查
  2. 配合注释一起可以清晰的知道要抓取哪些字段,没有定义的字段不能抓取,在目标字段少的时候可以使用字典代替
  3. 使用scrapy的一些特定组件需要Item做支持,如scrapy的ImagesPipeline管道类
  • 建模方法:在items.py文件中定义要提取的字段:
class MyspiderItem(scrapy.Item): 
    name = scrapy.Field()   # 讲师的名字
    title = scrapy.Field()  # 讲师的职称
    desc = scrapy.Field()   # 讲师的介绍
  • 使用模板类
    模板类定义以后需要在爬虫中导入并且实例化,之后的使用方法和使用字典相同
    job.py:
from myspider.items import MyspiderItem   # 导入Item,注意路径
...
    def parse(self, response)

        item = MyspiderItem() # 实例化后可直接使用

        item['name'] = node.xpath('./h3/text()').extract_first()
        item['title'] = node.xpath('./h4/text()').extract_first()
        item['desc'] = node.xpath('./p/text()').extract_first()
        
        print(item)

注意:

  1. from myspider.items import MyspiderItem这一行代码中 注意item的正确导入路径,忽略pycharm标记的错误
  2. python中的导入路径要诀:从哪里开始运行,就从哪里开始导入

开发流程总结

  1. 创建项目
    scrapy startproject 项目名
  2. 明确目标
    在items.py文件中进行建模
  3. 创建爬虫
    3.1 创建爬虫
    scrapy genspider 爬虫名 允许的域
    3.2 完成爬虫
    修改start_urls
    检查修改allowed_domains
    编写解析方法
  4. 保存数据
    在pipelines.py文件中定义对数据处理的管道
    在settings.py文件中注册启用管道

翻页请求

  • requests模块实现翻页请求:
  1. 找到下一页的URL地址
  2. 调用requests.get(url)
  • scrapy实现翻页的思路:
  1. 找到下一页的url地址
  2. 构造url地址的请求对象,传递给引擎

构造Request对象、发送请求

实现方法:

  1. 确定url地址
  2. 构造请求,scrapy.Request(url,callback)
    • callback:指定解析函数名称,表示该请求返回的响应使用哪一个函数进行解析
  3. 把请求交给引擎:yield scrapy.Request(url,callback)

网易招聘爬虫

通过爬取网易招聘的页面的招聘信息,学习如何实现翻页请求
地址:https://hr.163.com/position/list.do

思路分析:

  1. 获取首页的数据
  2. 寻找下一页的地址,进行翻页,获取数据

注意:

1. 可以在settings中设置ROBOTS协议
    # False表示忽略网站的robots.txt协议,默认为True
    ROBOTSTXT_OBEY = False

2. 可以在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'
代码实现
......
	# 提取下一页的href
	next_url = response.xpath('//a[contains(text(),">")]/@href').extract_first()

	# 判断是否是最后一页
	if next_url != 'javascript:void(0)':

        # 构造完整url
        url = 'https://hr.163.com/position/list.do' + next_url

		# 构造scrapy.Request对象,并yield给引擎
		# 利用callback参数指定该Request对象之后获取的响应用哪个函数进行解析
    	yield scrapy.Request(url, callback=self.parse)
......
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请求时使用(在下一章节中会介绍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的下载中间件的学习中进行介绍

小结

  1. 完善并使用Item数据类:
    1. 在items.py中完善要爬取的字段
    2. 在爬虫文件中先导入Item
    3. 实力化Item对象后,像字典一样直接使用
  2. 构造Request对象,并发送请求:
    1. 导入scrapy.Request类
    2. 在解析函数中提取url
    3. yield scrapy.Request(url, callback=self.parse_detail, meta={})
  3. 利用meta参数在不同的解析函数中传递数据:
    1. 通过前一个解析函数 yield scrapy.Request(url, callback=self.xxx, meta={}) 来传递meta
    2. 在self.xxx函数中 response.meta.get(‘key’, ‘’) 或 response.meta[‘key’] 的方式取出传递的数据

网易项目
提取岗位及岗位职责要求,meta传递参数

wangyi/spiders/job.py

import scrapy
from wangyi.items import WangyiItem

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

    def parse(self, response):
        # 提取数据
        # 获取所有职位结点列表
        node_list = response.xpath('//tbody/tr')
        # node_list=response.xpath('//*[@class="position-tb"]/tbody/tr')
        print(len(node_list))
        # 遍历结点列表,过滤
        for num, node in enumerate(node_list):
            # print(num,node)
            # 设置过滤条件
            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/text()').extract_first())
                item["link"] = 'https://hr.163.com/' + 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()
                item["num"] = node.xpath('./td[6]/text()').extract_first().strip()  # 用于去除字符串中的空格
                item["date"] = node.xpath('./td[7]/text()').extract_first()
                # yield item

                # 构建详情页面的请求
                yield scrapy.Request(
                    url=item['link'],
                    callback=self.parse_deatil,
                    meta={'item': item}
                )

        # 模拟翻页
        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
            )

    def parse_deatil(self, response):
        # print(response.meta['item'])
        # 提取剩余字段的数据
        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()  # 数据为多个
        # print(item)

        # 返回给引擎
        yield item

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()

    duty = scrapy.Field()
    require = scrapy.Field()

wangyi/pipeline.py

import json
class WangyiPipeline:
    def __init__(self):
        self.file=open('wangyi.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
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值