前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >实战案例 | Scrapy 集成Selenium爬取智联招聘数据

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

作者头像
快学Python
发布2021-08-09 14:52:04
1.3K0
发布2021-08-09 14:52:04
举报
文章被收录于专栏:快学Python快学Python

人生苦短,快学Python!

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

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

一、需求分析

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

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

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

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

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

  1. name 职位名称
  2. salary 薪资
  3. adress 地区
  4. experience 经验
  5. eduBack 教育背景
  6. company 公司名称
  7. companyType 公司类型
  8. scale 公司规模
  9. info 简介

二、scrapy项目文件配置

定义items

代码语言:javascript
复制
import scrapy

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

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

代码语言:javascript
复制
#这里先说明下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、初始化设置:

代码语言:javascript
复制
# -*- 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函数:

代码语言:javascript
复制
    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、分页:

代码语言:javascript
复制
        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

代码语言:javascript
复制
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

代码语言:javascript
复制
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配置

代码语言:javascript
复制
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,
}
......

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

三、程序运行

命令行键入:

代码语言:javascript
复制
scrapy crawl hr

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

pic02:(csv文件)

四、数据简单分析

查看数据

代码语言:javascript
复制
import pandas as pd
df = pd.read_csv('./zlJob.csv')
df.head()

薪资饼图展示

代码语言:javascript
复制
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()  

经验要求柱图展示

代码语言:javascript
复制
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()

学历要求柱图展示

代码语言:javascript
复制
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()  

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

公司类型柱图展示

代码语言:javascript
复制
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()  

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

五、总结

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

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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-07-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 快学Python 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、需求分析
  • 二、scrapy项目文件配置
  • 三、程序运行
  • 四、数据简单分析
  • 五、总结
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档