基于Spider异步爬虫框架+JS动态参数逆向+隧道代理+自定义中间件的猎聘招聘数据爬取

news2024/11/15 10:49:52

在本篇博客中,我们将介绍如何使用 Scrapy 框架结合 JS 逆向技术、代理服务器和自定义中间件,来爬取猎聘网站的招聘数据。猎聘是一个国内知名的招聘平台,提供了大量的企业招聘信息和职位信息。本项目的目标是抓取指定城市的招聘信息,提取相关的职位名称、薪资、公司名称等信息。

项目结构

项目的基本结构如下:

liepin/
├── liepin/
│   ├── items.py          # 定义Item模型
│   ├── pipelines.py      # 定义数据处理管道
│   ├── settings.py       # Scrapy的配置文件
│   ├── spiders/
│   │   └── lp_spider.py  # 爬虫代码
├── lp.js                 # JS逆向代码
└── data.csv              # 存储爬取的招聘数据
  • lp_spider.py:定义了爬虫的核心逻辑,负责发起请求、解析数据并将数据传递给 pipeline 进行处理。
  • middlewares.py:自定义了爬虫和下载器中间件,处理请求的重试逻辑和代理。
  • pipelines.py:定义了数据存储的逻辑,将爬取的数据保存为 CSV 文件。
  • settings.py:配置 Scrapy 框架的各项设置。
  • lp.js:包含 JS 代码,用于生成请求所需的参数。

步骤 1: 配置代理和用户身份认证

在爬虫开始前,我们首先需要配置代理服务器。为了防止 IP 被封锁,我们使用了快代理提供的代理服务器。代理认证信息包括 usernamepassword,通过将这些信息构造为代理 URL:

# 隧道域名:端口号
tunnel = "xxxx"

# 用户名密码方式
username = "xxxx"
password = "xxxx"

proxy_url = f"http://{username}:{password}@{tunnel}"

然后,我们将在后续请求中使用该代理 URL。

步骤 2: 使用 JS 逆向技术生成请求参数

猎聘的 API 请求需要一个动态生成的 ckId 参数,这是通过执行 JavaScript 代码来生成的。为了获取这个参数,我们通过 Python 的 execjs 库来执行 JS 代码。

js_file = open('../lp.js', mode='r', encoding='utf-8').read()
js_code = execjs.compile(js_file)
ckId = js_code.call('r', 32)

其中,r 是 JS 代码中的一个函数,用于生成一个随机的 ckId 值。这样可以模拟正常用户请求,避免被反爬虫机制拦截。

步骤 3: 编写爬虫逻辑

爬虫的核心是发起请求并解析返回的数据。我们定义了一个名为 LpSpider 的爬虫类,继承自 Scrapy 的 Spider 类。

class LpSpider(scrapy.Spider):
    name = "lp"
    custom_settings = {
        'RETRY_ENABLED': True,
        'RETRY_TIMES': 3,
        'RETRY_HTTP_CODES': []
    }

    def start_requests(self):
        for city in city_list:
            for key in key_word:
                for page in range(0, 10):
                    js_code = execjs.compile(js_file)
                    ckId = js_code.call('r', 32)
                    
                    data = { ... }  # 构造请求数据
                    json_data = json.dumps(data)
                    
                    url = 'https://api-c.liepin.com/api/com.liepin.searchfront4c.pc-search-job'
                    
                    headers = { ... }  # 请求头部信息
                    
                    yield scrapy.Request(url=url, method='POST', headers=headers, body=json_data, callback=self.parse, meta={'proxy': proxy_url})

步骤 4: 解析响应数据

当服务器返回职位数据时,我们需要提取相关信息。主要的数据包括职位名称、薪资、公司信息等。在 parse 方法中,我们从 JSON 响应中提取数据,并将每个职位的详情链接传递给 parse_details_page 方法。

def parse(self, response):
    data = json.loads(response.body)
    job_card_list = data['data']['data']['jobCardList']
    for job_card in job_card_list:
        job_link = job_card['job']['link']
        yield scrapy.Request(url=job_link, callback=self.parse_details_page, meta={'proxy': proxy_url})

