Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
T
tj_tool
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
牟俊洁
tj_tool
Commits
503eaa36
Commit
503eaa36
authored
Dec 11, 2025
by
白满斌
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
新
parent
9ba65111
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
506 additions
and
611 deletions
+506
-611
BatchDetail.php
app/Console/Commands/BatchDetail.php
+73
-18
BatchExportUserPhone.php
app/Console/Commands/BatchExportUserPhone.php
+60
-35
test.php
app/Console/Commands/test.php
+29
-28
Decryptor.php
app/Http/Services/Decryptor.php
+296
-0
GgongkaoLeidaCrawler.php
app/Http/Services/GgongkaoLeidaCrawler.php
+0
-528
t.php
public/t.php
+48
-2
No files found.
app/Console/Commands/BatchDetail.php
View file @
503eaa36
...
...
@@ -126,7 +126,7 @@ class BatchDetail extends Command
dump
(
$id
,
$max
);
$total
=
0
;
$LeidaModel
->
whereBetween
(
'id'
,
[
$id
,
$max
])
->
select
([
'*'
])
->
chunkById
(
50
,
function
(
$list
)
use
(
&
$LeidaModel
)
{
$LeidaModel
->
whereBetween
(
'id'
,
[
$id
,
$max
])
->
select
([
'*'
])
->
chunkById
(
50
,
function
(
$list
)
use
(
&
$LeidaModel
,
&
$total
)
{
$nowTime
=
time
();
$list
=
$list
->
toArray
();
foreach
(
$list
as
$key
=>
$value
)
{
...
...
@@ -134,13 +134,45 @@ class BatchDetail extends Command
$articleId
=
self
::
getLastNumberFromUrl
(
$value
[
'url'
]);
$str
=
'appid=uqsFgLOVbuPrfn1v&articleId='
.
$articleId
.
'&from_device=h5×tamp='
.
time
()
.
'&token=
569fff2952c0317c29f3308df15cd75d11d15fmern1cdeol3f9tcfo31cdnb3o9o5yd03&userId=26239811
'
;
$str
=
'appid=uqsFgLOVbuPrfn1v&articleId='
.
$articleId
.
'&from_device=h5×tamp='
.
time
()
.
'&token=
3d24d79a5420c8fe1af5d74f71fc72a211d15fk7z41cdeu6tzo6fsl41cdngpeu2j1px4&userId=26137696
'
;
$signStr
=
md5
(
urlencode
(
$str
.
'&secret=Hf6yn1JPb1QZxniWhIPv1IrHbWeLh2e8'
));
$url
=
'https://api.gongkaoleida.com/api/FrontBackSecure/article/detailUserPc?'
.
$str
.
'&sign='
.
$signStr
;
// URL: https://api.gongkaoleida.com/api/FrontBackSecure/article/detailUserPc?appid=uqsFgLOVbuPrfn1v&articleId=2720773&from_device=h5×tamp=1765414971&token=2ada686c9962ad44f0544e43ea24080e11d154q7q71cdesi46q1sey51cdnf0p14eeca5&userId=7941535&sign=7a8a2a3da73e9c762ad6b785f8a882c4
// URL: https://api.gongkaoleida.com/api/FrontBackSecure/article/detailUserPc?appid=uqsFgLOVbuPrfn1v&articleId=2721189&from_device=h5×tamp=1765421999&token=0f196df3dcfbb78b83a4e1d18fc9fe9611d155dxb81cdeo6jkh9wuv81cdnap4evmis78&userId=9047780&sign=6cac7679cb76b1f0b74377d711847687
// https://api.gongkaoleida.com/api/v6_0_3_0/job/jobList?cursor=&branchId=0&examId=717862&page=1&searchKey=&uuid=a5d91f5a-0628-4ad0-84ce-83f8514d13bf&from_device=android×tamp=1765433032&sign=3a45a404bdc9616bcd995185b6890935&appid=8tQhhq32HIxOH72p&osVersion=29&devVersion=6.2.0.0&appLaunchChannel=default&token=f65258b207d7f3a8af87063d74531ba211d15fliu41cdev1qkuochan1cdnhkbf90yemn&userId=26198428
// 13164251418:0f196df3dcfbb78b83a4e1d18fc9fe9611d155dxb81cdeo6jkh9wuv81cdnap4evmis78 -- 9047780
// 17310088217:cbf6a15cf45c95f89a664f3534f64eae11d15fnc6h1cdetb9sj5pczd1cdnftumxibabd -- 26283113
// junjie:f65258b207d7f3a8af87063d74531ba211d15fliu41cdev1qkuochan1cdnhkbf90yemn -- 26198428
$param
=
[
'remindId'
=>
0
,
'articleId'
=>
$articleId
,
'from_device'
=>
'ios'
,
'timestamp'
=>
time
(),
'appid'
=>
'8tQhhq32HIxOH72p'
,
'osVersion'
=>
'29'
,
'devVersion'
=>
'6.2.0.0'
,
'appLaunchChannel'
=>
'default'
,
'token'
=>
'f65258b207d7f3a8af87063d74531ba211d15fliu41cdev1qkuochan1cdnhkbf90yemn'
,
'userId'
=>
'26198428'
,
];
ksort
(
$param
);
$argStr
=
''
;
foreach
(
$param
as
$key
=>
$val
)
{
$argStr
.=
$key
.
"="
.
trim
(
$val
,
' '
)
.
"&"
;
}
$argStr2
=
$argStr
.
'secret=GO1GgDeC1qIr7KcuGfZIGTHk5R3RM1KT'
;
$signStr2
=
md5
(
urlencode
(
$argStr2
));
$url
=
'https://api.gongkaoleida.com/api/v6_0_3_0/article/detail?'
.
$argStr
.
'&sign='
.
$signStr2
;
try
{
$refer
=
$url
;
...
...
@@ -150,31 +182,35 @@ class BatchDetail extends Command
// 获取响应
$statusCode
=
$response
->
getStatusCode
();
$content
=
json_decode
(
$response
->
getBody
()
->
getContents
(),
true
);
$total
++
;
if
(
$statusCode
!=
200
){
dd
(
'状态码异常:'
.
$statusCode
,
$value
[
'id'
],
$content
);
}
if
(
$content
[
'code'
]
!=
1
){
if
(
!
in_array
(
$content
[
'code'
],
[
0
,
1
])
){
dd
(
$value
[
'id'
],
$content
);
}
$sourceData
=
$content
[
'data'
][
'articleInfo'
]
??
[];
if
(
empty
(
$sourceData
)){
dd
(
'数据异常:'
.
$value
[
'id'
]);
}
$from_url
=
$sourceData
[
'sourcePageUrl'
];
$from_title
=
$sourceData
[
'origin'
];
$from_detail
=
json_encode
(
$sourceData
);
$this
->
saveRetToFile
(
json_encode
(
$content
),
$value
[
'id'
]);
$updateData
=
[
'from_url'
=>
$from_url
,
'from_title'
=>
$from_title
,
'from_detail'
=>
$from_detail
,
];
$LeidaModel
->
updateData
([
'id'
=>
$value
[
'id'
]],
$updateData
);
// $sourceData = $content['data']['articleInfo'] ?? [];
// if(empty($sourceData)){
// dd('数据异常:'.$value['id']);
// }
// $from_url = $sourceData['sourcePageUrl'];
// $from_title = $sourceData['origin'];
// $from_detail = json_encode($sourceData);
//
// $updateData = [
// 'from_url' => $from_url,
// 'from_title' => $from_title,
// 'from_detail' => $from_detail,
// ];
// $LeidaModel->updateData(['id'=>$value['id']], $updateData);
dump
(
'done:'
.
$value
[
'id'
])
;
echo
'done:'
.
$value
[
'id'
]
.
' total:'
.
$total
.
' '
;
sleep
(
5
);
sleep
(
3
);
}
catch
(
\Exception
$e
)
{
...
...
@@ -207,4 +243,23 @@ class BatchDetail extends Command
}
/**
* 保存HTML内容到文件
*/
public
function
saveRetToFile
(
string
$html
,
$id
)
:
string
{
$filename
=
$id
.
'.txt'
;
$path
=
storage_path
(
'app/crawled/'
.
$filename
);
// 确保目录存在
if
(
!
is_dir
(
dirname
(
$path
)))
{
mkdir
(
dirname
(
$path
),
0755
,
true
);
}
file_put_contents
(
$path
,
$html
);
return
$path
;
}
}
app/Console/Commands/BatchExportUserPhone.php
View file @
503eaa36
...
...
@@ -17,7 +17,7 @@ class BatchExportUserPhone extends Command
*
* @var string
*/
protected
$signature
=
'leida:gongkao {page : 要爬取的page}'
;
protected
$signature
=
'leida:gongkao {page : 要爬取的page}
{type : 要到哪个type} {area : 要到哪个area}
'
;
/**
* The console command description.
...
...
@@ -37,7 +37,6 @@ class BatchExportUserPhone extends Command
//省-市-0-考试类型-招考公告
public
static
$ksType
=
[
// '公务员' => '-0-0-2-124',
"教师"
=>
"-0-0-59-124"
,
"事业单位"
=>
"-0-0-3-124"
,
"医疗"
=>
"-0-0-60-124"
,
...
...
@@ -58,37 +57,37 @@ class BatchExportUserPhone extends Command
// 1117 => "安徽",
// 1 => "北京",
// 1255 => "福建",
2129
=>
"广东"
,
3191
=>
"甘肃"
,
2290
=>
"广西"
,
2723
=>
"贵州"
,
37
=>
"河北"
,
1849
=>
"湖北"
,
705
=>
"黑龙江"
,
1654
=>
"河南"
,
2429
=>
"海南"
,
1979
=>
"湖南"
,
627
=>
"吉林"
,
878
=>
"江苏"
,
1359
=>
"江西"
,
498
=>
"辽宁"
,
374
=>
"内蒙古"
,
3357
=>
"宁夏"
,
3304
=>
"青海"
,
2500
=>
"四川"
,
1482
=>
"山东"
,
859
=>
"上海"
,
232
=>
"山西"
,
3063
=>
"陕西"
,
19
=>
"天津"
,
2980
=>
"西藏"
,
3390
=>
"新疆"
,
2826
=>
"云南"
,
1004
=>
"浙江"
,
//
2129 => "广东",
//
3191 => "甘肃",
//
2290 => "广西",
//
2723 => "贵州",
//
37 => "河北",
//
1849 => "湖北",
//
705 => "黑龙江",
//
1654 => "河南",
//
2429 => "海南",
//
1979 => "湖南",
//
627 => "吉林",
//
878 => "江苏",
//
1359 => "江西",
//
498 => "辽宁",
//
374 => "内蒙古",
//
3357 => "宁夏",
//
3304 => "青海",
//
2500 => "四川",
//
1482 => "山东",
//
859 => "上海",
//
232 => "山西",
//
3063 => "陕西",
//
19 => "天津",
//
2980 => "西藏",
//
3390 => "新疆",
//
2826 => "云南",
//
1004 => "浙江",
2460
=>
"重庆"
,
3508
=>
"香港"
,
3509
=>
"澳门"
,
3507
=>
"台湾"
,
//
3508 => "香港",
//
3509 => "澳门",
//
3507 => "台湾",
];
...
...
@@ -106,6 +105,9 @@ class BatchExportUserPhone extends Command
{
$initpage
=
$this
->
argument
(
'page'
);
$typeStr
=
$this
->
argument
(
'type'
);
$areaStr
=
$this
->
argument
(
'area'
);
$LeidaModel
=
new
LeidaModel
();
...
...
@@ -122,12 +124,35 @@ class BatchExportUserPhone extends Command
]);
$firstDone
=
false
;
// 是否已处理第一个特殊循环
$found
=
false
;
// 是否找到了起始键, 考试类型从哪个开始
$areafound
=
false
;
// 是否找到了起始键, 考试类型从哪个开始
$lianxuNone
=
0
;
// 连续三次是未爬取到就die;
foreach
(
self
::
$ksArea
as
$pid
=>
$areaName
)
{
// 如果还没找到起始键
if
(
!
$areafound
)
{
// 如果当前键就是起始键
if
(
$pid
==
$areaStr
)
{
$areafound
=
true
;
// 标记已找到
}
else
{
continue
;
}
}
foreach
(
self
::
$ksType
as
$typeName
=>
$typeIds
)
{
// 如果还没找到起始键
if
(
!
$found
)
{
// 如果当前键就是起始键
if
(
$typeName
==
$typeStr
)
{
$found
=
true
;
// 标记已找到
}
else
{
continue
;
}
}
$refer
=
'https://www.gongkaoleida.com/area/3510-0-0-59-124'
;
// 确定当前外层循环的起始页
$startPage
=
$firstDone
==
true
?
1
:
$initpage
;
...
...
@@ -147,7 +172,7 @@ class BatchExportUserPhone extends Command
'Connection'
=>
'keep-alive'
,
'Sec-Ch-Ua'
=>
'"Chromium";v="116", "Not)A;Brand";v="24", "Google Chrome";v="116"'
,
'Referer'
=>
$refer
,
'Cookie'
=>
"Hm_lvt_f721d958b1ffbdd95625a927f9bbe719=1764753040; HMACCOUNT=D3E8E8384EAB7DAC; Hm_lvt_a85566772a4d8c7093230e45128ffa8f=1764753040; _c_WBKFRo=qpwRYzkl3x5zkB59FmtRBA7hNLLyiTF2HfalK5J5; _nb_ioWEgULi=; vipInfo=%7B%22isVip%22%3A0%2C%22vipGrade%22%3A0%2C%22vipExpire%22%3A0%7D; gkld_captchaSecret=2f246381c807d7085d53e20ef141809c; userId=11317419; token=c1c6a9fdf0bea1fe6a46314e3dd8c17111d156qkkr1cdejzbb1znjle1cdn6hw5gc9gxe; acw_
sc__v2=197d84838-b78ec3f20861d54bee402608fc7cc3a5e15a044e3e021f6f64; acw_tc=0b34264217652651756636995ed7562c242228d8abe852a130a3ec737b74dd; Hm_lpvt_a85566772a4d8c7093230e45128ffa8f=1765265176; Hm_lpvt_f721d958b1ffbdd95625a927f9bbe719=1765265176; XSRF-TOKEN=eyJpdiI6Iit0Y3ZzMWFJTnFyZFZQYVU0SU5XQWc9PSIsInZhbHVlIjoiNnNUMWF1MDFvTVpycFpKVHc3cVFuSlYwVTQ1T3RaUVE2MTVsVnVoOVdLaGdlVEpVNUs1WGFNZFk5XC9FSXQ4TkwiLCJtYWMiOiI1NDE3ODQyZjg2YWUyNWEzNjg2ODNmNTJhNDAxMjlmZDMwZDVmYmNmNzBhNjdmNjEwNWY5NzEzZDVhOTAzNDdjIn0%3D; gkld_session=eyJpdiI6IkRtck9JblFURGdcL1RNVU5iRWN4MENBPT0iLCJ2YWx1ZSI6Ikc4T09hMW1QbFpYeEhmQnM0VUlEbkw5d3ZXMTIyZmtEVlFhdlpyT3AzdlwvOElBQkkxM0dFNkhFdnc1aFNnRVZwT2FPa3dUVUx6OGxRdTJGM2ViN2gyb2htVTFZVXhLZHdocjRNbm5wUmdGVXZ1UUdlaU1vVGNwOEpaOHN5SkhSSSIsIm1hYyI6IjczNGQ4ZGU2Y2ExZDdkMTk3ODU0ZGU4N2VjMWRiMGJkMzRkNWQyZDQ2YTc2YzJiMWZiY2RlZWViZGRhODE0ZmM
ifQ%3D%3D"
,
'Cookie'
=>
"Hm_lvt_f721d958b1ffbdd95625a927f9bbe719=1764753040; HMACCOUNT=D3E8E8384EAB7DAC; Hm_lvt_a85566772a4d8c7093230e45128ffa8f=1764753040; _c_WBKFRo=qpwRYzkl3x5zkB59FmtRBA7hNLLyiTF2HfalK5J5; _nb_ioWEgULi=; vipInfo=%7B%22isVip%22%3A0%2C%22vipGrade%22%3A0%2C%22vipExpire%22%3A0%7D; gkld_captchaSecret=2f246381c807d7085d53e20ef141809c; userId=11317419; token=c1c6a9fdf0bea1fe6a46314e3dd8c17111d156qkkr1cdejzbb1znjle1cdn6hw5gc9gxe; acw_
tc=0a09ec3e17653592476737214ebeae1d398493b77b9696dfeea355956a779e; acw_sc__v2=197d84838-b2b66fad0989c27cec1040019734a2073a611a674a08611730; Hm_lpvt_f721d958b1ffbdd95625a927f9bbe719=1765360707; Hm_lpvt_a85566772a4d8c7093230e45128ffa8f=1765360707; XSRF-TOKEN=eyJpdiI6IjRGMlljQVJSWFpweURuKzkzQ2lDY0E9PSIsInZhbHVlIjoiS3RLbXlBVkM4V0lUZTh1aExUa2ZtaWRLSnhUNzlDUzJGb2NsY3ZBRjROdHZpNmJTR3lWV2gxa2JwSWRrMVlLOCIsIm1hYyI6IjMwNjM2Njg5MzJhNWExNmNkMzliZDkzMTAzYjQxZjYwOTYzNDc1YmI3MzhiNTAxZmI1ZjEzM2YyNGFiMDkxMWYifQ%3D%3D; gkld_session=eyJpdiI6IklKWXdUalwvdTJVTkhJSkpsQzBTV1NBPT0iLCJ2YWx1ZSI6IkJWZkQ0aUdMeEVqT0ptUkJGM3B6SW5aVmdSSTMzUHl3VFN6a3NDRmZUZWxvY0xIRVZONFhiOTA3SDRHZ2NQZnpzQzBzRXhTZEtYZzkzbWNiUXBiZFwvazdiU0VrV0pMWldRNVRKdWZFVTg4UXVrSmdqOWlOWmRUdjlvbjJ1blY1UyIsIm1hYyI6Ijk0N2QwNGI0MWUzZDJmZmM1OGZiYTJjOTk5OWI0OGFiZjQ2NjFmODQyZTM4MGZlMmE1OWJiNDM3OWE4NDRhYjI
ifQ%3D%3D"
,
];
$refer
=
$url
;
...
...
@@ -162,7 +187,7 @@ class BatchExportUserPhone extends Command
// dd($content, $url);
// Storage::put('1.html', $content);
$ret
=
BaseService
::
htmlExplain
(
$url
,
$content
,
$areaName
);
sleep
(
7
);
sleep
(
6
);
if
(
$lianxuNone
>=
3
){
...
...
app/Console/Commands/test.php
View file @
503eaa36
...
...
@@ -6,7 +6,7 @@ use Illuminate\Console\Command;
use
GuzzleHttp\Client
;
use
GuzzleHttp\Cookie\CookieJar
;
use
Illuminate\Support\Facades\Cache
;
use
App\Http\Services\Decryptor
;
class
test
extends
Command
{
protected
$signature
=
'crawl:dynamic-token'
;
...
...
@@ -20,39 +20,40 @@ class test extends Command
public
function
handle
()
{
// 示例加密数据
$encryptedData
=
"K0retCPdI@kG41DJ5wRYc-W8RT9NsePsLMAknEnd4g7KeOxkrlicfiHfb*tts8VRwSzrjpguE1CYpfZH0\/8epwIayZrEoL5qFWBbhaBhzwLmYvuqRdBhQdWJ8yHbo362R8iHfiD+q66DYjwMZp2WPKtCjzQVtGHXU1qdFWm\/JSFeDjuYk8ZRivh0TTuuqUjlek+2RF8e16bIVFfScB+cLrImNFo1l\/fL7bPtpFDyZRdWVXnKZ\/iRLiLZAiAtTqB6VgpanceHU69NMLwqn3p7fQ6rqeZc6qINxZSOaMf53S\/Yqw2J5g2PzlQ+xpyhVv\/WiuIATqFy1ZWWWg9UeHbWa3PAywmZ201dvOMZatR03RC\/CsOVRKYNkFVYoIg7ECPKDMdZh70R9vwWYqivFde07G8+iStPC\/egJMp64GodBh2aKqwyv8\/3Hmy8UCB8zPobJGeKHE0nNUUpcCsBAMXKs4lQWqtyISFTXqEcXB\/ciISWjSN68k0gdCEoE0xDOdIayWsAS4ptwRTz\/SCNz7GHgcSIzar3RsKXVlvo\/R9jVCRekR5\/hLIovp\/phgHRMiKBSKp9\/HrvwN0rpsZULDy7iwmkX95C4RCWs8JDo3dczdeoVJz9HLCdInF9Fsi3K7+pEeTxNtL5pEBnjclvE1LCL9Nz+dGtt5RfIWADUyQkzQcbqvaPz8\/FTIINMC9hYYgTOx93nurSdG1bgjZTgMxxcGsHEGgxuGAc7U+hxRdALOHQbiFr9K1h4+PN1Ik79T6POvtoowPTO92goA+muISy8DWls2Whal9\/TFlAvqXI1rRzbJ6EU8GWHl1qtGRBoV7vGFanfbWPubOh1A74i2VysQdQJXh0wuHDU88h5otDNuvLLs39yhYVJfZJQCM9QnlLgEHtxU7sWZP+39pCDzn33mU6YxTxp6CkzJeWUoy555LtzSoHvwmOwQyiDG7XhnHYt41WYO7mTT7B+eJH7ev1mHoqPUAuFZa+yv27V5FifE8+\/3LjNdZ+tfXdCRAdOEn7H7gw\/MVuBpuFaVlbtSUiNNksVTzbCRD9xMzuTvClvJwWmS9q\/Iu37yx7CyPiHEG\/0KSVOKEFn8GPKjpjw2FoQO8ScDFKLclhNIzo0qIUAdmSHOnn8VzDFUrfNgTON1sDSQ2gA\/FTYNYl+oaZL1l57LoCviaJHdbcGQZ2qZbsqmPGaF5BkvwMJ6bWJWDpMA\/8VcpPYj\/mvRiA+aJfeB\/U61YwNDGJ17Ac8SGOCBItL5eEchk993Mq2FR\/ziKNKZb10r5IRsgns5pPn7GoJT\/83vyprEQuMrNAu7xuL\/pcda1xTH35eFS9E5A\/CvdxeYgjCGl0gvpSOAxCSF5HtlFgZ\/PCAVyTMyKMZbKwdxA2LPZ+nQpM19mwRKiKvo57Giu74wCjUhvz\/95kAUWdsgQUa6dYsDPuqKhL5Qr6dN2DNojpb8WLZ3zhZIwPZHWIF5ogqiQwZPwlryadIo1tqveqZbTQ0SOJ8HJZ0DuDTWUXrzAQPWgCw\/\/6aWtSmuOgUGDBeVM2BxK77LUHeYngIuKyb4wiPymx8OHo\/KZWTymcmyrLi5+pQMsoITVgq5oDNZpUSFzIfnqqjduplmszyKphHddmfcBqb5qFaeSjnH7XQNWmYiigo8moCN3KveJHcoBcPnfJiyaOjfvpLaUgnuZYbAnIW5bayPQZKiIphTB1CnMN9GQOwuOlhcF3\/dMdIyjofne7wzKPIm1dgaQtGOvfdU5NZYzmczniHwhDuQpLs0J8TZg1\/DZ1Y6HuhMDH6hycOm7LTE+XvDaQLrKWpKT5\/ObVNMQWtQsyTSuXuvnYqJV6+8DUvs70CmislkNa4LeCLisQE2F1hwbBcMR9ZY4Wuw9MWnaqoYwm8z5GYZkVoKta125fk\/GcdmxvNfX2GT8iUAwIaubr0u2BXKV4ncIIUAhkW4kynIfYY\/18eTmrpaZUVhfcuFSZvSYWQEDUqPJM8s+8rnp6uMMCpVIGw3l5+PW2gkOMC1ncbpGvB4MXSoILrMQQXcj2fzQ8T\/uuZAwqZgGjuE141PuHRd4aJp8MCac5rJIQ9aE1wLzheZHVqG6602TaugjPray74ztjb5XgPzNOvZqF2HpUuAogyVYza++1BuN7uCAV0ofCdrA73iILOKZm0j0+mOi94TngskhdfoX3opS1Pjv17hSH3Jyg29B\/6JG7FHKzuC+78HRBxdxS16mjTG\/6d+rv\/CzBq5YKkJhe7uhy3v1IGrK7d0S9LZklJmcF+jIO8vXc6VHsaytmi5zXB\/+dUAExuz02ubupFFdl\/mw4xyTaG1BTcw+ScRihz7ai0IeC7e8T1RsFsj5ZlWIT6GDyf9TQnesgpOViCa\/kfcJb6LwJF3EN0T4pJYWHtlZ5lGcF6sB3fCMkk1Ry+LDVrtyQqLsSyuOV\/\/Hl3bW16ikfyYMRDlJZE+8n0vAfJcyN5\/5s4kUb2Ir4gIIZp5O8N7JKvGJRiw9XNiK\/qlHFUVrZOIFeWGR0akiGp0OM7\/kHs4QsZnwUp3bxLk4Nu4w5fTurCTOyjdJ+I6pGjnhjTAZW32NGkqrythcOfppcfjsw\/BuODICaidoGVUUtJruyGpqqomehxGQiueJXsAWOAFrDJ5OulO79kcIKxop7UOE8DQUAZnQOBeQe0UmkgN6WoRcQ41wRP1w19j9b1apw4k\/j\/MYefPFMTfaJiTQNPO5MKo3uqk8nwx3tXJATmxe+F+wYig9ZrdsmEWs5kt+RCY0xISn9K02\/S3WacQArfmj02vdsyzBrUBkP0JrZCo6H7VCdIrFdAk1nsB67+DzkDTrJwlnb7G0OUnMH8QBv5oPHU2CJc\/YHxme+zEszsg6MEedNdrEM+oPEsP3LY2mlqWcSN6GljqqctBSk0QDEBA84W9B+Y1whW09w46isyLsulrctBu8ETqvpeATEeFJWHvirVXVYv3er1+H84KV2uZxQXRYv15Sbq44\/msseWRDsdVcmP5ibihwAOFUOrr9DwnR+urX5CoSc\/EwJJ+t6WV+WWziUY4P2h\/52txc4dlB1meT8x0Jy9iWmMtTsNKv\/xscsv7jyrmoSfaKEBjWcUvkNi0iJjffQP55qxNbQnk4aUh42u7euSXjgS3YKwCdf+J3Tyif1JDEgd02hNhdy+bQ6i3WiDPZ8hVwcJHlAEY7E+VZ5Qc9zUyNaV6G1CjtAp4oiEYQgXKL55DbmMAD+UHeWCNf5jIPG0dZcdeSTTeDFBKLICVRSLxjIsIMYNwoISWmVoSJJRbR42c4Y6XlppE7gbAqUsr+dUiq2jrb70xCrlu5DJyOQZXOAp2WocSJEJsA6Wmduv10XPQRMm\/SSwVWgtRwlHszr5pBZUvqYMkggA6xzDeJ9YsqKf99x6C3gHROgeeS6uRiWVZtyVh6p7uHTtxG7aL6qEaQwsyheC09tI+3Yr5isaLxVBUDLYtG6mjaqKdNImtGEj9WxO9kuEZmqdBE4Au40jGGMRm5OTF+uTKdTzN2z6ZSJAv+\/EjZULRHmpkShdiWaH5qvJ7PxlxYpQ=="
;
try
{
// 方法1:简单解密
$decrypted
=
Decryptor
::
decrypt
(
$encryptedData
);
// 方法2:获取详细结果
$result
=
Decryptor
::
decryptWithResult
(
$encryptedData
);
// https://api.gongkaoleida.com/api/FrontBackSecure/article/detailUserPc?appid=uqsFgLOVbuPrfn1v&articleId=2716421&from_device=h5×tamp=1765185164&token=569fff2952c0317c29f3308df15cd75d11d15fmern1cdeol3f9tcfo31cdnb3o9o5yd03&userId=26239811&sign=a11b430c7a5610930bd603b7c6d05df5
// https://api.gongkaoleida.com/api/FrontBackSecure/article/detailUserPc?appid=uqsFgLOVbuPrfn1v&articleId=2455556&from_device=h5×tamp=1765185842&token=c1c6a9fdf0bea1fe6a46314e3dd8c17111d156qkkr1cdejzbb1znjle1cdn6hw5gc9gxe&userId=11317419&sign=ebf4a4832ade58f2a45b772d273a838c
// https://api.gongkaoleida.com/api/FrontBackSecure/article/detailUserPc?appid=uqsFgLOVbuPrfn1v&articleId=2305006&from_device=h5×tamp=1765251531&token=569fff2952c0317c29f3308df15cd75d11d15fmern1cdeol3f9tcfo31cdnb3o9o5yd03&userId=26239811&sign=31b7c0372a445598bbbe40aabb987acc
$s
=
'appid=uqsFgLOVbuPrfn1v&articleId=2305006&from_device=h5×tamp=1765251531&token=569fff2952c0317c29f3308df15cd75d11d15fmern1cdeol3f9tcfo31cdnb3o9o5yd03&userId=26239811'
;
$s
.=
'&secret=Hf6yn1JPb1QZxniWhIPv1IrHbWeLh2e8'
;
dd
(
md5
(
urlencode
(
$s
)),
$s
);
dd
(
333
);
$this
->
initializeClient
();
// 1. 启动会话
$this
->
startSession
();
// 2. 爬取目标页面
$url
=
$this
->
argument
(
'url'
);
$result
=
$this
->
smartRequest
(
$url
);
dd
(
$result
);
dd
(
$result
);
if
(
$result
[
'success'
])
{
$this
->
processResult
(
$result
);
if
(
$result
[
'success'
])
{
dd
([
'success'
=>
true
,
'data'
=>
json_decode
(
$result
[
'data'
],
true
),
// 假设返回JSON
'original'
=>
$encryptedData
]);
}
else
{
dd
([
'success'
=>
false
,
'error'
=>
$result
[
'error'
],
'exception'
=>
$result
[
'exception'
]
?
$result
[
'exception'
]
->
getMessage
()
:
null
],
400
);
}
}
catch
(
\Exception
$e
)
{
dd
([
'success'
=>
false
,
'error'
=>
'Decryption failed'
,
'message'
=>
$e
->
getMessage
()
],
500
);
}
return
0
;
}
...
...
app/Http/Services/Decryptor.php
0 → 100755
View file @
503eaa36
<?php
namespace
App\Http\Services
;
use
Exception
;
/**
* 对应Java的C21499a类
* 实现AES解密、GZIP解压和特定数据格式处理
*/
class
Decryptor
{
/**
* 主要解密方法,对应Java的m111788d/d方法
* @param string $data 加密数据
* @return string 解密后的数据
*/
public
static
function
decrypt
(
string
$data
)
:
string
{
$result
=
self
::
decryptWithResult
(
$data
);
return
$result
[
'success'
]
?
$result
[
'data'
]
:
$data
;
}
/**
* 详细的解密方法,返回包含状态的结果
* @param string $data 加密数据
* @return array 解密结果
*/
public
static
function
decryptWithResult
(
string
$data
)
:
array
{
$sb
=
$data
;
$iIndexOf
=
strpos
(
$data
,
"dI@kG"
);
$str
=
"fdi7/mzlL2iar+hn0tz0Ev/Y8enbWJpt"
;
$z
=
false
;
$string
=
null
;
if
(
$iIndexOf
!==
false
)
{
$i2
=
$iIndexOf
+
5
;
$i3
=
$i2
+
1
;
if
(
$i2
<
strlen
(
$data
))
{
$strValueOf
=
$data
[
$i2
];
$intValue
=
intval
(
$strValueOf
);
if
(
$intValue
==
1
||
$intValue
==
3
)
{
$sb
=
substr
(
$sb
,
0
,
$iIndexOf
)
.
substr
(
$sb
,
$i3
);
$string
=
$sb
;
if
(
$intValue
==
3
)
{
$z
=
true
;
}
}
elseif
((
$intValue
==
2
||
$intValue
==
4
)
&&
$i3
<
strlen
(
$data
))
{
$i4
=
intval
(
$data
[
$i3
]);
$sb
=
substr
(
$sb
,
0
,
$iIndexOf
)
.
substr
(
$sb
,
$i2
+
2
);
$string2
=
$sb
;
$i5
=
$i4
+
64
;
if
(
$i5
<
strlen
(
$string2
))
{
$strSubstring
=
substr
(
$string2
,
$i4
,
64
);
$strSubstring2
=
(
$i4
%
2
==
1
)
?
substr
(
$strSubstring
,
0
,
32
)
:
substr
(
$strSubstring
,
32
,
32
);
$sb2
=
''
;
for
(
$i6
=
0
;
$i6
<
strlen
(
$strSubstring2
);
$i6
++
)
{
$cCharAt
=
$strSubstring2
[
$i6
];
if
(
ctype_digit
(
$cCharAt
))
{
$sb2
.=
((
intval
(
$cCharAt
)
<<
$i4
)
%
10
);
}
else
{
$sb2
.=
$cCharAt
;
}
}
$string3
=
$sb2
;
$sb3
=
substr
(
$string2
,
0
,
$i4
)
.
substr
(
$string2
,
$i5
);
$string
=
$sb3
;
$z
=
(
$intValue
==
4
);
$str
=
$string3
;
}
else
{
$str
=
"shdshdi&1232sndh+*aspqw"
;
$string
=
null
;
}
}
}
}
if
(
$string
===
null
)
{
return
[
'success'
=>
false
,
'data'
=>
$data
,
'error'
=>
'No valid data to decrypt'
,
'exception'
=>
null
];
}
try
{
$keyBytes
=
self
::
stringToBytes
(
$str
);
$aVar
=
self
::
decryptCore
(
$string
,
$keyBytes
,
$z
);
if
(
$aVar
[
'success'
])
{
return
$aVar
;
}
else
{
if
(
$aVar
[
'exception'
]
!==
null
)
{
// 记录异常,但继续执行
error_log
(
"Decryption exception: "
.
$aVar
[
'exception'
]
->
getMessage
());
}
return
[
'success'
=>
false
,
'data'
=>
$data
,
'error'
=>
$aVar
[
'error'
],
'exception'
=>
$aVar
[
'exception'
]
];
}
}
catch
(
Exception
$e
)
{
error_log
(
"Decryption failed: "
.
$e
->
getMessage
());
return
[
'success'
=>
false
,
'data'
=>
$data
,
'error'
=>
'Decryption failed: '
.
$e
->
getMessage
(),
'exception'
=>
$e
];
}
}
/**
* 核心解密方法,对应Java的m111790b/b方法
* @param string $encryptText 加密文本
* @param array $key 密钥字节数组
* @param bool $isGzip 是否GZIP压缩
* @return array 解密结果
* @throws Exception
*/
private
static
function
decryptCore
(
string
$encryptText
,
array
$key
,
bool
$isGzip
)
:
array
{
try
{
// Base64解码
$bArrDecode
=
base64_decode
(
$encryptText
);
if
(
$bArrDecode
===
false
)
{
throw
new
Exception
(
"Base64 decode failed"
);
}
$bArrDecodeBytes
=
self
::
stringToBytes
(
$bArrDecode
);
$length
=
count
(
$bArrDecodeBytes
);
// 检查长度是否为16的倍数
if
(
$length
%
16
!=
0
)
{
throw
new
Exception
(
"need 16X"
);
}
// 准备AES解密
$keyString
=
self
::
bytesToString
(
$key
);
$decrypted
=
openssl_decrypt
(
$bArrDecode
,
'AES-128-ECB'
,
$keyString
,
OPENSSL_RAW_DATA
);
if
(
$decrypted
===
false
)
{
throw
new
Exception
(
"AES decryption failed"
);
}
$bArr
=
self
::
stringToBytes
(
$decrypted
);
// 应用自定义块处理
$bArr2
=
array_fill
(
0
,
$length
,
0
);
// 处理第一个块
$firstBlock
=
array_slice
(
$bArr
,
0
,
16
);
$transformedFirst
=
self
::
transformKey
(
$firstBlock
);
array_splice
(
$bArr2
,
0
,
16
,
$transformedFirst
);
// 处理后续块
$i2
=
16
;
while
(
$i2
<
$length
)
{
$i3
=
$i2
+
16
;
$block
=
array_slice
(
$bArr
,
$i2
,
16
);
$transformedBlock
=
self
::
transformKey
(
$block
);
for
(
$i4
=
0
;
$i4
<
16
&&
(
$i2
+
$i4
)
<
$length
;
$i4
++
)
{
$bArr2
[
$i2
+
$i4
]
=
$transformedBlock
[
$i4
]
^
$bArrDecodeBytes
[(
$i2
-
16
)
+
$i4
];
}
$i2
=
$i3
;
}
// 转换为字符串
$decryptedString
=
self
::
bytesToString
(
$bArr2
);
$strTrim
=
trim
(
$decryptedString
);
// 如果是GZIP压缩,则解压
if
(
$isGzip
)
{
$decompressed
=
self
::
gzipDecompress
(
$bArr2
);
if
(
$decompressed
!==
null
)
{
$strTrim
=
$decompressed
;
}
}
return
[
'success'
=>
true
,
'data'
=>
$strTrim
,
'error'
=>
null
,
'exception'
=>
null
];
}
catch
(
Exception
$e
)
{
return
[
'success'
=>
false
,
'data'
=>
null
,
'error'
=>
'failed: '
.
$e
->
getMessage
(),
'exception'
=>
$e
];
}
}
/**
* 密钥转换方法,对应Java的m111787c方法
* @param array $block 16字节块
* @return array 转换后的16字节块
*/
private
static
function
transformKey
(
array
$block
)
:
array
{
$result
=
array_fill
(
0
,
16
,
0
);
for
(
$i
=
0
;
$i
<
16
;
$i
++
)
{
$value
=
$block
[
$i
]
&
0xFF
;
$xorred
=
$value
^
$i
;
$shifted
=
$xorred
>>
1
;
$result
[
$i
]
=
((
$xorred
<<
7
)
|
$shifted
)
&
0xFF
;
}
return
$result
;
}
/**
* GZIP解压方法,对应Java的m111786a方法
* @param array $data 字节数组
* @return string|null 解压后的字符串
*/
private
static
function
gzipDecompress
(
array
$data
)
:
?
string
{
try
{
$bytesString
=
self
::
bytesToString
(
$data
);
// 尝试使用gzdecode解压
$decompressed
=
@
gzdecode
(
$bytesString
);
if
(
$decompressed
===
false
)
{
// 如果失败,尝试使用gzinflate
$decompressed
=
@
gzinflate
(
substr
(
$bytesString
,
10
,
-
8
));
if
(
$decompressed
===
false
)
{
throw
new
Exception
(
"GZIP decompression failed"
);
}
}
return
$decompressed
;
}
catch
(
Exception
$e
)
{
error_log
(
"GZIP decompression error: "
.
$e
->
getMessage
());
return
null
;
}
}
/**
* 辅助方法:字符串转字节数组
* @param string $str 字符串
* @return array 字节数组
*/
private
static
function
stringToBytes
(
string
$str
)
:
array
{
return
array_values
(
unpack
(
'C*'
,
$str
));
}
/**
* 辅助方法:字节数组转字符串
* @param array $bytes 字节数组
* @return string 字符串
*/
private
static
function
bytesToString
(
array
$bytes
)
:
string
{
$chars
=
array_map
(
'chr'
,
$bytes
);
return
implode
(
''
,
$chars
);
}
/**
* 对应Java的m111789e方法(虽然未使用,但保留)
* @param array $signedBytes 有符号字节数组
* @return array 无符号字节数组
*/
public
static
function
toUnsignedBytes
(
array
$signedBytes
)
:
array
{
$result
=
[];
foreach
(
$signedBytes
as
$byte
)
{
$result
[]
=
$byte
&
0xFF
;
}
return
$result
;
}
}
\ No newline at end of file
app/Http/Services/GgongkaoLeidaCrawler.php
deleted
100755 → 0
View file @
9ba65111
<?php
namespace
App\Http\Services
;
use
GuzzleHttp\Client
;
use
GuzzleHttp\Cookie\CookieJar
;
use
GuzzleHttp\HandlerStack
;
use
GuzzleHttp\Middleware
;
use
Illuminate\Support\Facades\Cache
;
use
Illuminate\Support\Facades\Log
;
class
GgongkaoLeidaCrawler
{
private
$client
;
private
$baseUrl
=
'https://www.gongkaoleida.com'
;
private
$cookieJar
;
private
$sessionActive
=
false
;
private
$lastCookieUpdate
=
null
;
private
$cookieRefreshInterval
=
25
;
private
$requestDelay
=
2
;
public
function
__construct
()
{
$this
->
initializeClient
();
}
private
function
initializeClient
()
:
void
{
$this
->
cookieJar
=
new
CookieJar
();
$stack
=
HandlerStack
::
create
();
$stack
->
push
(
Middleware
::
cookies
(
$this
->
cookieJar
));
$this
->
client
=
new
Client
([
'base_uri'
=>
$this
->
baseUrl
,
'handler'
=>
$stack
,
'cookies'
=>
$this
->
cookieJar
,
'timeout'
=>
30
,
'connect_timeout'
=>
10
,
]);
}
/**
* 初始化会话
*/
public
function
initializeSession
()
:
bool
{
try
{
Log
::
info
(
'初始化公考雷达爬虫会话...'
);
// 访问首页获取初始cookie
$response
=
$this
->
client
->
get
(
'/'
,
[
'headers'
=>
$this
->
getDefaultHeaders
(),
]);
$this
->
updateCookiesFromResponse
(
$response
);
$this
->
sessionActive
=
true
;
$this
->
lastCookieUpdate
=
time
();
Log
::
info
(
'会话初始化完成'
,
[
'cookie_count'
=>
count
(
$this
->
cookieJar
->
toArray
())
]);
return
true
;
}
catch
(
\Exception
$e
)
{
Log
::
error
(
'会话初始化失败: '
.
$e
->
getMessage
());
return
false
;
}
}
/**
* 使用特定的cookie字符串初始化
*/
public
function
initializeWithCookieString
(
string
$cookieString
)
:
bool
{
try
{
$cookies
=
$this
->
parseCookieString
(
$cookieString
);
foreach
(
$cookies
as
$name
=>
$value
)
{
$cookie
=
new
\GuzzleHttp\Cookie\SetCookie
([
'Name'
=>
$name
,
'Value'
=>
$value
,
'Domain'
=>
'www.gongkaoleida.com'
,
'Path'
=>
'/'
,
]);
$this
->
cookieJar
->
setCookie
(
$cookie
);
}
$this
->
sessionActive
=
true
;
$this
->
lastCookieUpdate
=
time
();
Log
::
info
(
'使用cookie字符串初始化成功'
,
[
'cookie_count'
=>
count
(
$cookies
),
'keys'
=>
array_keys
(
$cookies
)
]);
return
true
;
}
catch
(
\Exception
$e
)
{
Log
::
error
(
'使用cookie字符串初始化失败: '
.
$e
->
getMessage
());
return
false
;
}
}
/**
* 爬取单个页面
*/
public
function
crawl
(
string
$url
)
:
array
{
if
(
!
$this
->
sessionActive
)
{
$this
->
initializeSession
();
}
// 确保cookie是新鲜的
$this
->
ensureFreshCookie
();
// 请求延迟
sleep
(
$this
->
requestDelay
);
try
{
$fullUrl
=
strpos
(
$url
,
'http'
)
===
0
?
$url
:
$this
->
baseUrl
.
$url
;
Log
::
info
(
'开始爬取: '
.
$fullUrl
);
$response
=
$this
->
client
->
get
(
$fullUrl
,
[
'headers'
=>
$this
->
getRequestHeaders
(
$fullUrl
),
'allow_redirects'
=>
[
'max'
=>
5
,
'strict'
=>
true
,
'referer'
=>
true
,
],
]);
// 更新cookie
$this
->
updateCookiesFromResponse
(
$response
);
$statusCode
=
$response
->
getStatusCode
();
$body
=
$response
->
getBody
()
->
getContents
();
if
(
$statusCode
===
200
&&
$this
->
isValidResponse
(
$body
))
{
Log
::
info
(
'爬取成功'
,
[
'url'
=>
$fullUrl
,
'status'
=>
$statusCode
,
'length'
=>
strlen
(
$body
)
]);
return
[
'success'
=>
true
,
'status'
=>
$statusCode
,
'content'
=>
$body
,
'cookies'
=>
$this
->
getCookieSummary
(),
];
}
else
{
throw
new
\Exception
(
"无效响应,状态码:
{
$statusCode
}
"
);
}
}
catch
(
\Exception
$e
)
{
Log
::
error
(
'爬取失败: '
.
$e
->
getMessage
());
if
(
$this
->
isSessionError
(
$e
))
{
$this
->
sessionActive
=
false
;
}
return
[
'success'
=>
false
,
'error'
=>
$e
->
getMessage
(),
'cookies'
=>
$this
->
getCookieSummary
(),
];
}
}
/**
* 批量爬取
*/
public
function
batchCrawl
(
array
$urls
,
int
$delayBetween
=
5
)
:
array
{
$results
=
[];
foreach
(
$urls
as
$index
=>
$url
)
{
Log
::
info
(
"批量爬取进度: "
.
(
$index
+
1
)
.
"/"
.
count
(
$urls
));
$result
=
$this
->
crawl
(
$url
);
$results
[
$url
]
=
$result
;
// 延迟
if
(
$index
<
count
(
$urls
)
-
1
)
{
$sleepTime
=
$delayBetween
+
mt_rand
(
1
,
3
);
sleep
(
$sleepTime
);
}
}
return
$results
;
}
/**
* 解析HTML并提取数据
*/
public
function
extractJobsFromHtml
(
string
$html
)
:
array
{
$dom
=
new
\DOMDocument
();
@
$dom
->
loadHTML
(
mb_convert_encoding
(
$html
,
'HTML-ENTITIES'
,
'UTF-8'
));
$xpath
=
new
\DOMXPath
(
$dom
);
$jobs
=
[];
// 根据公考雷达的实际HTML结构调整这些选择器
$jobElements
=
$xpath
->
query
(
'//div[contains(@class, "job-item") or contains(@class, "position-item")]'
);
if
(
$jobElements
->
length
===
0
)
{
// 尝试其他可能的选择器
$jobElements
=
$xpath
->
query
(
'//li[contains(@class, "job") or contains(@class, "position")]'
);
}
foreach
(
$jobElements
as
$jobElement
)
{
$job
=
[
'title'
=>
''
,
'company'
=>
''
,
'location'
=>
''
,
'salary'
=>
''
,
'publish_time'
=>
''
,
'details_url'
=>
''
,
];
// 提取职位标题
$titleNodes
=
$xpath
->
query
(
'.//h3 | .//h4 | .//a[contains(@class, "title")]'
,
$jobElement
);
if
(
$titleNodes
->
length
>
0
)
{
$job
[
'title'
]
=
trim
(
$titleNodes
->
item
(
0
)
->
textContent
);
}
// 提取公司名称
$companyNodes
=
$xpath
->
query
(
'.//div[contains(@class, "company")] | .//span[contains(@class, "company")]'
,
$jobElement
);
if
(
$companyNodes
->
length
>
0
)
{
$job
[
'company'
]
=
trim
(
$companyNodes
->
item
(
0
)
->
textContent
);
}
// 提取工作地点
$locationNodes
=
$xpath
->
query
(
'.//span[contains(@class, "location") or contains(@class, "city")]'
,
$jobElement
);
if
(
$locationNodes
->
length
>
0
)
{
$job
[
'location'
]
=
trim
(
$locationNodes
->
item
(
0
)
->
textContent
);
}
// 提取发布时间
$timeNodes
=
$xpath
->
query
(
'.//span[contains(@class, "time") or contains(@class, "date")]'
,
$jobElement
);
if
(
$timeNodes
->
length
>
0
)
{
$job
[
'publish_time'
]
=
trim
(
$timeNodes
->
item
(
0
)
->
textContent
);
}
// 提取详情链接
$linkNodes
=
$xpath
->
query
(
'.//a[contains(@href, "/job/") or contains(@href, "/position/")]'
,
$jobElement
);
if
(
$linkNodes
->
length
>
0
)
{
$href
=
$linkNodes
->
item
(
0
)
->
getAttribute
(
'href'
);
$job
[
'details_url'
]
=
strpos
(
$href
,
'http'
)
===
0
?
$href
:
$this
->
baseUrl
.
$href
;
}
$jobs
[]
=
$job
;
}
return
$jobs
;
}
/**
* 保存HTML内容到文件
*/
public
function
saveHtmlToFile
(
string
$html
,
string
$url
)
:
string
{
$filename
=
'gongkaoleida_'
.
date
(
'Ymd_His'
)
.
'_'
.
md5
(
$url
)
.
'.html'
;
$path
=
storage_path
(
'app/crawled/'
.
$filename
);
// 确保目录存在
if
(
!
is_dir
(
dirname
(
$path
)))
{
mkdir
(
dirname
(
$path
),
0755
,
true
);
}
file_put_contents
(
$path
,
$html
);
return
$path
;
}
/**
* 保存数据到JSON文件
*/
public
function
saveDataToJson
(
array
$data
,
string
$filename
)
:
string
{
$path
=
storage_path
(
'app/crawled/'
.
$filename
);
// 确保目录存在
if
(
!
is_dir
(
dirname
(
$path
)))
{
mkdir
(
dirname
(
$path
),
0755
,
true
);
}
file_put_contents
(
$path
,
json_encode
(
$data
,
JSON_PRETTY_PRINT
|
JSON_UNESCAPED_UNICODE
));
return
$path
;
}
/**
* 获取当前会话信息
*/
public
function
getSessionInfo
()
:
array
{
return
[
'active'
=>
$this
->
sessionActive
,
'last_cookie_update'
=>
$this
->
lastCookieUpdate
?
date
(
'Y-m-d H:i:s'
,
$this
->
lastCookieUpdate
)
:
null
,
'cookie_count'
=>
count
(
$this
->
cookieJar
->
toArray
()),
];
}
/**
* 获取cookie摘要
*/
public
function
getCookieSummary
()
:
array
{
$cookies
=
$this
->
cookieJar
->
toArray
();
$summary
=
[];
foreach
(
$cookies
as
$cookie
)
{
$name
=
$cookie
[
'Name'
]
??
'unknown'
;
$value
=
$cookie
[
'Value'
]
??
''
;
$summary
[
$name
]
=
strlen
(
$value
)
>
30
?
substr
(
$value
,
0
,
30
)
.
'...'
:
$value
;
}
return
$summary
;
}
/**
* 确保cookie是新鲜的
*/
private
function
ensureFreshCookie
()
:
void
{
$currentTime
=
time
();
if
(
$this
->
lastCookieUpdate
===
null
||
(
$currentTime
-
$this
->
lastCookieUpdate
)
>=
$this
->
cookieRefreshInterval
)
{
Log
::
info
(
'刷新Cookie...'
);
$this
->
refreshCookie
();
}
}
/**
* 刷新cookie
*/
private
function
refreshCookie
()
:
void
{
try
{
$response
=
$this
->
client
->
get
(
'/'
,
[
'headers'
=>
$this
->
getDefaultHeaders
(),
]);
$this
->
updateCookiesFromResponse
(
$response
);
$this
->
lastCookieUpdate
=
time
();
}
catch
(
\Exception
$e
)
{
Log
::
error
(
'刷新Cookie失败: '
.
$e
->
getMessage
());
throw
$e
;
}
}
/**
* 从响应更新cookie
*/
private
function
updateCookiesFromResponse
(
$response
)
:
void
{
$cookieHeaders
=
$response
->
getHeader
(
'Set-Cookie'
);
foreach
(
$cookieHeaders
as
$header
)
{
$cookie
=
$this
->
parseSetCookieHeader
(
$header
);
if
(
$cookie
)
{
$this
->
cookieJar
->
setCookie
(
$cookie
);
}
}
}
/**
* 解析Set-Cookie头
*/
private
function
parseSetCookieHeader
(
string
$header
)
:
?
\GuzzleHttp\Cookie\SetCookie
{
$parts
=
explode
(
';'
,
$header
);
$nameValue
=
explode
(
'='
,
trim
(
$parts
[
0
]),
2
);
if
(
count
(
$nameValue
)
!==
2
)
{
return
null
;
}
$name
=
trim
(
$nameValue
[
0
]);
$value
=
trim
(
$nameValue
[
1
]);
$cookieData
=
[
'Name'
=>
$name
,
'Value'
=>
$value
,
'Domain'
=>
'www.gongkaoleida.com'
,
'Path'
=>
'/'
,
];
for
(
$i
=
1
;
$i
<
count
(
$parts
);
$i
++
)
{
$part
=
trim
(
$parts
[
$i
]);
if
(
strpos
(
$part
,
'='
)
!==
false
)
{
list
(
$attrName
,
$attrValue
)
=
explode
(
'='
,
$part
,
2
);
$attrName
=
strtolower
(
trim
(
$attrName
));
switch
(
$attrName
)
{
case
'domain'
:
$cookieData
[
'Domain'
]
=
$attrValue
;
break
;
case
'path'
:
$cookieData
[
'Path'
]
=
$attrValue
;
break
;
case
'expires'
:
$cookieData
[
'Expires'
]
=
strtotime
(
$attrValue
);
break
;
case
'max-age'
:
$cookieData
[
'Max-Age'
]
=
(
int
)
$attrValue
;
break
;
}
}
else
{
if
(
strtolower
(
$part
)
===
'secure'
)
{
$cookieData
[
'Secure'
]
=
true
;
}
elseif
(
strtolower
(
$part
)
===
'httponly'
)
{
$cookieData
[
'HttpOnly'
]
=
true
;
}
}
}
return
new
\GuzzleHttp\Cookie\SetCookie
(
$cookieData
);
}
/**
* 解析cookie字符串
*/
private
function
parseCookieString
(
string
$cookieString
)
:
array
{
$cookies
=
[];
$pairs
=
explode
(
';'
,
$cookieString
);
foreach
(
$pairs
as
$pair
)
{
$pair
=
trim
(
$pair
);
if
(
empty
(
$pair
))
continue
;
$parts
=
explode
(
'='
,
$pair
,
2
);
if
(
count
(
$parts
)
===
2
)
{
$name
=
trim
(
$parts
[
0
]);
$value
=
trim
(
$parts
[
1
]);
$cookies
[
$name
]
=
$value
;
}
}
return
$cookies
;
}
/**
* 获取默认请求头
*/
private
function
getDefaultHeaders
()
{
return
[
'User-Agent'
=>
$this
->
getRandomUserAgent
(),
'Accept'
=>
'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8'
,
'Accept-Language'
=>
'zh-CN,zh;q=0.9'
,
'Accept-Encoding'
=>
'gzip, deflate, br'
,
];
}
/**
* 获取请求头
*/
private
function
getRequestHeaders
(
string
$url
)
{
$headers
=
$this
->
getDefaultHeaders
();
if
(
strpos
(
$url
,
$this
->
baseUrl
)
===
0
)
{
$headers
[
'Referer'
]
=
$this
->
baseUrl
.
'/'
;
}
return
$headers
;
}
/**
* 获取随机User-Agent
*/
private
function
getRandomUserAgent
()
:
string
{
$userAgents
=
[
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36'
,
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0'
,
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36'
,
];
return
$userAgents
[
array_rand
(
$userAgents
)];
}
/**
* 检查响应是否有效
*/
private
function
isValidResponse
(
string
$body
)
:
bool
{
if
(
strpos
(
$body
,
'403 Forbidden'
)
!==
false
||
strpos
(
$body
,
'Access Denied'
)
!==
false
||
strpos
(
$body
,
'机器人检测'
)
!==
false
)
{
return
false
;
}
if
(
strpos
(
$body
,
'公务员'
)
!==
false
||
strpos
(
$body
,
'公考雷达'
)
!==
false
||
strlen
(
$body
)
>
2000
)
{
return
true
;
}
return
false
;
}
/**
* 检查是否是会话错误
*/
private
function
isSessionError
(
\Exception
$e
)
:
bool
{
$message
=
$e
->
getMessage
();
return
strpos
(
$message
,
'419'
)
!==
false
||
strpos
(
$message
,
'403'
)
!==
false
||
strpos
(
$message
,
'401'
)
!==
false
;
}
}
\ No newline at end of file
public/t.php
View file @
503eaa36
...
...
@@ -130,4 +130,51 @@ if ($mainContent->length > 0) {
?>
\ No newline at end of file
?>
<?php
namespace
App\Http\Controllers
;
use
App\Http\Controllers\Controller
;
use
App\Utils\Decryptor
;
class
DecryptionController
extends
Controller
{
public
function
decryptData
()
{
// 示例加密数据
$encryptedData
=
"1V167UQdI@kG46qz9ERADHf7UcYGbEX/05GD/3mxnCSUlgSRrxT5UFfjKWS*+MYww..."
;
try
{
// 方法1:简单解密
$decrypted
=
Decryptor
::
decrypt
(
$encryptedData
);
// 方法2:获取详细结果
$result
=
Decryptor
::
decryptWithResult
(
$encryptedData
);
if
(
$result
[
'success'
])
{
return
response
()
->
json
([
'success'
=>
true
,
'data'
=>
json_decode
(
$result
[
'data'
],
true
),
// 假设返回JSON
'original'
=>
$encryptedData
]);
}
else
{
return
response
()
->
json
([
'success'
=>
false
,
'error'
=>
$result
[
'error'
],
'exception'
=>
$result
[
'exception'
]
?
$result
[
'exception'
]
->
getMessage
()
:
null
],
400
);
}
}
catch
(
\Exception
$e
)
{
return
response
()
->
json
([
'success'
=>
false
,
'error'
=>
'Decryption failed'
,
'message'
=>
$e
->
getMessage
()
],
500
);
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment