«

scrapy数据建模与请求

时间:2023-3-1 00:00     作者:wen     分类: Python


一、数据建模

通常在做项目的过程中,在items.py中进行数据建模

  1. 为什么建模

    1. 定义item即提前规划好那些字段需要抓,防止手误,因为定义好之后,在运行过程中,系统会自动检查
    2. 配合注释一起可以清晰的知道要抓取那些字段,没有定义的字段不能抓取,在目标字段少的时候可以使用字典代替
    3. 使用 scrapy的一些特定组件需要item做支持,如scrapy的ImagePipeline管道类,百度收索了解更多
  2. 如何建模
    在items.py文件中定义要提取的字段:

    class MyspiderItem(scrapy.Item):
    # define the fields for your item here like:
    title = scrapy.Field()  # 网站标题
    href = scrapy.Field()  # 标题链接
    pass
  3. 如何使用模板类
    模板类定义以后需要在爬虫中导入并且实例化,之后的使用方式和使用字典相同:job.py

import scrapy
from myspider.items import MyspiderItem

class WenxkSpider(scrapy.Spider):
    name = 'wenxk'
    allowed_domains = ['wenxk.top']
    start_urls = ['http://wenxk.top/']

    def parse(self, response):
        node_list = response.xpath("//div[@class='views-row']")
        for node in node_list:
            temp = MyspiderItem()  # 实例化后可直接使用
            temp['title'] = node.xpath('./article/header/h2/a/span/text()').extract_first()
            temp['href'] = node.xpath('./article/header/h2/a/@href')[0].extract()
            # xpath方法返回的是选择器对象列表,extract()用于从选择器对象中提取数据
            # xpath结果为只含有一个结果的列表,可以使用extract_first(),没有结果返回None,如果为多个使用extract()

            yield temp
        pass

注意:

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

二、翻页请求的思路

  1. 找到下一页的url路径
  2. 构造url地址的请求对象,传递给引擎

三、构造Request对象,并发送请求

  1. 实现方法
    1. 确定url地址
    2. 构造请求,scrapy.Request(url,callback)
      • callback:指定解析函数名称,表示该请求返回的相应使用哪一个函数进行解析
    3. 把请求交给引擎:yield scrapy.Request(url,callback)
  2. 网易招聘爬虫
    通过爬取网易招聘的页面的信息,学习如何实现翻页请求
    地址:https://hr.163.com/position/list.do
    思路分析:
    1、获取页面的数据
    2、寻找下一页的地址,进行翻页,获取数据

注意:
1、可以在setting中设置RoBots协议,False表示忽略网站的robots.txt协议,默认为True

# Obey robots.txt rules
ROBOTSTXT_OBEY = True

2、可以在setting中设置User-Agent,scrapy发送的每一个请求地址的默认UA都是设置的这个User-Agent

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

3、代码实现

# items.py
import scrapy

class WagnyiItem(scrapy.Item):
    # define the fields for your item here like:
    name = scrapy.Field()  # 岗位名称
    link = scrapy.Field()  # 链接
    department = scrapy.Field()  # 所属部门
    category = scrapy.Field()  # 职位类别
    type = scrapy.Field()  # 工作类型
    address = scrapy.Field()  # 工作地点
    num = scrapy.Field()  # 招聘人数
    data = scrapy.Field()  # 发布时间
    pass
# job.py
import scrapy
from wagnyi.items import WagnyiItem

class JobSpider(scrapy.Spider):
    name = 'job'
    allowed_domains = ['163.com']
    start_urls = ['https://hr.163.com/position/list.do']

    def parse(self, response):
        # 提取数据

        # 获取所以职业节点列表
        node_list = response.xpath('//*[@class="position-tb"]/tbody/tr')

        # 遍历节点列表
        for num, node in enumerate(node_list):
            # 设置过滤条件,将目标节点获取出来
            if num % 2 == 0:
                item = WagnyiItem()

                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['department'] = 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['data'] = node.xpath('./td[7]/text()').extract_first()

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

        pass
# pipelines.py
import json

from itemadapter import ItemAdapter

class WagnyiPipeline:
    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

    def __del__(self):
        self.file.close()
  1. scrapy.Request的更多的参数
    scrapy.Request(url[,callback,method='GET',headers,body,cookies,meta,dont_filter=False])
    参数解释
    1. 中括号里的参数为可选参数
    2. callback:表示当前的url的响应交给那个函数去处理
    3. mete:实现数据在不同的解析函数中传递,meta默认带有部分数据,比如下载延时,请求深度
    4. dont_filter:默认为False,会过滤请求的URL地址,即请求过的url地址不会继续请求,对需要重复请求的URL地址可以把它设置为True,比如贴吧的翻页请求,页面的数据总是在变化;start_urls中的地址会被反复请求,否则程序不会启动
    5. method请求POST或GET请求
    6. headers:接收一个字典,其中不包括cookie
    7. cookies:接收一个字典,专门放置cookies
    8. body:接收json字符串,为POST的数据,发送payload_post请求时使用

四、meta参数的使用

meta的作用:meta可以实现数据在不同的解析函数中的传递
在爬虫文件的parse方法中,提取详细页增加之前callback指定的parse_detail函数:

# job.py
import scrapy
from wagnyi.items import WagnyiItem

class JobSpider(scrapy.Spider):
    name = 'job'
    allowed_domains = ['163.com']
    start_urls = ['https://hr.163.com/position/list.do']

    def parse(self, response):
        # 提取数据

        # 获取所以职业节点列表
        node_list = response.xpath('//*[@class="position-tb"]/tbody/tr')

        # 遍历节点列表
        for num, node in enumerate(node_list):
            # 设置过滤条件,将目标节点获取出来
            if num % 2 == 0:
                item = WagnyiItem()

                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['department'] = 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['data'] = node.xpath('./td[7]/text()').extract_first()

                yield scrapy.Request(url=item['link'], callback=self.parse_detail,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, meta={'item': item})

        pass

    def parse_detail(self, response):
        # 获取之前传入的item
        item = response.meta['item']
        print(item)

特别注意:

1、meta参数是一个字典
2、meta字典中有一个固定的键proxy,表示代理ip。

标签: 爬虫