步骤 5: 解析职位详情页

在职位详情页中,我们进一步提取职位的详细信息,如公司介绍、职位描述等。

def parse_details_page(self, response):
    try:
        item = LiepinItem()
        item['title'] = response.xpath('//span[@class="name ellipsis-2"]/text()').get()
        item['salary'] = response.xpath('//span[@class="salary"]/text()').get()
        item['company_name'] = response.xpath('//div[@class="name ellipsis-1"]/text()').get()
        item['company_intro'] = response.xpath('//div[@class="inner ellipsis-3"]/text()').get()
        item['job_intro'] = response.xpath('//dd[@data-selector="job-intro-content"]/text()').get()
        item['job_loca'] = response.xpath('/html/body/section[3]/div[1]/div[3]/span[1]/text()').get()
        item['job_exp'] = response.xpath('/html/body/section[3]/div[1]/div[3]/span[3]/text()').get()
        item['job_educate'] = response.xpath('/html/body/section[3]/div[1]/div[3]/span[5]/text()').get()
        
        key_word_elements = response.xpath('//div[@class="tag-box"]/ul/li/text()')
        item['key_word_list'] = [kw.get() for kw in key_word_elements]
        
        item['company_info'] = [
            response.xpath('//div[@class="company-other"]/div[1]/span[@class="text"]/text()').get(),
            response.xpath('//div[@class="company-other"]/div[2]/span[@class="text"]/text()').get(),
            response.xpath('//div[@class="company-other"]/div[3]/span[@class="text"]/text()').get(),
        ]
        
        item['details_url'] = response.url
        yield item
    except Exception as e:
        self.logger.error(f"An error occurred: {e}")

步骤 6: 配置重试和延迟

由于爬虫在运行时可能会遇到网络错误或被目标网站屏蔽,因此我们需要实现请求的重试逻辑。我们通过自定义重试中间件来实现该功能。

class CustomRetryMiddleware(RetryMiddleware):
    def process_exception(self, request, exception, spider):
        if isinstance(exception, self.EXCEPTIONS_TO_RETRY):
            retry_times = request.meta.get('retry_times', 0) + 1
            if retry_times <= self.max_retry_times:
                retryreq = self._retry(request, exception, spider)
                if retryreq is not None:
                    retryreq.meta['retry_times'] = retry_times
                    return retryreq

步骤 7: 数据存储

抓取到的职位信息通过 Scrapy 的 pipeline 存储到 CSV 文件中。我们定义了一个 LiepinPipeline 类来处理数据存储。

class LiepinPipeline:
    def __init__(self):
        self.file = open('data.csv', 'a', newline='', encoding='utf-8')
        self.writer = csv.writer(self.file)
        self.writer.writerow(['Job Name', 'Salary', 'Company Name', 'Company Intro', 'Job Intro', 'Job Location', 'Job Experience', 'Job Education', 'Key Word List', 'Company Info', 'Details URL'])

    def process_item(self, item, spider):
        self.writer.writerow([item['title'], item['salary'], item['company_name'], item['company_intro'], item['job_intro'], item['job_loca'], item['job_exp'], item['job_educate'], item['key_word_list'], item['company_info'], item['details_url']])
        return item

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

将爬取失败的代码采用selenium进行重采(登录后)

