基于Spider异步爬虫框架+JS动态参数逆向+隧道代理+自定义中间件的猎聘招聘数据爬取
在本篇博客中,我们将介绍如何使用 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 被封锁,我们使用了快代理提供的代理服务器。代理认证信息包括 username
和 password
,通过将这些信息构造为代理 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.urlyield itemexcept 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) + 1if retry_times <= self.max_retry_times:retryreq = self._retry(request, exception, spider)if retryreq is not None:retryreq.meta['retry_times'] = retry_timesreturn 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 itemdef close_spider(self, spider):self.file.close()
将爬取失败的代码采用selenium进行重采(登录后)
代码的主要流程如下:
-
读取失败的请求URL:
我们从一个文件failed_requests.txt
中读取之前失败的URL,并存储在failed_requests
列表中。 -
配置Selenium WebDriver:
我们使用Chrome浏览器作为Selenium的驱动程序,通过webdriver.Chrome()
初始化浏览器实例。设置隐式等待时间(implicitly_wait(3)
)来处理网页加载的延迟。 -
处理每个失败的请求:
对于每个失败的URL,我们重新访问该页面。为了避免频繁的登录,我们使用一个标志has_logged_in
来判断是否已经登录。如果没有登录,我们手动提示登录操作。 -
提取网页数据:
使用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进行提取。
- 职位标题:
-
处理提取错误:
使用try-except
语句来处理数据提取过程中可能出现的错误,避免程序中断。即使某个字段没有数据,代码仍然会继续运行,确保尽可能多地提取到有效数据。 -
将数据写入CSV:
提取的数据被按行写入CSV文件,包含公司名称、职位信息、薪资、工作要求等字段。 -
关闭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 爬虫的其他技术和最佳实践感兴趣,欢迎关注本博客。
需要源代码的添加我
相关文章:

基于Spider异步爬虫框架+JS动态参数逆向+隧道代理+自定义中间件的猎聘招聘数据爬取
在本篇博客中,我们将介绍如何使用 Scrapy 框架结合 JS 逆向技术、代理服务器和自定义中间件,来爬取猎聘网站的招聘数据。猎聘是一个国内知名的招聘平台,提供了大量的企业招聘信息和职位信息。本项目的目标是抓取指定城市的招聘信息࿰…...
Spring 中的 BeanDefinitionParserDelegate 和 NamespaceHandler
一、BeanDefinitionParserDelegate Spring在解析xml文件的时候,在遇到<bean>标签的时候,我们会使用BeanDefinitionParserDelegate对象类解析<bean>标签的内容,包括<bean>标签的多个属性,例如 id name class in…...
BERT模型核心组件详解及其实现
摘要 BERT(Bidirectional Encoder Representations from Transformers)是一种基于Transformer架构的预训练模型,在自然语言处理领域取得了显著的成果。本文详细介绍了BERT模型中的几个关键组件及其实现,包括激活函数、变量初始化…...

图论-代码随想录刷题记录[JAVA]
文章目录 前言深度优先搜索理论基础所有可达路径岛屿数量岛屿最大面积孤岛的总面积沉默孤岛Floyd 算法dijkstra(朴素版)最小生成树之primkruskal算法 前言 新手小白记录第一次刷代码随想录 1.自用 抽取精简的解题思路 方便复盘 2.代码尽量多加注释 3.记录…...

c#加载shellcode
本地加载bin文件 SharpPELoader项目如下: using System; using System.IO; using System.Runtime.InteropServices;namespace TestShellCode {internal class Program{private const uint MEM_COMMIT 0x1000;private const uint PAGE_EXECUTE_READWRITE 0x40;pr…...

HarmonyOS 开发环境搭建
HarmonyOS(鸿蒙操作系统)作为一种面向全场景多设备的智能操作系统,正逐渐在市场上崭露头角。为了进入HarmonyOS生态,开发者需要搭建一个高效的开发环境。本文将详细介绍如何搭建HarmonyOS开发环境,特别是如何安装和配置…...
【网络云计算】2024第46周周考-磁盘管理的基础知识-RAID篇
文章目录 1、画出各个RAID的结构图,6句话说明优点和缺点,以及磁盘可用率和坏盘数量,磁盘总的数量2、写出TCP五层模型以及对应的常用协议 【网络云计算】2024第46周周考-磁盘管理的基础知识-RAID篇 1、画出各个RAID的结构图,6句话说…...
深入理解 SQL_MODE 之 ANSI_QUOTES
引言 在 MySQL 数据库中,sql_mode 是一个重要的配置参数,它定义了 MySQL 应该遵循的 SQL 语法标准以及数据验证规则。其中,ANSI_QUOTES 是 sql_mode 中的一个重要选项,它改变了 MySQL 对于字符串和标识符的识别方式,使…...
容器技术在持续集成与持续交付中的应用
💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 容器技术在持续集成与持续交付中的应用 容器技术在持续集成与持续交付中的应用 容器技术在持续集成与持续交付中的应用 引言 容器…...

【嵌入式软件-STM32】OLED显示屏+调试方法
目录 一、调试方式 1)串口调试 优势 弊端 2)显示屏调试 优势 弊端 3)Keil调试模式 4)点灯调试法 5)注释调试法 6)对照法 二、OLED简介 OLED组件 OLED显示屏 0.96寸OLED模块 OLED外观和种类…...

