【Scrapy】CrawlSpider 单页面Ajax爬取

项目目标

爬取拉勾网职位列表基本信息+职位描述

项目思考

拉勾网的招聘岗位列表,这是Ajax异步加载的。

我想把岗位列表所显示的信息爬取下来,同时还需要岗位的工作详情。

爬取流程就是一开始就不断获取职位列表的json,然后从json中提取对应的职位详情页,再进去爬取其职位描述。

使用Scrapy的scrapy.Spider基础爬虫模板很简单就可以实现,直接重载编写parse方法,再加上个回调方法就可以。

但如何使用CrawlSpider做到类似的功能?

对于获取json的网址,rules规则没什么用,而CrawlSpider中的parse是不能被重载的。

  1. 第一步,重写start_request():

    crawlspider继承基类是spider,所以它的开始入口也是start_request(),然后默认回调parse。注意回调parse这个不能改。

  2. 第二步,重写parse_start_url()

    start_request()->parse()->_parse_response()->parse_start_url()

    如果设置了callback就会调用parse_start_url()方法,rules中的回调。

    因为rules在本次项目中没有作用,所以我们需要重载parse_start_url()作为我们的回调方法。

    在parse_start_url()中,需要获取职位的详情页,发起request,设置回调方法。
    不断发起下一页的职位列表请求。

  3. 第三步,编写解析职位描述的detail_parse()

    注意的时,这里最后在parse_start_url()中使用response.meta传递item到detail_parse()来进行数据保存。

    因为如果在parse_start_url()就把职位列表的信息保存下来的话,因为Scrapy程序调度的关系,在插入数据库的时候,item中的数据不一定同步,插入数据库会报一些错。

  4. 注意request要带上有cookie的header,不然会被重定向到login页面

项目代码

# -*- coding: utf-8 -*-
import json
import random
import time
from datetime import datetime

import scrapy
from scrapy import FormRequest
from scrapy.spiders import CrawlSpider, Rule


from utils.common import get_md5
from items import LagouJobItemLoader, LagouItem


class LagouSpider(CrawlSpider):
    name = 'lagou'
    allowed_domains = ['www.lagou.com']
    start_urls = ['https://www.lagou.com/jobs/positionAjax.json?']

    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36',
        'Content-Type': 'application/x-www-form-urlencoded; set=UTF-8',
        'Cookie': 'JSESSIONID=ABAAABAAAGFABEF2A9F526EEAF8A4D5979C9C91C470D916; user_trace_token=20181108222228-77038827-4d01-4f91-8b3a-55a0cccaf9d6; Hm_lvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1541686949; _ga=GA1.2.1567223367.1541686949; _gid=GA1.2.489493353.1541686949; LGUID=20181108222230-b9c05f9e-e361-11e8-9314-525400f775ce; TG-TRACK-CODE=search_code; Hm_lpvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1541777035; LGRID=20181109232414-8446b9ff-e433-11e8-94aa-525400f775ce; SEARCH_ID=e89119713fd54563ad1b598a8c7e631a',
        'Referer': 'https://www.lagou.com/jobs/list_Python?labelWords=&fromSearch=true&suginput=1',
        'Host': 'www.lagou.com',
        'Origin': 'https: // www.lagou.com',
        'X-Anit-Forge-Code': 0,
        'X-Anit-Forge-Token': None,
        'X-Requested-With': 'XMLHttpRequest'
    }

    page = 1

    def start_requests(self):
        for url in self.start_urls:
            yield FormRequest(url, headers=self.headers,
                              formdata={
                                  'first': 'true',
                                  'pn': str(self.page),
                                  'kd': 'Python',
                                  'city': '深圳'

                              }, callback=self.parse,
                              dont_filter=True
                              )

    def parse_start_url(self, response):

        data = json.loads(response.body.decode('utf-8'))
        result = data['content']['positionResult']['result']
        totalCount = data['content']['positionResult']['totalCount']
        resultSize = data['content']['positionResult']['resultSize']

        for each in result:
            item = LagouItem()
            item['url'] = 'https://www.lagou.com/jobs/{}.html'.format(each['positionId'])
            res = scrapy.Request(url=item['url'], headers=self.headers, callback=self.detail_parse)
            res.meta['item'] = item
            res.meta['each'] = each
            yield res

            time.sleep(0.1)

        time.sleep(random.randint(1, 5))

        if int(resultSize):
            self.allpage = int(totalCount) / int(resultSize) + 1
            if self.page < self.allpage:
                self.page += 1
                yield FormRequest(self.start_urls[0], headers=self.headers,
                                  formdata={
                                      'first': 'false',
                                      'pn': str(self.page),
                                      'kd': 'Python',
                                      'city': '深圳'
                                  }, callback=self.parse
                                  )

    def detail_parse(self, response):

        item = response.meta['item']
        des = response.css('.job_bt div p::text').extract()
        item['description'] = ",".join(des)

        each = response.meta['each']
        item['url_obj_id'] = get_md5(item['url'])
        item['city'] = each['city']
        item['company_full_name'] = each['companyFullName']
        item['company_size'] = each['companySize']
        item['district'] = each['district']
        item['education'] = each['education']
        item['linestaion'] = each['linestaion']
        item['position_name'] = each['positionName']
        item['job_nature'] = each['jobNature']
        item['salary'] = each['salary']
        item['create_time'] = each['createTime']
        item['work_year'] = each['workYear']
        item["crawl_time"] = datetime.now()

        yield item

github源码
博客地址

你可能感兴趣的:(Scrapy)