CrawlScrapy框架爬取Boos直聘职位信息

版权声明:转载请声明出处,谢谢! https://blog.csdn.net/qq_31468321/article/details/83218705

写在前面和推荐学习

零基础:21天搞定Python分布爬虫
在本文中使用CrawlScrapy框架结合代理来实现对Boos直聘职位信息的爬取。

简单说明Scrapy框架

在这里插入图片描述

主要流程
1.爬虫发送一个请求给引擎
2.引擎将这个请求发送给调度器
3.调度器按照一定的方式进行整理,在将请求发送给引擎
4.引擎再次将请求发送给下载器中间件,去到网络中请求资源进行下载
5.下载之后封装为Response对象,返回给引擎
6.引擎在将Resonse发送给爬虫提取信息,如果请求正常这时爬虫返回一个ITEMS,否则返回一个Resqust对象给引擎,再次进入调度器
7.这个items/Request返回给引擎,引擎将ITEMS对象发送给Pipeline对数据进行保存

爬虫结构

本次爬虫使用的是CrawlScrapy框架,这个框架继承了Scrapy框架,流程一致,不同的是,CrawlScrapy框架中有一个链接提取器,通过正则表达式进行匹配页面中所有的URL,有点广度优先的思想。
链接提取器如下:

rules = (
        #c101010100/?query=数据分析&page=2
        Rule(LinkExtractor(allow=r'.+/\?query=python&page=\d'), follow=True),
        #https://www.zhipin.com/job_detail/9f625f605d18d1661XN_39--GFU~.html
        Rule(LinkExtractor(allow=r'.+/job_detail/.+'), callback='parse_item', follow=False),
    )

在此次爬虫中主要实现了如下几个部分:

  1. 爬虫部分,解析提取网页
  2. 实现了链接提取器中的规则,确定了需要爬取那些网页
  3. 实现了下载器中间件部分,在该部分添加了代理的功能,这样即使IP被封也可以继续爬取信息
  4. 实现了Pipeline的部分,对获取到的数据进行的保存,将数据保存到了JSON文件中

爬虫部分

实现了一个类,继承了CrawlSpider类,再定义规则时,主要获取列表页信息和详情页信息,再列表页中获取详情页的URL,主要对详情页的信息进行解析,提取详情页中的主要信息,返回ITEMS
代码如下:

	class BossSpiderSpider(CrawlSpider):
    name = 'boss_spider'
    allowed_domains = ['www.zhipin.com']
    start_urls = ['https://www.zhipin.com/job_detail/?query=python&page=1']

    rules = (
    	#匹配列表页URL
        #c101010100/?query=数据分析&page=2
        Rule(LinkExtractor(allow=r'.+/\?query=python&page=\d'), follow=True),
        #匹配详情页URL
        #https://www.zhipin.com/job_detail/9f625f605d18d1661XN_39--GFU~.html
        Rule(LinkExtractor(allow=r'.+/job_detail/.+'), callback='parse_item', follow=False),
    )

    def parse_item(self, response):
        title = response.xpath("//div[@class='name']/h1/text()").get()
        salary = response.xpath("//span[@class='badge']/text()").get()
        if salary:
            salary = salary.strip()
        else:
            salary = ''
        company_div = response.xpath("//div[@class='info-company']")
        company_name = company_div.xpath("./h3[@class='name']/a/text()").get()
        company_info = company_div.xpath("./p[1]//text()").getall()
        primary_info = response.xpath("//div[@class='info-primary']/p[1]/text()").getall()
        city = primary_info[0]
        work_time = primary_info[1]
        education = primary_info[2]
        datail_count = response.xpath("//div[@class='detail-content']//div[@class='text']/text()").getall()
        
        item = BoosDemoItem(
            title = title,
            salary = salary,
            company_name = company_name,
            company_info = company_info,
            city = city,
            work_time = work_time,
            education = education,
            datail_count = datail_count
        )
        yield item

下载器中间件部分

在下载器中间件这里实现了两个类,分别为请求头中间件UserAgentMiddleware,和IP代理中间件IPProxyMiddlewares
请求头中间件:
在列表中保存多个请求头,在每次请求的时候随机选择一个。

    #process_request接口会在请求开始的时候执行
    def process_request(self,request,spider):
        User_Agent = random.choice(self.Useragent)
        request.headers['User-Agent'] = User_Agent

IP代理中间件:
判断每次URL失效的时候重新请求一个IP代理。重新返回爬取

	class IPProxyMiddlewares(object):
    def __init__(self):
        self.currenu_proxy = None
        self.lock = DeferredLock()

    def process_request(self,request,spider):
        if 'proxy' not in request.meta or self.currenu_proxy.is_expire:
            self.update_proxy()
        request.meta['proxy'] = self.currenu_proxy.proxy

    def update_proxy(self):
        self.lock.acquire()
        if not self.currenu_proxy or self.currenu_proxy.is_expire or self.currenu_proxy.block:
            self.url = 'http://webapi.http.zhimacangku.com/getip?num=1&type=2&pro=&city=0&yys=0&port=11&time=1&ts=1&ys=0&cs=0&lb=1&sb=0&pb=4&mr=1&regions='

            resp = requests.get(url=self.url)
            info_text = resp.text
            info = json.loads(info_text)
            if len(info['data'])>0:
                data = info['data'][0]
                self.currenu_proxy = Ipproxy(data)
        self.lock.release()
	#代理请求之后返回的时候执行
    def process_response(self,request,response,spider):
        if 'captcha' in response.url or response.status != 200 :
            if not self.currenu_proxy.block:
                self.currenu_proxy.block = True
            self.update_proxy()
            #request.meta['proxy'] = self.currenu_proxy.get_proxy()
            return request
        return response
        
class Ipproxy(object):
    def __init__(self,data):

        self.ip = data['ip']
        self.port = data['port']
        expire_time_str = data['expire_time']
        print(self.ip, self.port, expire_time_str)
        self.block = False
        # 117.94.113.20 4734 2018-10-18 23:23:13
        year, month, day = expire_time_str.split()[0].split('-')
        hour, minute, scend = expire_time_str.split()[1].split(':')
        self.expire_time = datetime(year=int(year), month=int(month), day=int(day), hour=int(hour), minute=int(minute),second=int(scend))

        self.proxy = "https://{}:{}".format(self.ip,self.port)

    @property
    def is_expire(self):
        if (self.expire_time - datetime.now()) < timedelta(seconds=5):
            return True
        else:
            return False

Pipeline的部分

打开一个JSON文件,将数据保存到JSON文件中

#按行写入数据
from scrapy.exporters import JsonLinesItemExporter

class BoosDemoPipeline(object):
    def __init__(self):
        self.boss_fp = open("boss_python_fp.json",'wb')
        self.boos_exporter = JsonLinesItemExporter(self.boss_fp,ensure_ascii = False)
    def process_item(self, item, spider):
        self.boos_exporter.export_item(item)

    def close_item(self,spider):
        self.boss_fp.close()

推荐学习

知了课堂零基础:21天搞定Python分布爬虫

猜你喜欢

转载自blog.csdn.net/qq_31468321/article/details/83218705