实战案例 | Scrapy 集成Selenium爬取智联招聘数据

人生苦短,快学Python!

初学scrapy之后,发现就是效率对比于selenium和requests快了很多,那么问题来了,如果网站设置了反爬,比如User-Agent反爬,cookie反爬,IP封禁等等,所以我们需要通过集成selenium到scrapy中,绕过网站反爬,达到目的。

这里选择智联招聘网站作为案例,就是虽然不是动态网页,但是它需要模拟登录,所以我们通过scrapy集成selenium进行数据抓取。

实战案例 | Scrapy 集成Selenium爬取智联招聘数据_第1张图片

一、需求分析

打开目标网站,搜索web前端开发工程师

实战案例 | Scrapy 集成Selenium爬取智联招聘数据_第2张图片

这是首页,由于我的当前位置在武汉,所以系统自动定位到武汉,点击搜索后:

实战案例 | Scrapy 集成Selenium爬取智联招聘数据_第3张图片

这个就是需要通过selenium出路的一个点。

手动登录后得到以下界面:

实战案例 | Scrapy 集成Selenium爬取智联招聘数据_第4张图片

我们的目标是每一条招聘信息的8条数据:

  1. name 职位名称

  2. salary 薪资

  3. adress  地区

  4. experience 经验

  5. eduBack  教育背景

  6. company 公司名称

  7. companyType 公司类型

  8. scale 公司规模

  9. info 简介

二、scrapy项目文件配置

定义items

import scrapy

class ZlzpItem(scrapy.Item):
    name = scrapy.Field()
 ***薪资  公司  规模...***
    info = scrapy.Field()

定义scrapy爬虫:zl.py(智联)

#这里先说明下url:
firstPageUrl : 'https://sou.zhaopin.com/?jl=736&kw=web%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%B8%88&p=1'
    #作为第一页的url,下面的myspider.py中就不在展示,避免代码冗余。
    base_url = 'https://sou.zhaopin.com/?jl=736&kw=web%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%B8%88&p={}'

然后下面是zl.py的源码:(分为几个部分)

1、初始化设置:

# -*- coding: utf-8 -*-
import scrapy
from zlzp.items import ZlzpItem

count = 1   # 定义一个全局变量,与base_url构建 下一页的url 
class ZlSpider(scrapy.Spider):
    name = 'zl'
    allowed_domains = ['zhaopin.com']
    start_urls = [firstPageUrl]

2、parse函数:

    def parse(self, response):

        global count
        count += 1  # 每解析一次页面,让count+1,和baseurl构造下一页的url

        jobList = response.xpath('//div[@class="positionlist"]/div/a')

        for job in jobList:
            name =  job.xpath("./div[1]/div[1]/span[1]/text()").extract_first() 
    ...salary***,company***,....
            info = job.xpath("./div[3]/div[1]//text()").extract_first()


            item = ZlzpItem(name=name,salary=salary,company=company,adress=adress,experience=experience,eduBack=eduBack,companyType=companyType,scale=scale,info=info)
            yield item

3、分页:

        next_url = 'https://sou.zhaopin.com/?jl=736&kw=web%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%B8%88&p={}'.format(count)
        if count == 34:
            return None # 设置程序停止的条件
        if next_url:
            yield scrapy.Request(next_url,callback=self.parse)

定义下载器中间件(DownloadMiddleware):myDownloadMiddleware.py

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
...
class ZlzpDownloaderMiddleware:
    def __init__(self):
        self.driver = webdriver.Chrome()
    def process_request(self, request, spider):
        self.driver.get(request.url)
        time.sleep(3) # 休息3s
  # 设置显示等待,由于需要登录,我们手机扫码登录,知道页面出现(即url显示为firstpageurl)
        WebDriverWait(self.driver, 1000).until(
            EC.url_contains(request.url)
        )
        time.sleep(6) # 登录成功之后页面需要时间加载出来,再休息几秒
        return HtmlResponse(url=self.driver.current_url, body=self.driver.page_source, encoding="utf-8",
                            request=request)  # 然后把这个response对象返回给爬虫(zl.py)

说明:

  1. selenium集成到scrapy中的核心就是在爬虫中间件中拦截请求,把处理后的响应对象返回,对应于爬虫文件(这里的zl.py)parse函数中的response,如果不集成selenium,那么response对象不能很好应对网站的反爬.

  2. 此处的parse_request方法中只有少量的selenium代码,因为动态操作其实不多.

  3. 重点:return后面的response对象:

在这里我们不能return None,如果return None,那么请求会被发送到下载中间件去下载这个页面,在将这个页面的response返回给spider(hr.py)。但是我们上面browser.get的时候就已经下载了这个页面的内容,所以没有必要在下载一次,我们只要制定一个response对象,直接返回这个response给spider即可