kubernetes简单入门实战
本章将介绍如何在kubernetes集群中部署一个nginx服务,并且能够对其访问 Namespace Namespace是k8s系统中一个非常重要的资源,它的主要作用是用来实现多套环境的资源隔离或者多租户的资源隔离。 默认情况下,k8s集群中的所有的Pod都是可以相…...
Python连接Mysql、Postgre、ClickHouse、Redis常用库及封装方法
博主在这里分享一些常见的python连接数据库或中间件的库和封装方案,希望对大家有用。 Mysql封装 #!/usr/bin/python # -*- coding: utf-8 -*- import sys import pymysql from settings import MYSQL_DB, MYSQL_PORT, MYSQL_USER, MYSQL_PASSWORD, MYSQL_HOST, EN…...

如何修改npm包
前言 开发中遇到一个问题,配置 Element Plus 自定义主题时,添加了 ElementPlusResolver({ importStyle: "sass" }) 后,控制台出现报错,这是因为 Dart Sass 2.0 不再支持使用 !global 来声明新变量,虽然当前…...

Django 2024全栈开发指南(三):数据库模型与ORM操作(上篇)
目录 一、模型的定义二、数据迁移三、数据表关系四、数据表操作4.1 Shell工具4.2 数据新增4.3 数据修改4.4 数据删除4.5 数据查询 Django 对各种数据库提供了很好的支持,包括 PostgreSQL、MySQL、SQLite 和 Oracle,而且为这些数据库提供了统一的 API 方法…...

低代码可视化-uniapp开关选择组件-低码生成器
开关(Switch)选择组件是一种用户界面元素,允许用户在两种状态(通常是开/关、是/否、启用/禁用等)之间进行切换。这种组件在移动应用、桌面软件、网页以及物联网设备中广泛应用。以下是对开关Switch选择组件的详细介绍&…...

【arxiv‘24】Vision-Language Navigation with Continual Learning
论文信息 题目:Vision-Language Navigation with Continual Learning 视觉-语言导航与持续学习 作者:Zhiyuan Li, Yanfeng Lv, Ziqin Tu, Di Shang, Hong Qiao 论文创新点 VLNCL范式:这是一个新颖的框架,它使得智能体能够在适…...

如何在 Ubuntu 上安装 Jupyter Notebook
本篇文章将教你在 Ubuntu 服务器上安装 Jupyter Notebook,并使用 Nginx 和 SSL 证书进行安全配置。 我将带你一步步在云服务器上搭建 Jupyter Notebook 服务器。Jupyter Notebook 在数据科学和机器学习领域被广泛用于交互式编码、可视化和实验。在远程服务器上运行…...

免费申请 Let‘s Encrypt SSL 证书
免费申请 Lets Encrypt SSL 证书 在网络安全日益重要的今天,为网站启用 SSL 证书是保障数据安全和用户信任的关键。Lets Encrypt 提供的免费 SSL 证书是一个很好的选择。下面我们详细介绍如何为网站域名申请该证书。 一、准备工作 域名 确保已注册要使用 SSL 证书的…...
【JAVA】Java基础—面向对象编程:继承—重写父类方法
在Java开发中,重写(Override)是面向对象编程(OOP)中的一个重要概念。它允许子类提供父类方法的具体实现,从而改变或扩展父类的行为。重写是实现多态性的重要手段,使得程序在运行时能够根据对象的…...

【C++初阶】C++入门
1、C第一个程序 C是脱胎于C语言的,所以也包含了C语言绝大多数的内容,C兼容C语言绝大多数的语法,在C语言中能实现的程序在C中也是可以执行的,但需要将定义文件代码的后缀改为.cpp 就比如hello world程序 // test.cpp #include<stdio.h&g…...

华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...

uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...

如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

群晖NAS如何在虚拟机创建飞牛NAS
套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...

【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...

论文阅读:Matting by Generation
今天介绍一篇关于 matting 抠图的文章,抠图也算是计算机视觉里面非常经典的一个任务了。从早期的经典算法到如今的深度学习算法,已经有很多的工作和这个任务相关。这两年 diffusion 模型很火,大家又开始用 diffusion 模型做各种 CV 任务了&am…...