Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
J
jy_jfb
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
Administrator
jy_jfb
Commits
384c02e1
提交
384c02e1
authored
1月 09, 2026
作者:
wangmenglong
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
增加爬虫
上级
f561fd6b
显示空白字符变更
内嵌
并排
正在显示
5 个修改的文件
包含
643 行增加
和
8 行删除
+643
-8
pom.xml
jfb-recruit/pom.xml
+14
-0
ApiJsonpController.java
...va/com/jfb/recruit/controller/api/ApiJsonpController.java
+224
-8
HttpClientHdfsUtils.java
...a/com/jfb/recruit/controller/api/HttpClientHdfsUtils.java
+70
-0
HttpClientResp.java
...n/java/com/jfb/recruit/controller/api/HttpClientResp.java
+177
-0
HttpClientUtils.java
.../java/com/jfb/recruit/controller/api/HttpClientUtils.java
+158
-0
没有找到文件。
jfb-recruit/pom.xml
浏览文件 @
384c02e1
...
...
@@ -205,6 +205,20 @@
<artifactId>
jsoup
</artifactId>
<version>
1.17.2
</version>
</dependency>
<!-- Selenium 核心依赖(包含 WebDriver 所有核心类) -->
<dependency>
<groupId>
org.seleniumhq.selenium
</groupId>
<artifactId>
selenium-java
</artifactId>
<version>
4.18.1
</version>
<!-- 最新稳定版,适配 Chrome 120+ -->
</dependency>
<dependency>
<groupId>
org.apache.hadoop
</groupId>
<artifactId>
hadoop-client
</artifactId>
<version>
2.7.4
</version>
</dependency>
</dependencies>
<build>
...
...
jfb-recruit/src/main/java/com/jfb/recruit/controller/api/ApiJsonpController.java
浏览文件 @
384c02e1
...
...
@@ -2,20 +2,41 @@ package com.jfb.recruit.controller.api;
import
base.controller.BaseController
;
import
base.result.BaseResult
;
import
com.alibaba.fastjson.JSONObject
;
import
org.apache.http.HttpEntity
;
import
org.apache.http.ParseException
;
import
org.apache.http.client.methods.CloseableHttpResponse
;
import
org.apache.http.client.methods.HttpGet
;
import
org.apache.http.impl.client.CloseableHttpClient
;
import
org.apache.http.impl.client.HttpClients
;
import
org.apache.http.message.BasicHeader
;
import
org.apache.http.util.EntityUtils
;
import
org.jsoup.Jsoup
;
import
org.jsoup.nodes.Document
;
import
org.jsoup.nodes.Element
;
import
org.jsoup.select.Elements
;
import
org.openqa.selenium.By
;
import
org.openqa.selenium.WebDriver
;
import
org.openqa.selenium.WebElement
;
import
org.openqa.selenium.chrome.ChromeDriver
;
import
org.springframework.web.bind.annotation.GetMapping
;
import
org.springframework.web.bind.annotation.RequestBody
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RestController
;
import
javax.servlet.http.HttpServletRequest
;
import
java.io.IOException
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.Random
;
import
java.util.*
;
import
org.openqa.selenium.By
;
import
org.openqa.selenium.WebDriver
;
import
org.openqa.selenium.WebElement
;
import
org.openqa.selenium.chrome.ChromeDriver
;
import
org.openqa.selenium.chrome.ChromeOptions
;
import
org.openqa.selenium.support.ui.WebDriverWait
;
import
org.openqa.selenium.support.ui.ExpectedConditions
;
import
java.time.Duration
;
import
java.util.concurrent.TimeUnit
;
/**
* @author wangmenglong
...
...
@@ -47,19 +68,34 @@ public class ApiJsonpController extends BaseController {
* @return: com.github.pagehelper.PageInfo
**/
@GetMapping
(
"/run"
)
public
BaseResult
run
(
HttpServletRequest
req
){
public
BaseResult
run
(
HttpServletRequest
req
,
@RequestBody
JSONObject
jsonObject
){
// 1. 目标爬取网址(静态页面,无反爬)
String
targetUrl
=
req
.
getParameter
(
"url"
);
String
targetUrl
=
jsonObject
.
getString
(
"url"
);
String
cookie
=
jsonObject
.
getString
(
"cookie"
);
try
{
// 2. 模拟浏览器发送请求(设置User-Agent避免被识别为爬虫)
Document
document
=
Jsoup
.
connect
(
targetUrl
)
.
userAgent
(
getRandomUserAgent
())
// 随机User-Agent
.
referrer
(
"https://www.baidu.com"
)
// 模拟从百度跳转(Referer)
.
header
(
"Accept"
,
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
)
// 接受的内容类型
.
header
(
"Accept-Language"
,
"zh-CN,zh;q=0.9,en;q=0.8"
)
// 语言偏好
.
header
(
"Cache-Control"
,
"max-age=0"
)
// 缓存控制
.
header
(
"Host"
,
"xian.baixing.com"
)
.
header
(
"Accept"
,
"application/json, text/javascript, image/webp, */*; q=0.01"
)
.
header
(
"Accept-Encoding"
,
"gzip, deflate, br"
)
.
header
(
"Accept-Language"
,
"zh-CN,zh;q=0.9"
)
.
header
(
"Cookie"
,
cookie
)
.
header
(
"If-Modified-Since"
,
"Fri, 09 Jan 2026 02:20:30 GMT"
)
.
header
(
"Priority"
,
"u=1,i"
)
.
header
(
"Referer"
,
"https://xian.baixing.com/gongren/m37318/%E5%8B%9E%E8%B5%84%E5%80%9D%E5%8B%9E%E8%B5%84%E5%81%9D=&query="
)
.
header
(
"Sec-Ch-Ua"
,
"\"Google Chrome\";v=\"143\", \"Chromium\";v=\"143\", \"Not A Brand\";v=\"24\""
)
.
header
(
"Sec-Ch-Ua-Mobile"
,
"?0"
)
.
header
(
"Sec-Ch-Ua-Platform"
,
"\"Windows\""
)
.
header
(
"Sec-Fetch-Dest"
,
"empty"
)
.
header
(
"Sec-Fetch-Mode"
,
"cors"
)
.
header
(
"Sec-Fetch-Site"
,
"same-origin"
)
.
header
(
"Sec-Fetch-User"
,
"?1"
)
.
header
(
"User-Agent"
,
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36"
)
.
header
(
"X-Requested-With"
,
"XMLHttpRequest"
)
.
timeout
(
10000
)
// 超时时间10秒
.
get
();
// 发送GET请求
...
...
@@ -83,4 +119,184 @@ public class ApiJsonpController extends BaseController {
}
/**
* @description: 获取openId
* @author: wangmenglong
* @date; 2023/12/7 10:07
* @param: [req]
* @return: com.github.pagehelper.PageInfo
**/
@GetMapping
(
"/run2"
)
public
BaseResult
run2
(
HttpServletRequest
req
,
@RequestBody
JSONObject
jsonObject
){
// 1. 目标爬取网址(静态页面,无反爬)
String
targetUrl
=
jsonObject
.
getString
(
"url"
);
String
cookie
=
jsonObject
.
getString
(
"cookie"
);
String
property
=
jsonObject
.
getString
(
"property"
);
String
userToken
=
jsonObject
.
getString
(
"userToken"
);
// 2. 创建HttpClient实例
try
(
CloseableHttpClient
httpClient
=
HttpClients
.
createDefault
())
{
// 3. 构造HttpGet请求
HttpGet
httpGet
=
new
HttpGet
(
targetUrl
);
// 4. 设置模拟浏览器的请求头(核心:添加Cookie)
// ========== 关键:添加Cookie ==========
httpGet
.
addHeader
(
new
BasicHeader
(
"Cookie"
,
cookie
));
httpGet
.
addHeader
(
new
BasicHeader
(
"Property"
,
property
));
httpGet
.
addHeader
(
new
BasicHeader
(
"User-Token"
,
userToken
));
// 其他核心请求头(和之前一致)
httpGet
.
addHeader
(
new
BasicHeader
(
"Host"
,
"www.baidu.com"
));
httpGet
.
addHeader
(
new
BasicHeader
(
"User-Agent"
,
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36"
));
httpGet
.
addHeader
(
new
BasicHeader
(
"Accept"
,
"application/json, text/plain, */*"
));
httpGet
.
addHeader
(
new
BasicHeader
(
"Referer"
,
"https://we.51job.com/pc/search?jobArea=170300&keyword=%E6%99%AE%E5%B7%A5&searchType=2&keywordType=guess_exp_tag6"
));
// 5. 执行请求(可选:添加爬取间隔,避免反爬)
Thread
.
sleep
(
3000
);
// 3秒间隔
try
(
CloseableHttpResponse
response
=
httpClient
.
execute
(
httpGet
))
{
// 6. 处理响应
int
statusCode
=
response
.
getStatusLine
().
getStatusCode
();
System
.
out
.
println
(
"响应状态码:"
+
statusCode
);
if
(
statusCode
!=
200
)
{
System
.
out
.
println
(
"请求失败,状态码:"
+
statusCode
);
}
// 解析响应内容
HttpEntity
entity
=
response
.
getEntity
();
if
(
entity
!=
null
)
{
String
responseContent
=
EntityUtils
.
toString
(
entity
);
System
.
out
.
println
(
"响应内容片段:\n"
+
responseContent
.
substring
(
0
,
500
));
}
}
catch
(
ParseException
e
)
{
System
.
out
.
println
(
"响应解析失败:"
+
e
.
getMessage
());
}
}
catch
(
IOException
|
InterruptedException
e
)
{
System
.
out
.
println
(
"请求异常:"
+
e
.
getMessage
());
}
return
BaseResult
.
success
();
}
/**
* @description: 获取openId
* @author: wangmenglong
* @date; 2023/12/7 10:07
* @param: [req]
* @return: com.github.pagehelper.PageInfo
**/
@GetMapping
(
"/run3"
)
public
BaseResult
run3
(
HttpServletRequest
req
,
@RequestBody
JSONObject
jsonObject
){
System
.
setProperty
(
"webdriver.chrome.driver"
,
"D:\\chromedriver-win64\\chromedriver.exe"
);
// 1. 配置 Chrome 浏览器选项(核心:反爬伪装)
ChromeOptions
options
=
new
ChromeOptions
();
// 🔴 核心反爬:禁用自动化检测(避免被网站识别为爬虫)
options
.
addArguments
(
"--disable-blink-features=AutomationControlled"
);
// 🔴 无头模式(无界面运行,节省资源)
options
.
addArguments
(
"--headless=new"
);
// 🔴 伪装真实 UA(从浏览器复制)
options
.
addArguments
(
"--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36"
);
// 🔴 可选:禁用图片/视频加载(提升爬取速度)
options
.
addArguments
(
"--blink-settings=imagesEnabled=false"
);
// 🔴 可选:配置代理IP(避免IP封禁,替换为你的代理)
// options.addArguments("--proxy-server=http://123.45.67.89:8080");
// 2. 初始化 WebDriver(模拟浏览器)
WebDriver
driver
=
new
ChromeDriver
(
options
);
// 设置隐式等待(等待页面元素加载)
driver
.
manage
().
timeouts
().
implicitlyWait
(
10000L
,
TimeUnit
.
MILLISECONDS
);
// 设置页面加载超时
driver
.
manage
().
timeouts
().
pageLoadTimeout
(
15000L
,
TimeUnit
.
MILLISECONDS
);
try
{
// 3. 模拟人类访问行为(随机延迟+逐步操作)
Random
random
=
new
Random
();
// 访问拉勾网首页
driver
.
get
(
"https://we.51job.com/pc/search?jobArea=170300&keyword=%E6%99%AE%E5%B7%A5&searchType=2&keywordType=guess_exp_tag6"
);
// 模拟人类等待(2-5秒随机延迟)
Thread
.
sleep
(
2000
+
random
.
nextInt
(
3000
));
// 🔴 等待搜索框加载完成(显式等待,比隐式更可靠)
WebDriverWait
wait
=
new
WebDriverWait
(
driver
,
8000
);
WebElement
searchInput
=
wait
.
until
(
ExpectedConditions
.
presenceOfElementLocated
(
By
.
cssSelector
(
"input[placeholder='请输入职位关键词']"
))
);
// 模拟人类输入(逐字输入,而非一次性输入)
String
keyword
=
"普工"
;
for
(
char
c
:
keyword
.
toCharArray
())
{
searchInput
.
sendKeys
(
String
.
valueOf
(
c
));
Thread
.
sleep
(
100
+
random
.
nextInt
(
200
));
// 每个字符间隔100-300ms
}
// 模拟点击搜索按钮
WebElement
searchBtn
=
driver
.
findElement
(
By
.
cssSelector
(
"button.search_button"
));
searchBtn
.
click
();
Thread
.
sleep
(
3000
+
random
.
nextInt
(
2000
));
// 等待搜索结果加载
// 4. 解析爬取结果(示例:获取职位列表)
WebElement
jobList
=
driver
.
findElement
(
By
.
className
(
"job-list"
));
System
.
out
.
println
(
"===== 爬取到的职位列表 ====="
);
System
.
out
.
println
(
jobList
.
getText
());
}
catch
(
InterruptedException
e
)
{
e
.
printStackTrace
();
}
catch
(
Exception
e
)
{
System
.
out
.
println
(
"爬取异常:"
+
e
.
getMessage
());
}
finally
{
// 5. 释放资源(必须quit(),而非close(),避免驱动残留)
driver
.
quit
();
}
return
BaseResult
.
success
();
}
/**
* @description: 获取openId
* @author: wangmenglong
* @date; 2023/12/7 10:07
* @param: [req]
* @return: com.github.pagehelper.PageInfo
**/
@GetMapping
(
"/run4"
)
public
BaseResult
run4
(
HttpServletRequest
req
,
@RequestBody
JSONObject
jsonObject
)
throws
Exception
{
Map
<
String
,
String
>
headers
=
new
HashMap
<
String
,
String
>();
headers
.
put
(
"Cookie"
,
"RECOMMEND_TIP=true; user_trace_token=20230509172245-850b8329-0db6-49d5-8ee5-788463473366; LGUID=20230509172245-ee291504-af55-4823-8b8f-da7830adea64; _ga=GA1.2.1941570256.1683624167; index_location_city=%E5%85%A8%E5%9B%BD; _gid=GA1.2.744431736.1684134362; privacyPolicyPopup=false; __lg_stoken__=00ef87c190275da025cc19a93d14d5da80c4c3ff29516c88d738dd7350f8601ae184994af7785dc2260517aa65b80ae0048d5bdb5ea64e76bf2b4df769b1de46bfa3cc6bd487; SEARCH_ID=c6e9d66fa6f64d48874952a58bf47660; gate_login_token=v1####da9e29af0db73d825a22a9a882bf9ddbb316eae052443e729b53cab3f19a8e70; LG_HAS_LOGIN=1; hasDeliver=0; __SAFETY_CLOSE_TIME__26120270=1; JSESSIONID=ABAAABAABEIABCI02041966E510E7120309F7B2F34013BF; WEBTJ-ID=20230515193746-1881f33c337109-00aed6e8f8a02b-7b515477-1327104-1881f33c33814dd; _putrc=743692222AE66441123F89F2B170EADC; login=true; unick=%E7%94%A8%E6%88%B77560; showExpriedIndex=1; showExpriedCompanyHome=1; showExpriedMyPublish=1; Hm_lvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1683624167,1684134362,1684150667; sensorsdata2015session=%7B%7D; X_HTTP_TOKEN=d5afe4428dfdf76486605148610ad9240e30d415e3; Hm_lpvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1684150668; TG-TRACK-CODE=index_zhaopin; LGRID=20230515193751-230d34f5-3f31-4ddd-8bb0-23d58400b756; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%2226120270%22%2C%22first_id%22%3A%22187ffd2094fb06-0d35cf63f3901a-7b515477-1327104-187ffd20950ca3%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E8%87%AA%E7%84%B6%E6%90%9C%E7%B4%A2%E6%B5%81%E9%87%8F%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC%22%2C%22%24latest_referrer%22%3A%22https%3A%2F%2Fcn.bing.com%2F%22%2C%22%24os%22%3A%22Windows%22%2C%22%24browser%22%3A%22Chrome%22%2C%22%24browser_version%22%3A%22113.0.0.0%22%7D%2C%22%24device_id%22%3A%22187ffd2094fb06-0d35cf63f3901a-7b515477-1327104-187ffd20950ca3%22%7D"
);
headers
.
put
(
"Connection"
,
"keep-alive"
);
headers
.
put
(
"Accept"
,
""
);
headers
.
put
(
"Accept-Language"
,
"zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"
);
headers
.
put
(
"User-Agent"
,
"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N)"
+
"ppleWebKit/537.36 (KHTML, like Gecko) "
+
"Chrome/113.0.0.0 Mobile Safari/537.36 Edg/113.0.1774.42"
);
headers
.
put
(
"Content-Type"
,
"text/html; charset=utf-8"
);
headers
.
put
(
"Referer"
,
"https://www.lagou.com/jobs/list_%E5%A4%A7%E6%95%B0%E6%8D%AE/p-city_0?&cl=false&fromSearch=true&labelWords=&suginput="
);
headers
.
put
(
"Origin"
,
"https://www.lagou.com"
);
headers
.
put
(
"X-Requested-With"
,
"XMLHttpRequest"
);
headers
.
put
(
"X-Anit-Forge-Token"
,
"None"
);
headers
.
put
(
"Cache-Control"
,
"no-cache"
);
headers
.
put
(
"X-Anit-Forge-Code"
,
"0"
);
headers
.
put
(
"Host"
,
"www.lagou.com"
);
Map
<
String
,
String
>
params
=
new
HashMap
<
String
,
String
>();
params
.
put
(
"kd"
,
"普工"
);
params
.
put
(
"city"
,
"全国"
);
for
(
int
i
=
1
;
i
<
31
;
i
++)
{
params
.
put
(
"pn"
,
String
.
valueOf
(
i
));
}
for
(
int
i
=
1
;
i
<
31
;
i
++){
params
.
put
(
"pn"
,
String
.
valueOf
(
i
));
HttpClientResp
result
=
HttpClientUtils
.
doPost
(
"https://www.lagou.com/jobs/positionAjax.json?"
+
"needAddtionalResult=false&first=true&px=default"
,
headers
,
params
);
System
.
out
.
println
((
result
.
getContent
()));
HttpClientHdfsUtils
.
createFileBySysTime
(
"hdfs://master:9820"
,
"page"
+
i
,
result
.
toString
());
Thread
.
sleep
(
1
*
500
);
}
return
BaseResult
.
success
();
}
}
jfb-recruit/src/main/java/com/jfb/recruit/controller/api/HttpClientHdfsUtils.java
0 → 100644
浏览文件 @
384c02e1
package
com
.
jfb
.
recruit
.
controller
.
api
;
import
java.io.ByteArrayInputStream
;
import
java.io.IOException
;
import
java.net.URI
;
import
java.text.SimpleDateFormat
;
import
java.util.Calendar
;
import
java.util.Date
;
import
org.apache.hadoop.conf.Configuration
;
import
org.apache.hadoop.fs.FSDataOutputStream
;
import
org.apache.hadoop.fs.FileSystem
;
import
org.apache.hadoop.fs.Path
;
import
org.apache.hadoop.io.IOUtils
;
public
class
HttpClientHdfsUtils
{
/**
* 根据系统当前时间在 HDFS 上创建文件并写入数据。
* 会以当前日期作为文件夹名,在 /JobData 目录下创建文件夹,然后在该文件夹中创建指定文件名的文件并写入数据。
*
* @param url HDFS 的 URI,例如 "hdfs://localhost:9000"
* @param fileName 要创建的文件名
* @param data 要写入文件的数据
*/
public
static
void
createFileBySysTime
(
String
url
,
String
fileName
,
String
data
)
{
// 指定操作 HDFS 的用户为 root
System
.
setProperty
(
"HADOOP_USER_NAME"
,
"root"
);
// 定义文件路径变量
Path
filePath
=
null
;
// 获取系统当前时间
Calendar
calendar
=
Calendar
.
getInstance
();
Date
currentTime
=
calendar
.
getTime
();
// 定义日期格式化对象,将日期格式化为 yyyyMMdd 的形式
SimpleDateFormat
dateFormat
=
new
SimpleDateFormat
(
"yyyyMMdd"
);
// 获取格式化后的日期字符串,作为文件夹名
String
directoryName
=
dateFormat
.
format
(
currentTime
);
// 创建 Hadoop 配置对象
Configuration
conf
=
new
Configuration
();
// 将传入的 URL 转换为 URI 对象
URI
hdfsUri
=
URI
.
create
(
url
);
// 使用 try-with-resources 语句自动管理 FileSystem 资源,确保资源正确关闭
try
(
FileSystem
fileSystem
=
FileSystem
.
get
(
hdfsUri
,
conf
))
{
// 构建要创建的目录路径
filePath
=
new
Path
(
"/SESSECS/"
+
directoryName
);
// 检查目录是否存在,如果不存在则创建
if
(!
fileSystem
.
exists
(
filePath
))
{
// 创建目录,包括所有必要的父目录
fileSystem
.
mkdirs
(
filePath
);
}
// 构建要创建的文件的完整路径
Path
fullFilePath
=
new
Path
(
filePath
.
toString
()
+
"/"
+
fileName
);
// 在指定路径创建文件并获取输出流
try
(
FSDataOutputStream
outputStream
=
fileSystem
.
create
(
fullFilePath
))
{
// 将数据转换为字节数组输入流
ByteArrayInputStream
inputStream
=
new
ByteArrayInputStream
(
data
.
getBytes
());
// 将输入流中的数据复制到输出流中,即写入文件
IOUtils
.
copyBytes
(
inputStream
,
outputStream
,
conf
,
true
);
}
}
catch
(
IOException
e
)
{
// 打印异常堆栈信息,方便调试
System
.
err
.
println
(
"在 HDFS 上创建文件并写入数据时发生错误: "
+
e
.
getMessage
());
e
.
printStackTrace
();
}
}
}
jfb-recruit/src/main/java/com/jfb/recruit/controller/api/HttpClientResp.java
0 → 100644
浏览文件 @
384c02e1
package
com
.
jfb
.
recruit
.
controller
.
api
;
import
java.io.File
;
import
java.io.IOException
;
import
java.io.Serializable
;
import
java.util.HashMap
;
import
java.util.Map
;
/**
* HttpClientResp 类用于封装 HTTP 响应的相关信息,包括响应状态码、响应内容和请求头信息。
* 该类实现了 Serializable 接口,以便可以进行序列化操作。
*/
public
class
HttpClientResp
implements
Serializable
{
// 序列化版本号,用于保证序列化和反序列化的兼容性
private
static
final
long
serialVersionUID
=
-
2224539827395038194L
;
// 响应状态码
private
int
code
;
// 响应数据
private
String
content
;
// 请求头信息,使用 Map 存储,键为头信息的名称,值为头信息的值
private
Map
<
String
,
String
>
headers
;
/**
* 空参构造函数,用于创建一个空的 HttpClientResp 对象。
*/
public
HttpClientResp
()
{
this
.
headers
=
new
HashMap
<>();
}
/**
* 构造函数,用于创建一个仅包含响应状态码的 HttpClientResp 对象。
*
* @param code 响应状态码
*/
public
HttpClientResp
(
int
code
)
{
this
.
code
=
code
;
this
.
headers
=
new
HashMap
<>();
}
/**
* 构造函数,用于创建一个仅包含响应内容的 HttpClientResp 对象。
*
* @param content 响应内容
*/
public
HttpClientResp
(
String
content
)
{
this
.
content
=
content
;
this
.
headers
=
new
HashMap
<>();
}
/**
* 构造函数,用于创建一个包含响应状态码和响应内容的 HttpClientResp 对象。
*
* @param code 响应状态码
* @param content 响应内容
*/
public
HttpClientResp
(
int
code
,
String
content
)
{
this
.
code
=
code
;
this
.
content
=
content
;
this
.
headers
=
new
HashMap
<>();
}
/**
* 构造函数,用于创建一个包含响应状态码、响应内容和请求头信息的 HttpClientResp 对象。
*
* @param code 响应状态码
* @param content 响应内容
* @param headers 请求头信息
*/
public
HttpClientResp
(
int
code
,
String
content
,
Map
<
String
,
String
>
headers
)
{
this
.
code
=
code
;
this
.
content
=
content
;
this
.
headers
=
headers
!=
null
?
headers
:
new
HashMap
<>();
}
/**
* 获取响应状态码。
*
* @return 响应状态码
*/
public
int
getCode
()
{
return
code
;
}
/**
* 设置响应状态码。
*
* @param code 响应状态码
*/
public
void
setCode
(
int
code
)
{
this
.
code
=
code
;
}
/**
* 获取响应内容。
*
* @return 响应内容
*/
public
String
getContent
()
{
return
content
;
}
/**
* 设置响应内容。
*
* @param content 响应内容
*/
public
void
setContent
(
String
content
)
{
this
.
content
=
content
;
}
/**
* 获取请求头信息。
*
* @return 请求头信息的 Map
*/
public
Map
<
String
,
String
>
getHeaders
()
{
return
headers
;
}
/**
* 设置请求头信息。
*
* @param headers 请求头信息的 Map
*/
public
void
setHeaders
(
Map
<
String
,
String
>
headers
)
{
this
.
headers
=
headers
;
}
/**
* 向请求头信息中添加一个键值对。
*
* @param key 请求头的名称
* @param value 请求头的值
*/
public
void
addHeader
(
String
key
,
String
value
)
{
this
.
headers
.
put
(
key
,
value
);
}
/**
* 获取响应状态码,与 getCode 方法功能相同,提供一个更具描述性的方法名。
*
* @return 响应状态码
*/
public
int
getStatusCode
()
{
return
code
;
}
/**
* 将请求头信息保存到指定的 JSON 文件中。
*
* @param filePath 要保存的 JSON 文件的路径
* @throws IOException 如果保存文件时发生 I/O 错误
*/
public
void
saveHeadersToJson
(
String
filePath
)
throws
IOException
{
ObjectMapper
objectMapper
=
new
ObjectMapper
();
objectMapper
.
writeValue
(
new
File
(
filePath
),
headers
);
}
/**
* 重写 toString 方法,用于方便打印对象的信息。
*
* @return 包含响应状态码和响应内容的字符串
*/
@Override
public
String
toString
()
{
return
"HttpClientResp [code="
+
code
+
", content="
+
content
+
", headers="
+
headers
+
"]"
;
}
/**
* 内部类,用于模拟 ObjectMapper 类,用于将请求头信息保存到 JSON 文件中。
*/
private
static
class
ObjectMapper
{
public
void
writeValue
(
File
file
,
Map
<
String
,
String
>
headers
)
{
throw
new
UnsupportedOperationException
(
"Not implemented"
);
}
}
}
jfb-recruit/src/main/java/com/jfb/recruit/controller/api/HttpClientUtils.java
0 → 100644
浏览文件 @
384c02e1
package
com
.
jfb
.
recruit
.
controller
.
api
;
import
java.io.IOException
;
import
java.io.UnsupportedEncodingException
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map.Entry
;
import
java.util.Set
;
import
org.apache.http.HttpStatus
;
import
org.apache.http.NameValuePair
;
import
org.apache.http.client.config.RequestConfig
;
import
org.apache.http.client.entity.UrlEncodedFormEntity
;
import
org.apache.http.client.methods.CloseableHttpResponse
;
import
org.apache.http.client.methods.HttpEntityEnclosingRequestBase
;
import
org.apache.http.client.methods.HttpPost
;
import
org.apache.http.client.methods.HttpRequestBase
;
import
org.apache.http.impl.client.CloseableHttpClient
;
import
org.apache.http.impl.client.HttpClients
;
import
org.apache.http.message.BasicNameValuePair
;
import
org.apache.http.util.EntityUtils
;
/**
* HttpClientUtils 类提供了使用 Apache HttpClient 进行 HTTP POST 请求的工具方法。
* 包含封装请求头、请求参数,执行请求并获取响应内容,以及释放资源等功能。
*/
public
class
HttpClientUtils
{
// 编码格式,发送编码格式统一用 UTF-8
private
static
final
String
ENCODING
=
"UTF-8"
;
// 设置连接超时时间,单位毫秒
private
static
final
int
CONNECT_TIMEOUT
=
6000
;
// 请求获取数据的超时时间(即响应时间),单位毫秒
private
static
final
int
SOCKET_TIMEOUT
=
6000
;
/**
* 封装 HTTP 请求头。
* 将传入的请求头参数添加到 HttpRequestBase 对象中。
*
* @param params 请求头参数的键值对映射
* @param httpMethod HttpRequestBase 对象,用于表示 HTTP 请求
*/
public
static
void
packageHeader
(
Map
<
String
,
String
>
params
,
HttpRequestBase
httpMethod
)
{
// 检查请求头参数是否为空
if
(
params
!=
null
)
{
// 获取请求头参数的键值对集合
Set
<
Entry
<
String
,
String
>>
entrySet
=
params
.
entrySet
();
// 遍历键值对集合,将每个键值对添加到请求头中
for
(
Entry
<
String
,
String
>
entry
:
entrySet
)
{
httpMethod
.
setHeader
(
entry
.
getKey
(),
entry
.
getValue
());
}
}
}
/**
* 封装 HTTP 请求参数。
* 将传入的请求参数封装到 HttpEntityEnclosingRequestBase 对象中。
*
* @param params 请求参数的键值对映射
* @param httpMethod HttpEntityEnclosingRequestBase 对象,用于表示包含实体的 HTTP 请求
* @throws UnsupportedEncodingException 如果不支持指定的编码格式
*/
public
static
void
packageParam
(
Map
<
String
,
String
>
params
,
HttpEntityEnclosingRequestBase
httpMethod
)
throws
UnsupportedEncodingException
{
// 检查请求参数是否为空
if
(
params
!=
null
)
{
// 创建一个 NameValuePair 列表,用于存储请求参数
List
<
NameValuePair
>
nvps
=
new
ArrayList
<>();
// 获取请求参数的键值对集合
Set
<
Entry
<
String
,
String
>>
entrySet
=
params
.
entrySet
();
// 遍历键值对集合,将每个键值对添加到 NameValuePair 列表中
for
(
Entry
<
String
,
String
>
entry
:
entrySet
)
{
nvps
.
add
(
new
BasicNameValuePair
(
entry
.
getKey
(),
entry
.
getValue
()));
}
// 将 NameValuePair 列表封装到 UrlEncodedFormEntity 中,并设置编码格式
httpMethod
.
setEntity
(
new
UrlEncodedFormEntity
(
nvps
,
ENCODING
));
}
}
/**
* 获取 HTTP 响应内容。
* 执行 HTTP 请求并返回响应结果的封装对象。
*
* @param httpResponse CloseableHttpResponse 对象,用于表示 HTTP 响应
* @param httpClient CloseableHttpClient 对象,用于执行 HTTP 请求
* @param httpMethod HttpRequestBase 对象,用于表示 HTTP 请求
* @return HttpClientResp 对象,封装了响应状态码和响应内容
* @throws Exception 如果执行请求或处理响应时发生异常
*/
public
static
HttpClientResp
getHttpClientResult
(
CloseableHttpResponse
httpResponse
,
CloseableHttpClient
httpClient
,
HttpRequestBase
httpMethod
)
throws
Exception
{
// 执行 HTTP 请求
httpResponse
=
httpClient
.
execute
(
httpMethod
);
// 检查响应是否有效
if
(
httpResponse
!=
null
&&
httpResponse
.
getStatusLine
()
!=
null
)
{
String
content
=
""
;
// 检查响应实体是否存在
if
(
httpResponse
.
getEntity
()
!=
null
)
{
// 将响应实体内容转换为字符串,并设置编码格式
content
=
EntityUtils
.
toString
(
httpResponse
.
getEntity
(),
ENCODING
);
}
// 创建并返回 HttpClientResp 对象,封装响应状态码和响应内容
return
new
HttpClientResp
(
httpResponse
.
getStatusLine
().
getStatusCode
(),
content
);
}
// 如果响应无效,返回表示内部服务器错误的 HttpClientResp 对象
return
new
HttpClientResp
(
HttpStatus
.
SC_INTERNAL_SERVER_ERROR
);
}
/**
* 通过 HttpClient Post 方式提交请求头和请求参数,从服务端返回状态码和 JSON 数据内容。
*
* @param url 请求的 URL
* @param headers 请求头参数的键值对映射
* @param params 请求参数的键值对映射
* @return HttpClientResp 对象,封装了响应状态码和响应内容
* @throws Exception 如果执行请求或处理响应时发生异常
*/
public
static
HttpClientResp
doPost
(
String
url
,
Map
<
String
,
String
>
headers
,
Map
<
String
,
String
>
params
)
throws
Exception
{
// 创建默认的 CloseableHttpClient 对象
CloseableHttpClient
httpClient
=
HttpClients
.
createDefault
();
// 创建 HttpPost 对象,用于表示 POST 请求
HttpPost
httpPost
=
new
HttpPost
(
url
);
// 封装请求配置项,设置连接超时时间和响应超时时间
RequestConfig
requestConfig
=
RequestConfig
.
custom
().
setConnectTimeout
(
CONNECT_TIMEOUT
).
setSocketTimeout
(
SOCKET_TIMEOUT
).
build
();
// 设置 POST 请求的配置项
httpPost
.
setConfig
(
requestConfig
);
// 封装请求头
packageHeader
(
headers
,
httpPost
);
// 封装请求参数
packageParam
(
params
,
httpPost
);
// 初始化 CloseableHttpResponse 对象,用于存储响应结果
CloseableHttpResponse
httpResponse
=
null
;
try
{
// 执行请求并获得响应结果
return
getHttpClientResult
(
httpResponse
,
httpClient
,
httpPost
);
}
finally
{
// 释放资源
release
(
httpResponse
,
httpClient
);
}
}
/**
* 释放 httpclient(HTTP 请求)对象资源和 httpResponse(HTTP 响应)对象资源。
*
* @param httpResponse CloseableHttpResponse 对象,用于表示 HTTP 响应
* @param httpClient CloseableHttpClient 对象,用于执行 HTTP 请求
* @throws IOException 如果关闭资源时发生 I/O 异常
*/
private
static
void
release
(
CloseableHttpResponse
httpResponse
,
CloseableHttpClient
httpClient
)
throws
IOException
{
// 检查响应对象是否为空,若不为空则关闭
if
(
httpResponse
!=
null
)
{
httpResponse
.
close
();
}
// 检查客户端对象是否为空,若不为空则关闭
if
(
httpClient
!=
null
)
{
httpClient
.
close
();
}
}
}
\ No newline at end of file
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论