java 爬取51job招聘信息

2024-01-08 05:20
文章标签 java 信息 爬取 招聘 51job

本文主要是介绍java 爬取51job招聘信息,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本案例是基于webmagic和jsoup对51job招聘信息的爬取,并将爬取到的数据存入mysql数据库中。

Jsoup

jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。

jsoup的主要功能如下:

  1. 从一个URL,文件或字符串中解析HTML;
  2. 使用DOM或CSS选择器来查找、取出数据;
  3. 可操作HTML元素、属性、文本;

WebMagic

WebMagic是一个简单灵活的Java爬虫框架。底层基于HttpClient和Jsoup。

WebMagic项目代码分为核心和扩展两部分。核心部分(webmagic-core)是一个精简的、模块化的爬虫实现,而扩展部分则包括一些便利的、实用性的功能。

WebMagic的设计目标是尽量的模块化,并体现爬虫的功能特点。这部分提供非常简单、灵活的API,在基本不改变开发模式的情况下,编写一个爬虫。

扩展部分(webmagic-extension)提供一些便捷的功能,例如注解模式编写爬虫等。同时内置了一些常用的组件,便于爬虫开发。

业务分析

要实现的是爬取https://www.51job.com/上的招聘信息。只爬取java招聘的信息。
在这里插入图片描述


点击职位详情页,我们分析发现详情页还有一些数据需要抓取:
职位、公司名称、工作地点、薪资、发布时间、职位信息、公司联系方式、公司信息
在这里插入图片描述
在这里插入图片描述
根据以上信息,设计数据库表

CREATE TABLE `job_info` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',`company_name` varchar(100) DEFAULT NULL COMMENT '公司名称',`company_addr` varchar(200) DEFAULT NULL COMMENT '公司联系方式',`company_info` text COMMENT '公司信息',`job_name` varchar(100) DEFAULT NULL COMMENT '职位名称',`job_addr` varchar(50) DEFAULT NULL COMMENT '工作地点',`job_info` text COMMENT '职位信息',`salary_min` int(10) DEFAULT NULL COMMENT '薪资范围,最小',`salary_max` int(10) DEFAULT NULL COMMENT '薪资范围,最大',`url` varchar(150) DEFAULT NULL COMMENT '招聘信息详情页',`time` varchar(10) DEFAULT NULL COMMENT '职位最近发布时间',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='招聘信息';

环境搭建

pom.xml 导入jar包坐标

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.0.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>sunyuqi.com</groupId><artifactId>crawlerjob_project</artifactId><version>1.0-SNAPSHOT</version><properties><java.version>9</java.version></properties><dependencies><!--SpringMVC--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--SpringData Jpa--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><!--MySQL连接包--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.11</version></dependency><!--WebMagic核心包--><dependency><groupId>us.codecraft</groupId><artifactId>webmagic-core</artifactId><version>0.7.3</version><exclusions><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId></exclusion></exclusions></dependency><!--WebMagic扩展--><dependency><groupId>us.codecraft</groupId><artifactId>webmagic-extension</artifactId><version>0.7.3</version></dependency><!--WebMagic对布隆过滤器的支持--><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>16.0</version></dependency><!--工具包--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency><!--单元测试--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency></dependencies>
</project>

编写application.properties配置文件

#DB Configuration:
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/crawler?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=qwe123#JPA Configuration:
spring.jpa.database=MySQL
spring.jpa.show-sql=true

编写实体类

