Scrapy框架——CrawlSpider爬取某招聘信息网站

CrawlSpider

Scrapy框架中分两类爬虫,Spider类和CrawlSpider类。

它是Spider的派生类,Spider类的设计原则是只爬取start_url列表中的网页,

而CrawlSpider类定义了一些规则(rule)来提供跟进link的方便的机制,从爬取的网页中获取link并继续爬取的工作更适合。

创建项目指令:

scrapy startproject tenCent

CrawlSpider创建:

scrapy genspider -t crawl crawl_tencent "hr.tencent.com"

CrawlSpider继承于Spider类,除了继承过来的属性外(name、allow_domains),还提供了新的属性和方法:

LinkExtractor

from scrapy.linkextractors import LinkExtractor
LinkExtractor(allow=r'start=\d+')

通过实例化LinkExtractor提取链接

主要参数

allow:满足括号中“正则表达式”的值会被提取,如果为空,则全部匹配。
            
deny:与这个正则表达式(或正则表达式列表)不匹配的URL一定不提取。

rules

Rule(LinkExtractor(allow=r'start=\d+'), callback='parse_tencent', follow=True),

在rules中包含一个或多个Rule对象,每个Rule对爬取网站的动作定义了特定操作。

如果多个rule匹配了相同的链接,则根据规则在本集合中被定义的顺序,第一个会被使用

主要参数

link_extractor:是一个Link Extractor对象,用于定义需要提取的链接
callback: 从link_extractor中每获取到链接时,参数所指定的值作为回调函数,该回调函数接受一个response作为其第一个参数。
    
注意:当编写爬虫规则时,避免使用parse作为回调函数。由于CrawlSpider使用parse方法来实现其逻辑,如果覆盖了 parse方法,crawl spider将会运行失败。
    
follow:是一个布尔(boolean)值,指定了根据该规则从response提取的链接是否需要跟进。 如果callback为None,follow 默认设置为True ,否则默认为False。

使用CrawlSpider爬取信息

1.编写item文件

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

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

import scrapy


class TencentItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    # 职位名称
    position_name = scrapy.Field()
    # 详情链接
    position_link = scrapy.Field()
    # 职位类别
    position_type = scrapy.Field()
    # 职位人数
    position_number = scrapy.Field()
    # 职位地点
    work_location = scrapy.Field()
    # 发布时间
    publish_times = scrapy.Field()
    # 工作职责
    position_duty = scrapy.Field()
    # 工作要求
    position_require = scrapy.Field()


class DetailItem(scrapy.Item):
    # 工作职责
    position_duty = scrapy.Field()
    # 工作要求
    position_require = scrapy.Field()

2.编写crawlspider文件

# -*- coding: utf-8 -*-
import scrapy
from tenCent.items import TencentItem
from tenCent.items import DetailItem
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule


class CrawlTencentSpider(CrawlSpider):
    name = 'crawl_tencent'
    allowed_domains = ['hr.tencent.com']
    start_urls = ['https://hr.tencent.com/position.php']

    '''
    rule LinkExtractor规则:
        allow:根据正则表达式匹配链接
        callback:回调函数
        follow:是否提取跟进页(链接套链接)
    '''
    rules = (
        Rule(LinkExtractor(allow=r'start=\d+'), callback='parse_tencent', follow=True),
        # 从上面的规则传递下一个
        Rule(LinkExtractor(allow=r'position_detail\.php\?id=\d+'), callback='parse_detail', follow=False),
    )

    def parse_tencent(self, response):
        print('start……')
        node_list = response.xpath('//tr[@class="even"] | //tr[@class="odd"]')
        # 选取所有标签tr 且class属性等于even或odd的元素
        #i['domain_id'] = response.xpath('//input[@id="sid"]/@value').extract()
        #i['name'] = response.xpath('//div[@id="name"]').extract()
        #i['description'] = response.xpath('//div[@id="description"]').extract()
        for node in node_list:
            item = TencentItem()
            item['position_name'] = node.xpath('./td[1]/a/text()').extract_first()  # 获取第一个td标签下a标签的文本
            item['position_link'] = node.xpath('./td[1]/a/@href').extract_first()  # 获取第一个td标签下a标签href属性
            item['position_type'] = node.xpath('./td[2]/text()').extract_first()  # 获取第二个td标签下文本
            item['position_number'] = node.xpath('./td[3]/text()').extract_first()  # 获取第3个td标签下文本
            item['work_location'] = node.xpath('./td[4]/text()').extract_first()  # 获取第4个td标签下文本
            item['publish_times'] = node.xpath('./td[5]/text()').extract_first()  # 获取第5个td标签下文本

            yield item

    def parse_detail(self, response):
        item = DetailItem()
        item['position_duty'] = ''.join(
            response.xpath('//ul[@class="squareli"]')[0].xpath('./li/text()').extract())  # 转化为字符串
        item['position_require'] = ''.join(
            response.xpath('//ul[@class="squareli"]')[1].xpath('./li/text()').extract())  # 转化为字符串

        yield item

