scrapy由浅入深(二) 爬取51job职位薪资信息

        上次的爬虫只是爬取了CSDN论坛的问题数据,相对来说比较简单,本篇文章来介绍一下爬取51job网站,获取它的职位,薪资,职位要求等信息。

        代码思路:1.首先获取到种子网页的所有职位的url,以及下一页的url。2.通过抽取到的职位的url来依次请求相应职位的详细信息,包括薪资,职位要求等。3.定义解析数据的函数,通过xpath或者css选择器获取到职位薪资信息。4.请求第一步中获取到的下一页的网址,对下一页的网址进行分析,抽取出url,再依次分析。5.将数据保存到数据库。

1.创建项目

scrapy startproject wuyou

创建一个名称为wuyou的项目,爬取前程无忧网站的职位信息。

手动在spiders文件夹下创建一个job_spider.py文件,用来实现爬虫主要逻辑。

二.配置项目

(1)编写items文件

import scrapy


class WuyouItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    title = scrapy.Field()
    salary = scrapy.Field()
    profile = scrapy.Field()

我在这里定义了三个字段,分别对应职位名称,薪资,职位要求。

(2)编写pipelines文件

import sqlite3
db = sqlite3.connect("./../jobs.db")
cursor = db.cursor()

class WuyouPipeline(object):
    def process_item(self, item, spider):
        cursor.execute("insert into job(title, salary, profile) values (?,?,?)",(item["title"],item["salary"],item["profile"]))
        db.commit()

将数据保存在sqlite数据库中,也可以保存在MySQL数据库中,写法类似。

(3)配置settings文件

启用管道文件

ITEM_PIPELINES = {
   'wuyou.pipelines.WuyouPipeline': 300,
}

启用下载延迟

DOWNLOAD_DELAY = 3
RANDOMIZE_DOWNLOAD_DELAY = True

(4)编写spider文件

爬虫主要逻辑

# -*- coding: utf-8 -*-
import scrapy
from scrapy import Request
from wuyou.items import WuyouItem


class JobSpiderSpider(scrapy.Spider):
    name = 'job_spider'
    # allowed_domains = ['jobs.51job.com']

    def start_requests(self):
        url_str = 'https://search.51job.com/list/000000,000000,0000,00,9,99,python,2,1.html?lang=c&stype=1&postchannel=0000&workyear=99&cotype=99&degreefrom=99&jobterm=99&companysize=99&lonlat=0%2C0&radius=-1&ord_field=0&confirmdate=9&fromType=&dibiaoid=0&address=&line=&specialarea=00&from=&welfare='
        yield Request(url=url_str,callback=self.parse,meta={"page":"0"})

    def parse(self, response):
        """
        解析出职位的url以及下一页的url
        :param response:
        :return:
        """
        all_href = response.xpath('//div[@class="el"]/p/span[not(@class)]/a/@href').extract()
        next_page = response.xpath('//a[text()="下一页"]/@href').extract_first()

        for href in all_href:
            yield Request(url=href,callback=self.parse_one_job,meta={"page":"0"})
        if next_page:
            yield Request(url=next_page,callback=self.parse,meta={"page":"0"},dont_filter=True)


    def parse_one_job(self,response):
        """
        通过xpath获取职位,薪资,职位要求信息
        :param response:
        :return:
        """
        title = response.xpath('//div[@class="cn"]/h1/@title').extract()[0]
        salary = response.xpath('//div[@class="cn"]/strong/text()').extract()[0]
        job_profile = response.xpath('//div[@class="bmsg job_msg inbox"]/p/text()').extract()
        # 因为51job页面有的职位信息xpath不相同,所以使用两个xpath来获取职位要求信息
        job_profile_div = response.xpath('//div[@class="bmsg job_msg inbox"]/div/text()').extract()
        if job_profile_div is not None:
            job_profile = job_profile_div + job_profile
        content = ""
        for profile in job_profile:
            content += profile
        content = ' '.join(content.split())
        item = WuyouItem()
        item["title"] = title
        item["salary"] = salary
        item["profile"] = content
        yield item