定义管道(Pipeline):pipelines.py

from itemadapter import ItemAdapter
import csv

class ZlzpPipeline:
    def __init__(self):
        self.f = open('zlJob.csv', 'w', encoding='utf-8', newline='')
        # self.file_name = ['name','upTime','salary','needs','welfare','company','scale','types']
        self.file_name = ['name','salary','company','adress','experience','eduBack','companyType','scale','info'] 
        self.writer = csv.DictWriter(self.f, fieldnames=self.file_name)
        self.writer.writeheader()

    def process_item(self, item, spider):
        self.writer.writerow(dict(item))# 写入spider传过来的具体数值
        return item # 写入完返回

    def close_spider(self, spider):
        self.f.close()

settings.py配置

BOT_NAME = 'zlzp'

SPIDER_MODULES = ['zlzp.spiders']
NEWSPIDER_MODULE = 'zlzp.spiders'
LOG_LEVEL = 'WARNING'
......
ROBOTSTXT_OBEY = False

......
DEFAULT_REQUEST_HEADERS = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.106 Safari/537.36',
  'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  'Accept-Language': 'en',
}
......
DOWNLOADER_MIDDLEWARES = {
   'zlzp.middlewares.ZlzpDownloaderMiddleware': 543,
}
......
ITEM_PIPELINES = {
   'zlzp.pipelines.ZlzpPipeline': 300,
}
......

......表示注释代码,这里省略。

三、程序运行

命令行键入:

scrapy crawl hr

pic1:运行程序结束到第34页,对应count = 34

实战案例 | Scrapy 集成Selenium爬取智联招聘数据_第5张图片

pic02:(csv文件)

实战案例 | Scrapy 集成Selenium爬取智联招聘数据_第6张图片

四、数据简单分析

查看数据

import pandas as pd
df = pd.read_csv('./zlJob.csv')
df.head()
实战案例 | Scrapy 集成Selenium爬取智联招聘数据_第7张图片

薪资饼图展示

c = (
    Pie(init_opts=opts.InitOpts(bg_color="white"))
    .add("", [list(z) for z in zip(typesX,number)])   # zip函数两个部分组合在一起list(zip(x,y))-----> [(x,y)]
    .set_global_opts(title_opts=opts.TitleOpts(title="类型:"))  # 标题
    .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}"))  # 数据标签设置
)

c.render_notebook()  
实战案例 | Scrapy 集成Selenium爬取智联招聘数据_第8张图片

经验要求柱图展示

from pyecharts.charts import Bar
bar = Bar()
bar.add_xaxis(['3-5年', '1-3年', '不限', '5-10年', '无经验', '1年以下', '10年以上'])
bar.add_yaxis('经验要求',[462,329,83,78,19,15,4])
bar.render()
实战案例 | Scrapy 集成Selenium爬取智联招聘数据_第9张图片

学历要求柱图展示

c = (
    Pie(init_opts=opts.InitOpts(bg_color="white"))
    .add("", [list(z) for z in zip(educationTypes,number)])   # zip函数两个部分组合在一起list(zip(x,y))-----> [(x,y)]
    .set_global_opts(title_opts=opts.TitleOpts(title="类型:"))  # 标题
    .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}"))  # 数据标签设置
)

c.render_notebook()  
实战案例 | Scrapy 集成Selenium爬取智联招聘数据_第10张图片

大多数要求本科学历,或者说大专及以上学历。

公司类型柱图展示

c = (
    Pie(init_opts=opts.InitOpts(bg_color="white"))
    .add("", [list(z) for z in zip(companyTypes,number)])   # zip函数两个部分组合在一起list(zip(x,y))-----> [(x,y)]
    .set_global_opts(title_opts=opts.TitleOpts(title="类型:"))  # 标题
    .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}"))  # 数据标签设置
)

c.render_notebook()  
实战案例 | Scrapy 集成Selenium爬取智联招聘数据_第11张图片

可以看到大多数公司是民营或者上市公司。

五、总结

页面翻页处理,由于我们只是使用selenium就是打开网页请求数据,所以一般在爬虫文件中进行翻页处理,如果对应的下一页的a标签的href属性不是下一页的页面url,我们需要设置动态全局变量,构建动态的url。

下载中间件中设置的selenium的相关操作,动态点击,页面滚轮操作,显隐式等待等等,重要的是返回的response对象,这个是集成selenimu到scrapy的核心,在下载中间件中拦截请求,把处理后的response对象返回给爬虫。

实战案例 | Scrapy 集成Selenium爬取智联招聘数据_第12张图片

                                         分享给更多朋友,转发,点赞,在看

你可能感兴趣的:(定位,selenium,python,web,css)