3.建立pipeline文件

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

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html

import json
from .items import TencentItem
from .items import DetailItem

class TencentPipeline(object):
    def open_spider(self, spider):
        """
         # spider (Spider 对象) – 被开启的spider
         # 可选实现,当spider被开启时,这个方法被调用。
        :param spider:
        :return:
        """
        self.file = open('tencent.json', 'w', encoding='utf-8')
        json_header = '{ "tencent_info":['
        self.count = 0
        self.file.write(json_header)  # 保存到文件

    def close_spider(self, spider):
        """
        # spider (Spider 对象) – 被关闭的spider
        # 可选实现,当spider被关闭时,这个方法被调用
        :param spider:
        :return:
        """
        json_tail = '] }'
        self.file.seek(self.file.tell() - 1)  # 定位到最后一个逗号
        self.file.truncate()  # 截断后面的字符
        self.file.write(json_tail)  # 添加终止符保存到文件
        self.file.close()

    def process_item(self, item, spider):
        """
        # item (Item 对象) – 被爬取的item
        # spider (Spider 对象) – 爬取该item的spider
        # 这个方法必须实现,每个item pipeline组件都需要调用该方法,
        # 这个方法必须返回一个 Item 对象,被丢弃的item将不会被之后的pipeline组件所处理。

        :param item:
        :param spider:
        :return:
        """
        # print('item=',dict(item))

        if isinstance(item, TencentItem):
            print('--'*20)
            content = json.dumps(dict(item), ensure_ascii=False, indent=2) + ","  # 字典转换json字符串
            self.count += 1
            print('content', self.count)
            self.file.write(content)  # 保存到文件
        '''
        return item后,item会根据优先级
        传递到下一个管道DetailPipeline处理
        此段代码说明当实例不属于TencentItem时,放弃存储json,
        直接传递到下一个管道处理
        return放在if外面,如果写在if里面item在不属于TencentItem实例后,
        item会终止传递,造成detail数据丢失
        '''
        return item

class DetailPipeline(object):
    def open_spider(self, spider):
        """
         # spider (Spider 对象) – 被开启的spider
         # 可选实现,当spider被开启时,这个方法被调用。
        :param spider:
        :return:
        """
        self.file = open('detail.json', 'w', encoding='utf-8')
        json_header = '{ "detail_info":['
        self.count = 0
        self.file.write(json_header)  # 保存到文件

    def close_spider(self, spider):
        """
        # spider (Spider 对象) – 被关闭的spider
        # 可选实现,当spider被关闭时,这个方法被调用
        :param spider:
        :return:
        """
        json_tail = '] }'
        self.file.seek(self.file.tell() - 1)  # 定位到最后一个逗号
        self.file.truncate()  # 截断后面的字符
        self.file.write(json_tail)  # 添加终止符保存到文件
        self.file.close()

    def process_item(self, item, spider):
        """
        # item (Item 对象) – 被爬取的item
        # spider (Spider 对象) – 爬取该item的spider
        # 这个方法必须实现,每个item pipeline组件都需要调用该方法,
        # 这个方法必须返回一个 Item 对象,被丢弃的item将不会被之后的pipeline组件所处理。

        :param item:
        :param spider:
        :return:
        """
        # print('item=',dict(item))

        if isinstance(item, DetailItem):
            '''
            得到item,判断item实例属于DetailItem,存储json文件
            如果不属于,直接return item到下一个管道
          '''
            print('**' * 30)
            content = json.dumps(dict(item), ensure_ascii=False, indent=2) + ","  # 字典转换json字符串
            self.count += 1
            print('content', self.count)
            self.file.write(content)  # 保存到文件
        return item

4.设置settiing

#1、项目名称,默认的USER_AGENT由它来构成,也作为日志记录的日志名
BOT_NAME = 'tenCent'
# 2、爬虫应用路径
SPIDER_MODULES = ['tenCent.spiders']
NEWSPIDER_MODULE = 'tenCent.spiders'


# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = '"User-Agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"'  # 头部信息,反爬

# Obey robots.txt rules
ROBOTSTXT_OBEY = True

# log日志
LOG_FILE = 'tencent.log'
LOG_LEVEL = 'DEBUG'
LOG_ENCODING = 'utf-8'
LOG_DATEFORMAT='%m/%d/%Y %H:%M:%S %p'

ITEM_PIPELINES = {
   'tenCent.pipelines.TencentPipeline': 300,
   'tenCent.pipelines.DetailPipeline':400
}

5.执行程序

scrapy crawl crawl_tencent

tencent.log

tencent.json

detail.json

猜你喜欢

转载自www.cnblogs.com/xiao-apple36/p/9027326.html
今日推荐