代码的主要流程如下:

  1. 读取失败的请求URL
    我们从一个文件failed_requests.txt中读取之前失败的URL,并存储在failed_requests列表中。

  2. 配置Selenium WebDriver
    我们使用Chrome浏览器作为Selenium的驱动程序,通过webdriver.Chrome()初始化浏览器实例。设置隐式等待时间(implicitly_wait(3))来处理网页加载的延迟。

  3. 处理每个失败的请求
    对于每个失败的URL,我们重新访问该页面。为了避免频繁的登录,我们使用一个标志has_logged_in来判断是否已经登录。如果没有登录,我们手动提示登录操作。

  4. 提取网页数据
    使用Selenium获取当前页面的源代码后,使用lxml.etree解析HTML。通过XPath选择器,我们提取职位的相关信息,如职位标题、薪资、公司介绍、职位要求等。

    • 职位标题tree.xpath('/html/body/section[3]/div[1]/div[1]/span[1]/span/text()')
    • 薪资tree.xpath('//span[@class="salary"]/text()')
    • 公司信息:包括公司名称、公司介绍等,均通过XPath进行提取。
  5. 处理提取错误
    使用try-except语句来处理数据提取过程中可能出现的错误,避免程序中断。即使某个字段没有数据,代码仍然会继续运行,确保尽可能多地提取到有效数据。

  6. 将数据写入CSV
    提取的数据被按行写入CSV文件,包含公司名称、职位信息、薪资、工作要求等字段。

  7. 关闭WebDriver
    在所有请求处理完毕后,调用driver.quit()关闭浏览器实例,释放资源。

代码示例
# 从文件中读取失败的请求URL
with open('failed_requests.txt') as f:
    failed_requests = [line.strip() for line in f.readlines()]

# 设置Selenium WebDriver
driver = webdriver.Chrome()
driver.implicitly_wait(3)

has_logged_in = False

# 打开CSV文件用于写入数据
with open('failed_requests_data.csv', mode='w', newline='', encoding='utf-8') as file:
    writer = csv.writer(file)
    # 写入表头
    writer.writerow(
        ['Company Name', 'Company Intro', 'Job Intro', 'Job Location', 'Job Experience', 'Job Education', 'Title',
         'Salary', 'Key Words', 'Company Info', 'Details URL'])

    for failed_request in failed_requests:
        driver.get(failed_request)
        if not has_logged_in:
            input('请登录')
            has_logged_in = True

            # 提取数据
            title = tree.xpath('/html/body/section[3]/div[1]/div[1]/span[1]/span/text()')[0] if tree.xpath('/html/body/section[3]/div[1]/div[1]/span[1]/span/text()') else ''
            salary = tree.xpath('//span[@class="salary"]/text()')[0] if tree.xpath('//span[@class="salary"]/text()') else ''
            company_name = tree.xpath('//div[@class="name ellipsis-1"]/text()')[0] if tree.xpath('//div[@class="name ellipsis-1"]/text()') else ''
            company_intro = tree.xpath('//div[@class="inner ellipsis-3"]/text()')[0] if tree.xpath('//div[@class="inner ellipsis-3"]/text()') else ''
            job_intro = tree.xpath('//dd[@data-selector="job-intro-content"]/text()')[0] if tree.xpath('//dd[@data-selector="job-intro-content"]/text()') else ''
            job_loca = tree.xpath('/html/body/section[3]/div[1]/div[3]/span[1]/text()')[0] if tree.xpath('/html/body/section[3]/div[1]/div[3]/span[1]/text()') else ''
           
# 关闭WebDriver
driver.quit()
代码说明
  • WebDriverWait:用于等待网页中的某个元素加载完成,避免程序在页面未加载完毕时就进行数据提取。
  • XPath提取tree.xpath()用于从HTML中提取相关数据,XPath的使用使得提取过程更加灵活和精确。
  • CSV写入:提取到的数据被写入到CSV文件中,方便后续分析。

最后爬取的数据结果

在这里插入图片描述

总结

本项目通过 Scrapy 框架结合 JS 逆向技术和自定义中间件,成功地爬取了猎聘招聘平台的数据,并存储在本地 CSV 文件中。重试机制和代理设置保证了爬虫的稳定性和反爬虫防护。该方案适用于类似需要绕过反爬虫机制的招聘网站或其他数据来源。

如果你对 Web 爬虫的其他技术和最佳实践感兴趣,欢迎关注本博客。

需要源代码的添加我

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2240784.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机网络 (2)计算机网络的类别

计算机网络的类别繁多&#xff0c;根据不同的分类原则&#xff0c;可以得到各种不同类型的计算机网络。 一、按覆盖范围分类 局域网&#xff08;LAN&#xff09;&#xff1a; 定义&#xff1a;局域网是一种在小区域内使用的&#xff0c;由多台计算机组成的网络。覆盖范围&#…