首先请求种子网址,从种子网址中抽取每个职位对应的url与下一页的url,通过函数parse_one_job解析每个工作的详细信息,通过对下一页的url解析,判断是否有下一页,如果有则请求下一页,并对下一页进行分析。

注意:因为51job网站工作详情的html标签并不相同,这里即使用到了两个xpath来获取职位的详细信息,但是依然有个别职位的详细信息无法爬取到。下次准备爬取智联招聘这个网站,虽然同样是招聘网站,但是它跟51job最大的区别在于,智联招聘是ajax动态页面,无法直接获取,下次介绍一下使用selenium模拟浏览器来爬取ajax动态页面。

优化(防止数据丢失)

        我在后续的爬去过程中发现这个程序会造成数据丢失的问题,原因在于,当我们判断如果有下一页就请求下一页的时候,这个请求与下载一个页面所有的职位信息速度不匹配,也就相当于当你请求到第十几页的时候,第一页的所有数据才会下载完成。那么这个时候scrapy已经接受了几百个爬取职位详细信息的请求,因为请求过多,所以会造成数据丢失的后果。

        解决办法:查询数据库的数据记录,因为51job一个网页有50条职位信息,当我们爬取了大约45条数据的时候就能执行请求下一页的操作了,这样就相当于只有在下载完当前页面的数据才会请求下一页的数据,也就大大减少了数据丢失的可能性。

        优化代码:

# -*- coding: utf-8 -*-
import scrapy
from scrapy import Request
from wuyou.items import WuyouItem
import sqlite3
import time

db = sqlite3.connect("./../jobs.db")
cursor = db.cursor()

i = 0

def select_from_sql():
    """
    :return: 当前数据库中数据的总数
    """
    count = cursor.execute("select * from job")
    return len(count.fetchall())

class JobSpiderSpider(scrapy.Spider):
    name = 'job_spider'
    # allowed_domains = ['jobs.51job.com']

    def start_requests(self):
        url_str = 'https://search.51job.com/list/000000,000000,0000,00,9,99,python,2,1.html?lang=c&stype=1&postchannel=0000&workyear=99&cotype=99&degreefrom=99&jobterm=99&companysize=99&lonlat=0%2C0&radius=-1&ord_field=0&confirmdate=9&fromType=&dibiaoid=0&address=&line=&specialarea=00&from=&welfare='
        yield Request(url=url_str,callback=self.parse)

    def parse(self, response):
        """
        解析出职位的url以及下一页的url
        :param response:
        :return:
        """
        all_href = response.xpath('//div[@class="el"]/p/span[not(@class)]/a/@href').extract()
        next_page = response.xpath('//a[text()="下一页"]/@href').extract_first()

        for href in all_href:
            yield Request(url=href,callback=self.parse_one_job,meta={"next_page":next_page})


    def parse_one_job(self,response):
        """
        通过xpath获取职位,薪资,职位要求信息
        :param response:
        :return:
        """
        global i
        next_page = response.meta["next_page"]
        count = select_from_sql()

        title = response.xpath('//div[@class="cn"]/h1/@title').extract()[0]
        salary = response.xpath('//div[@class="cn"]/strong/text()').extract()[0]
        job_profile = response.xpath('//div[@class="bmsg job_msg inbox"]/p/text()').extract()
        # 因为51job页面有的职位信息xpath不相同,所以使用两个xpath来获取职位要求信息
        job_profile_div = response.xpath('//div[@class="bmsg job_msg inbox"]/div/text()').extract()
        if job_profile_div is not None:
            job_profile = job_profile_div + job_profile
        content = ""
        for profile in job_profile:
            content += profile
        content = ' '.join(content.split())


        item = WuyouItem()
        item["title"] = title
        item["salary"] = salary
        item["profile"] = content

        if count - i > 45:
            if next_page:
                i = count
                yield Request(url=next_page, callback=self.parse, meta={"page": "2"}, dont_filter=True)
        yield item

猜你喜欢

转载自blog.csdn.net/WanYu_Lss/article/details/82794813