package job.domain;import javax.persistence.*;@Entity
@Table(name = "job_info")
public class JobInfo {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String companyName;private String companyAddr;private String companyInfo;private String jobName;private String jobAddr;private String jobInfo;private Integer salaryMin;private Integer salaryMax;private String url;private String time;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getCompanyName() {return companyName;}public void setCompanyName(String companyName) {this.companyName = companyName;}public String getCompanyAddr() {return companyAddr;}public void setCompanyAddr(String companyAddr) {this.companyAddr = companyAddr;}public String getCompanyInfo() {return companyInfo;}public void setCompanyInfo(String companyInfo) {this.companyInfo = companyInfo;}public String getJobName() {return jobName;}public void setJobName(String jobName) {this.jobName = jobName;}public String getJobAddr() {return jobAddr;}public void setJobAddr(String jobAddr) {this.jobAddr = jobAddr;}public String getJobInfo() {return jobInfo;}public void setJobInfo(String jobInfo) {this.jobInfo = jobInfo;}public Integer getSalaryMin() {return salaryMin;}public void setSalaryMin(Integer salaryMin) {this.salaryMin = salaryMin;}public Integer getSalaryMax() {return salaryMax;}public void setSalaryMax(Integer salaryMax) {this.salaryMax = salaryMax;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getTime() {return time;}public void setTime(String time) {this.time = time;}@Overridepublic String toString() {return "JobInfo{" +"id=" + id +", companyName='" + companyName + '\'' +", companyAddr='" + companyAddr + '\'' +", companyInfo='" + companyInfo + '\'' +", jobName='" + jobName + '\'' +", jobAddr='" + jobAddr + '\'' +", jobInfo='" + jobInfo + '\'' +", salaryMin=" + salaryMin +", salaryMax=" + salaryMax +", url='" + url + '\'' +", time='" + time + '\'' +'}';}
}

编写Dao

package job.dao;import job.domain.JobInfo;
import org.springframework.data.jpa.repository.JpaRepository;public interface JobInfoDao extends JpaRepository<JobInfo, Long> {
}

编写Service

Service接口

package job.service;import job.domain.JobInfo;
import org.springframework.data.domain.Page;import java.util.List;public interface JobInfoService {/*** 保存工作信息** @param jobInfo*/public void save(JobInfo jobInfo);/*** 根据条件查询工作信息** @param jobInfo* @return*/public List<JobInfo> findJobInfo(JobInfo jobInfo);/*** 分页查询数据* @param page* @param rows* @return*/Page<JobInfo> findJobInfoByPage(int page, int rows);
}

Service实现类

package job.service.impl;import job.dao.JobInfoDao;
import job.domain.JobInfo;
import job.service.JobInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.List;@Service
public class JobInfoServiceImpl implements JobInfoService {@Autowiredprivate JobInfoDao jobInfoDao;@Override@Transactionalpublic void save(JobInfo jobInfo) {//根据url和发布时间查询数据JobInfo param = new JobInfo();param.setUrl(jobInfo.getUrl());param.setTime(jobInfo.getTime());//执行查询List<JobInfo> list = this.findJobInfo(param);//判断查询结果是否为空if (list.size() == 0) {//如果查询结果为空,表示招聘信息数据不存在,或者已经更新了,需要新增或者更新数据库this.jobInfoDao.saveAndFlush(jobInfo);}}@Overridepublic List<JobInfo> findJobInfo(JobInfo jobInfo) {//设置查询条件Example example = Example.of(jobInfo);//执行查询List list = this.jobInfoDao.findAll(example);return list;}@Overridepublic Page<JobInfo> findJobInfoByPage(int page, int rows) {Page<JobInfo> JobInfos = this.jobInfoDao.findAll(PageRequest.of(page - 1, rows));return JobInfos;}
}

编写引导类

package job;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;@SpringBootApplication
@EnableScheduling//开启定时任务
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}

自定义Pipeline

SpringDataPipeline

package job.task;import job.domain.JobInfo;
import job.service.JobInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import us.codecraft.webmagic.ResultItems;
import us.codecraft.webmagic.Task;
import us.codecraft.webmagic.pipeline.Pipeline;@Component
public class SpringDataPipeline  implements Pipeline {@Autowiredprivate JobInfoService jobInfoService;@Overridepublic void process(ResultItems resultItems, Task task) {//获取封装好的招聘详情对象JobInfo jobInfo = resultItems.get("jobInfo");//判断数据是否不为空if (jobInfo != null) {//如果不为空把数据保存到数据库中this.jobInfoService.save(jobInfo);}}
}

计算薪水工具类

MathSalary.java

package job.task;public class MathSalary {/*** 获取薪水范围* @param salaryStr* @return*/public static Integer[] getSalary(String salaryStr) {if (salaryStr==null || "".equals(salaryStr)){return new Integer[]{0,0};}Integer[] salary = new Integer[2];String date = salaryStr.substring(salaryStr.length() - 1, salaryStr.length());if (!"月".equals(date) && !"年".equals(date)) {salaryStr = salaryStr.substring(0, salaryStr.length() - 2);salary[0] = salary[1] = str2Num(salaryStr, 365);return salary;}String unit = salaryStr.substring(salaryStr.length() - 3, salaryStr.length() - 2);String[] salarys = salaryStr.substring(0, salaryStr.length() - 3).split("-");salary[0] = mathSalary(date, unit, salarys[0]);salary[1] = mathSalary(date, unit, salarys[1]);return salary;}private static Integer mathSalary(String date, String unit, String salaryStr) {Integer salary = 0;if ("万".equals(unit)) {salary = str2Num(salaryStr, 10000);} else {salary = str2Num(salaryStr, 1000);}if ("月".equals(date)) {salary = str2Num(salary.toString(), 12);}return salary;}private static int str2Num(String salaryStr, int num) {try {Number result = Float.parseFloat(salaryStr) * num;return result.intValue();} catch (Exception e) {}return 0;}
}

编写爬虫

JobProcessor.java

package job.task;import job.domain.JobInfo;
import org.jsoup.Jsoup;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.processor.PageProcessor;
import us.codecraft.webmagic.scheduler.BloomFilterDuplicateRemover;
import us.codecraft.webmagic.scheduler.QueueScheduler;
import us.codecraft.webmagic.selector.Html;
import us.codecraft.webmagic.selector.Selectable;import java.util.List;@Component
public class JobProcessor implements PageProcessor {private int yema=2;private String url = "https://search.51job.com/list/000000,000000,0000,00,9,99,java,2,1.html?lang=c&postchannel=0000&workyear=99&cotype=99&degreefrom=99&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare=";@Overridepublic void process(Page page) {//解析页面,获取招聘信息详情的url地址List<Selectable> list = page.getHtml().regex("\"job_href\":\"(.*?)\"").nodes();//判断获取到的集合是否为空if (list.size() == 0) {// 如果为空,表示这是招聘详情页,解析页面,获取招聘详情信息,保存数据this.saveJobInfo(page);} else {//如果不为空,表示这是列表页,解析出详情页的url地址,放到任务队列中for (Selectable selectable : list) {//获取url地址String jobInfoUrl = selectable.toString();jobInfoUrl = jobInfoUrl.replace("\\","");//把获取到的url地址放到任务队列中page.addTargetRequest(jobInfoUrl);}if (this.yema<100){//下一页的urlString next_url = "https://search.51job.com/list/000000,000000,0000,00,9,99,java,2,"+this.yema+".html?lang=c&postchannel=0000&workyear=99&cotype=99&degreefrom=99&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare=";//把url放到任务队列中page.addTargetRequest(next_url);this.yema = this.yema+1;}}}//解析页面,获取招聘详情信息,保存数据private void saveJobInfo(Page page) {//创建招聘详情对象JobInfo jobInfo  = new JobInfo();//解析页面Html html = page.getHtml();//获取数据,封装到对象中jobInfo.setCompanyName(html.css("p.cname > a","text").toString());String tmp = html.css("p.ltype","title").toString();String[] strings = tmp.split("  |  ");jobInfo.setCompanyAddr(strings[0]);jobInfo.setCompanyInfo(Jsoup.parse(html.css("div.tmsg").toString()).text());jobInfo.setJobName(html.css("div.in > div.cn > h1","text").toString());jobInfo.setJobAddr(html.css("div.bmsg > p.fp","text").toString());jobInfo.setJobInfo(Jsoup.parse(html.css("div.job_msg").toString()).text());jobInfo.setUrl(page.getUrl().toString());//获取薪资Integer[] salary = MathSalary.getSalary(html.css("div.cn strong", "text").toString());jobInfo.setSalaryMin(salary[0]);jobInfo.setSalaryMax(salary[1]);//获取发布时间String time = html.css("p.ltype","title").regex("\\d\\d-\\d\\d发布").toString();jobInfo.setTime(time.substring(0,time.length()-2));//把结果保存起来page.putField("jobInfo",jobInfo);}private Site site = Site.me().setCharset("gbk")//设置编码.setTimeOut(10 * 1000)//设置超时时间.setRetrySleepTime(3000)//设置重试的间隔时间.setRetryTimes(3);//设置重试的次数@Overridepublic Site getSite() {return site;}@Autowiredprivate SpringDataPipeline springDataPipeline;//initialDelay当任务启动后,等等多久执行方法//fixedDelay每个多久执行方法@Scheduled(initialDelay = 1000, fixedDelay = 1000 * 1000)public void process() {Spider.create(new JobProcessor()).addUrl(url).setScheduler(new QueueScheduler().setDuplicateRemover(new BloomFilterDuplicateRemover(100000))).thread(10).addPipeline(this.springDataPipeline).run();}
}

运行引导类即可爬取数据。
爬取的部分数据如下:
在这里插入图片描述
欢迎关注。

这篇关于java 爬取51job招聘信息的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/582449

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定