超好用shell脚本NuShell mac安装

利用管道控制任意系统 Nu 可以在 Linux、macOS 和 Windows 上运行。一次学习&#xff0c;处处可用。 一切皆数据 Nu 管道使用结构化数据&#xff0c;你可以用同样的方式安全地选择&#xff0c;过滤和排序。停止解析字符串&#xff0c;开始解决问题。 强大的插件系统 具备强…

【C#设计模式(9)——组合模式(Component Pattern)】

前言 组合模型是将对象组合成树形结构以表示“整体-部分”的层次结构&#xff0c;使客户终端代码更加简洁和灵活。 代码 //目录抽象类 public abstract class Directory {protected string _name;public Directory(string name){_name name;}public abstract void Show(); } …

Ubuntu下Xshell连接腾讯云服务器

1.在腾讯云上买好服务器后&#xff0c;找到控制台&#xff0c;找到自己的服务器重置密码&#xff0c;默认用户名时ubuntu 2.在Xshell连接服务器 然后出现一个秘钥接受&#xff0c;直接接受就好了&#xff0c;然后就出现下面 然后就可以了 查看当前登录的用户 whoami 查看当前服…

初识算法 · 位运算(3)

目录 前言&#xff1a; 两整数之和 题目解析 算法原理 算法编写 只出现一次的数字II 题目解析 算法原理 算法编写 前言&#xff1a; ​本文的主题是位运算&#xff0c;通过两道题目讲解&#xff0c;一道是只出现一次的数字II&#xff0c;一道是两整数之和。 链接分别…

微信小程序——01开发前的准备和开发工具

文章目录 一、开发前的准备1注册小程序账号2安装开发者工具 二、开发者工具的使用1创建项目2 工具的使用3目录结构4各个页面之间的关系5 权限管理6提交审核和发布 一、开发前的准备 开发前需要进行以下准备&#xff1a; 1 注册小程序账号2激活邮箱3 信息登记4 登录小程序管理后…

使用热冻结数据层生命周期优化在 Elastic Cloud 中存储日志的成本

作者&#xff1a;来自 Elastic Jonathan Simon 收集数据对于可观察性和安全性至关重要&#xff0c;而确保数据能够快速搜索且获得低延迟结果对于有效管理和保护应用程序和基础设施至关重要。但是&#xff0c;存储所有这些数据会产生持续的存储成本&#xff0c;这为节省成本创造…

记录配置ubuntu18.04下运行ORBSLAM3的ros接口的过程及执行单目imu模式遇到的问题(详细说明防止忘记)

今天的工作需要自己录制的数据集来验证昨天的标定结果 用ORBSLAM3单目imu模式运行&#xff0c;mentor给的是一个rosbag格式的数据包&#xff0c;配置过程出了几个问题记录一下&#xff0c;沿配置流程写。 一.orbslam3编译安装 1.首先是安装各种依赖 这里不再赘述&#xff0…

vue2项目启用tailwindcss - 开启class=“w-[190px] mr-[20px]“ - 修复tailwindcss无效的问题

效果图 步骤 停止编译"npm run dev"安装依赖 npm install -D tailwindcssnpm:tailwindcss/postcss7-compat postcss^7 autoprefixer^9 创建文件/src/assets/tailwindcss.css&#xff0c;写入内容&#xff1a; tailwind base; tailwind components; tailwind utiliti…

实施工程师简历「精选篇」

【#实施工程师简历#】一份出色的实施工程师简历&#xff0c;是获得优质工作机会的重要跳板。那么&#xff0c;如何打造一份令人眼前一亮的实施工程师简历呢&#xff1f;以下是幻主简历网整理的实施工程师简历「精选篇」&#xff0c;欢迎大家阅读收藏&#xff01; 实施工程师简历…

Linux篇(权限管理命令)

目录 一、权限概述 1. 什么是权限 2. 为什么要设置权限 3. Linux中的权限类别 4. Linux中文件所有者 4.1. 所有者分类 4.2. 所有者的表示方法 属主权限 属组权限 其他权限 root用户&#xff08;超级管理员&#xff09; 二、普通权限管理 1. ls查看文件权限 2. 文件…

惊爆!72.1K star 的 Netdata:实时监控与可视化的超炫神器!

在当今复杂的 IT 环境中&#xff0c;实时监控与可视化对于保障系统的稳定运行和性能优化至关重要。 无论是服务器、应用程序&#xff0c;还是网络设备&#xff0c;及时获取性能数据能够帮助我们快速定位问题、优化资源配置。 Netdata&#xff0c;作为一个开源的实时监控工具&a…

姓名改成商标名称,李子柒已成身份证名字!

近日李子柒紫气东来&#xff0c;以中国非物质文化遗产“漆器”生动地展现了中国漆器的独特美学和工艺之美&#xff0c;这条视频在微博已超过1.3亿观看&#xff0c;在国外视频平台订阅超二千万粉丝成海外中文创作第一人。 李子柒原名李佳佳&#xff0c;在网上看到她已经正式将身…

论云游戏的性能与性价比,ToDesk、青椒云、顺网云游戏等具体实操看这篇就够了

文章目录 一、前言二、云电脑产品基础介绍2.1 ToDesk云电脑2.1.1 ToDesk云电脑硬件参数2.1.2 ToDesk云电脑鲁大师跑分2.1.3 ToDesk云电脑收费方式2.1.4 ToDesk云电脑特色功能 2.2 青椒云2.2.1 青椒云游戏娱乐硬件配置2.2.2 青椒云云电脑鲁大师跑分2.2.3 青椒云收费方式2.2.4 青…

服务器数据恢复——Ext4文件系统使用fsck后mount不上的数据恢复案例

关于Ext4文件系统的几个概念&#xff1a; 块组&#xff1a;Ext4文件系统的全部空间被划分为若干个块组&#xff0c;每个块组结构基本上相同。 块组描述符表&#xff1a;每个块组都对应一个块组描述符&#xff0c;这些块组描述符统一放在文件系统的前部&#xff0c;称为块组描述…

ubuntu 下mosquitto TLS配置

1、/etc/mosquitto/mosquitto.conf文件配置 persistence true persistence_location /var/lib/mosquitto/ log_dest file /var/log/mosquitto/mosquitto.log include_dir /etc/mosquitto/conf.d listener 1883 listener 8883 0.0.0.0 password_file /etc/mosquitto/pwfile cert…

隧道论文阅读2-采用无人融合扫描数据的基于深度学习的垂直型隧道三维数字损伤图

目前存在的问题&#xff1a; 需要开发新的无人测量系统测量垂直隧道图像数据量巨大&#xff0c;基于深度学习完成损伤评估跟踪获取图像位置的困难&#xff0c;对大型基础设施感兴趣区域(roi)的2d和3d地图建立进行了研究&#xff0c;对整个目标结构的损伤定位仍然具有挑战性。为…

【C++动态规划 最长公共子序列】1035. 不相交的线|1805

本文涉及知识点 C动态规划 LeetCode1035. 不相交的线 在两条独立的水平线上按给定的顺序写下 nums1 和 nums2 中的整数。 现在&#xff0c;可以绘制一些连接两个数字 nums1[i] 和 nums2[j] 的直线&#xff0c;这些直线需要同时满足&#xff1a; nums1[i] nums2[j] 且绘制的…

SELINUX和防火墙

SELINUX 简介&#xff1a; SELinux通过对进程和文件的访问进行精细的控制和限制&#xff0c;可以在系统级别上实现强制访问控制&#xff0c;即使是root用户也无法绕过这种控制。它基于策略规则来确定哪些进程可以访问哪些文件&#xff0c;从而有效地减少了系统遭受恶意攻击的…

计算机毕业设计Python+大模型农产品推荐系统 农产品爬虫 农产品商城 农产品大数据 农产品数据分析可视化 PySpark Hadoop

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…