最近做爬虫时碰到了521错误,500开头的都是服务器错误;521错误码需要请求多次才能返回正确的结果;查看请求次数需要借助抓包工具,我自己使用Fiddler 4抓取到发送了三次请求才拿到结果,所以这就需要我们解析三次请求了。
1、Fiddler 抓取结果两次521、1次200拿到了结果,这就意味着我们需要提交三次请求才能拿到结果。
2、根据抓包看到必须三次请求才能拿到结果,我们逐一解析每次请求和响应。下面是正确拿到结果的请求头,红框内的cookie是重点。
1)、我们使用postman发送第一次请求,得到响应头Set-Cookie中的
__jsluid_s=ed2b28b0b03114218d2b0e03a1b56f12,此参数在第二次,第三次请求时加入cookie中(两次请求中此参数值保持不变);还有另一个参数就是返回值js代码(返回值中的js也可以在html中打印出来),使用ScriptEngineManager处理两个红竖杠中的js;得到__jsl_clearance_s=1628664214.493|-1|cx9ymjyfz6PzeXeREg6KcTa7P24%3D;(它后面的代码不需要)
2)、将__jsluid_s和__jsl_clearance_s放入到cookie中再次发送请求,会得到返回值是一大串js代码,跟第一次的返回值不一样,这个又多又乱,看到下图中的代码眼晕吧
别慌,咱们看重点,将返回值拉到最后,两个红竖杠之间才是我们想要的,所以上面那么一大堆代码我们可以不用管它;直接看重点,仔细看看这段是不是有点眼熟,这就是解密的关键。
首先我们看bts对象,是不是很像__jsl_clearance_s参数值,没错,这就是第三次请求的cookies参数的部分值(是一大部分),chars,ct,ha这几个对象是解密的关键,chars是随机的字母,ha是加密算法(MD5,sha1,sha256三种算法),ct是加密的结果,看到这,各位应该想到了解析方式;我们可以对比标准的数据__jsl_clearance_s=1628662388.812|0|nzqTL5FJzLGZp%2BuNyQfckpBkghQ%3D
__jsl_clearance_s = (bts数组1)+(chars随机两个字母)+(bts数组2),
这就是第二个参数值
3)、根据第一次得到的不变参数__jsluid_s与第二次得到的__jsl_clearance_s在次发送请求,就能拿到结果了。
3、最后,我将我自己的代码贴上来,供大家参考;提醒:在第二次请求解析__jsl_clearance_s参数的方式不一定能使用到什么时候,如果过段时间他们更新了其他加密方法,就需要各位自行处理了。
============================
/**
* cookie参数
*/
private static String JSL_UID = "";
/**
* cookie参数
*/
private static String JSL_CLEARANCE = "";
/**
* @Description 抓取网上的图片
* @Author lqt
* @Date 2021/8/4
* @Param imgSrc 图片路径
* @Param www 域名地址
* @Return
* @Exception
*/
public static void downloadImgByNet(String imgSrc,String www) {
CloseableHttpClient client = null;
CloseableHttpResponse response = null;
try {
//设置https协议访问
System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2,SSLv3");
// 发送请求
client = HttpClients.createDefault();
RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(TIME_OUT)
.setSocketTimeout(TIME_OUT).setConnectTimeout(TIME_OUT).build();
HttpGet get = new HttpGet(imgSrc);
get.setConfig(requestConfig);
//模拟浏览器
get.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9");
get.setHeader("Accept-Encoding", "gzip, deflate, br");
get.setHeader("Accept-Language", "zh-CN,zh;q=0.9");
get.setHeader("Cache-Control", "no-cache");
get.setHeader("Connection", "keep-alive");
get.setHeader("Host", www);
get.setHeader("Cookie", JSL_UID + ";" + JSL_CLEARANCE);
get.setHeader("Pragma", "no-cache");
get.setHeader("sec-ch-ua", "Not;A Brand\";v=\"99\", \"Google Chrome\";v=\"91\", \"Chromium\";v=\"91");
get.setHeader("sec-ch-ua-mobile", "?0");
get.setHeader("Sec-Fetch-Dest", "document");
get.setHeader("Sec-Fetch-Mode", "navigate");
get.setHeader("Sec-Fetch-Site", "none");
get.setHeader("Sec-Fetch-User", "?1");
get.setHeader("Upgrade-Insecure-Requests", "1");
get.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36");
response = client.execute(get);
// 第一次请求响应头中的,在第二次第三次请求需要带此参数
String jsluid_s = "";
// 第二次请求 带入 __jsluid_s 与 __jsl_clearance_s
if (response.getStatusLine().getStatusCode() == HTTP_521) {
System.out.println("错误-521");
Header[] hs = response.getHeaders("Set-Cookie");
if(Objects.isNull(hs) || hs.length < 1){
System.out.println("获取Set-Cookie错误");
downloadImgByNet(imgSrc,filePath,fileName,www,"");
}
jsluid_s = "__jsluid_s=" + hs[0].toString().split(";")[0].split("=")[1];
HttpEntity entity = response.getEntity();
String resHtml = EntityUtils.toString(entity);
// 对返回js处理 拿到jsl_clearance
String jsl_clearance_s = getJslClearance(resHtml);
get.setHeader("Cookie", jsluid_s + ";" + jsl_clearance_s);
response = client.execute(get);
}
// 第三次请求 带入 __jsluid_s 与 __jsl_clearance_s(重新计算得到的与第二次不同)
if (response.getStatusLine().getStatusCode() == HTTP_521) {
HttpEntity entity = response.getEntity();
String resHtml = EntityUtils.toString(entity);
// 对返回js处理 拿到jsl_clearance
resHtml = resHtml.substring(resHtml.lastIndexOf("go(") + 3, resHtml.lastIndexOf(")"));
JSONObject data = JSON.parseObject(resHtml);
String jsl_clearance_s = go(data);
JSL_UID = jsluid_s;
JSL_CLEARANCE = jsl_clearance_s;
get.setHeader("Cookie", jsluid_s + ";" + jsl_clearance_s);
response = client.execute(get);
}
// 输出文件
if (response.getStatusLine().getStatusCode() == HttpStatus.HTTP_OK) {
//拿到最终想要的页面
HttpEntity entity = response.getEntity();
if(!Objects.isNull(entity)){
String s = IOUtils.toString(entity.getContent(), StandardCharsets.UTF_8);
}
}
}catch (Exception e){
e.printStackTrace();
}finally {
try {if(response != null) {response.close();}} catch (IOException e) {e.printStackTrace();}
}
}
/**
* @Description 将第一次返回js数据转换
* @Author lqt
* @Date 2021/8/3
* @Param body js文件
* @Return
* @Exception
*/
private static String getJslClearance(String body){
String jsl_clearance = "";
ScriptEngineManager manager = new ScriptEngineManager();
//得到脚本引擎
ScriptEngine engine = manager.getEngineByName("JavaScript");
//处理加密js
String js = body.trim().replace("<script>", "")
.replace("</script>", "")
.replace("document.cookie=", "")
.replace("location.href=location.pathname+location.search", "");
try {
//得到解密后的js
String result = (String) engine.eval(js);
jsl_clearance = result.split(";")[0];
} catch (ScriptException e) {
e.printStackTrace();
}
return jsl_clearance;
}
/**
* @Description 处理第二次返回js数据
* @Author lqt
* @Date 2021/8/3
* @Param data go对象
* @Return
* @Exception
*/
private static String go(JSONObject data) {
String[] bts = data.getObject("bts",String[].class);
String ct = data.getString("ct");
String[] chars = data.getString("chars").split("");
String ha = data.getString("ha");
for (int i = 0; i < chars.length; i ++){
for (int j = 0; j < chars.length; j ++){
String i1 = chars[i];
String j1 = chars[j];
String cookie = bts[0] + i1 + j1 + bts[1];
if (Objects.equals(hash(cookie,ha),ct)) {
return "__jsl_clearance_s=" + cookie;
}
}
}
return "";
}
/**
* @Description 匹配加密 sha1,sha256,md5
* @Author lqt
* @Date 2021/8/3
* @Param cookie需要加密的字符串
* @Param ha 加密类型
* @Return
* @Exception
*/
private static String hash(String cookie, String ha){
String str = "";
try {
switch (ha){
case "sha1":str = HeaUtil.sha1(cookie);break;
case "sha256":str = HeaUtil.sha256(cookie);break;
case "md5":str = HeaUtil.md5(cookie);break;
default:break;
}
return str;
}catch (Exception e){
e.printStackTrace();
}
return str;
}
============== 下面代码为工具类
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* @Description HeaUtil
* Hash encryption algorithm
* 哈希加密算法:MD5、SHA-1、SHA-256、HMAC-SHA-1、HMAC-SHA-256
* 需要导入 org.apache.commons.codec 包
* @Author lqt
* @Date 2021/8/3
* @Param
* @Return
* @Exception
*/
@SuppressWarnings("WeakerAccess")
public class HeaUtil {
/**
* md5加密
*
* @param text 内容
* @return digest 摘要
* @throws NoSuchAlgorithmException e
*/
public static String md5(String text) throws NoSuchAlgorithmException {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
byte[] bytes = messageDigest.digest(text.getBytes());
return Hex.encodeHexString(bytes);
}
/**
* sha1加密
*
* @param text 内容
* @return digest 摘要
* @throws NoSuchAlgorithmException e
*/
public static String sha1(String text) throws NoSuchAlgorithmException {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
byte[] bytes = messageDigest.digest(text.getBytes());
return Hex.encodeHexString(bytes);
}
/**
* sha256加密
*
* @param text 内容
* @return digest 摘要
* @throws NoSuchAlgorithmException e
*/
public static String sha256(String text) throws NoSuchAlgorithmException {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
byte[] bytes = messageDigest.digest(text.getBytes());
return Hex.encodeHexString(bytes);
}
/**
* hmac-sha1加密
*
* @param text 内容
* @param key 密钥
*
* @return 密文
* @throws Exception e
*/
public static String hmacSha1(String text, String key) throws Exception {
SecretKeySpec sk = new SecretKeySpec(key.getBytes(), "HmacSHA1");
return hmacSha1(text, sk);
}
/**
* hmac-sha1加密
*
* @param text 内容
* @param sk 密钥
*
* @return 密文
* @throws Exception e
*/
public static String hmacSha1(String text, SecretKeySpec sk) throws Exception {
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(sk);
byte[] rawHmac = mac.doFinal(text.getBytes());
return new String(Base64.encodeBase64(rawHmac));
}
/**
* 生成 HmacSha1 密钥
*
* @param key 密钥字符串
* @return SecretKeySpec
*/
public static SecretKeySpec createHmacSha1Key(String key) {
return new SecretKeySpec(key.getBytes(), "HmacSHA1");
}
/**
* hmac-sha256加密
*
* @param text 内容
* @param key 密钥
*
* @return 密文
* @throws Exception e
*/
public static String hmacSha256(String text, String key) throws Exception {
SecretKeySpec sk = new SecretKeySpec(key.getBytes(), "HmacSHA256");
return hmacSha1(text, sk);
}
/**
* hmac-sha256加密
*
* @param text 内容
* @param sk 密钥
*
* @return 密文
* @throws Exception e
*/
public static String hmacSha256(String text, SecretKeySpec sk) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(sk);
byte[] rawHmac = mac.doFinal(text.getBytes());
return new String(Base64.encodeBase64(rawHmac));
}
/**
* 生成 HmacSha256 密钥
*
* @param key 密钥字符串
* @return SecretKeySpec
*/
public static SecretKeySpec createHmacSha256Key(String key) {
return new SecretKeySpec(key.getBytes(), "HmacSHA256");
}
/**
* 测试
*
* @param args args
*/
public static void main(String[] args) throws Exception {
String s = "123456789了踩踩踩";
System.out.println(md5(s));
System.out.println(sha1(s));
System.out.println(sha256(s));
String k = "ada232@12";
System.out.println(hmacSha1(s, k));
s = "aeqnfoavneornqoenr1啊可是到了南方情况无法弄清了我呢010jownfasdfqijqor";
System.out.println(hmacSha1(s, k));
SecretKeySpec sk1 = createHmacSha1Key(k);
System.out.println(hmacSha1(s, sk1));
SecretKeySpec sk256 = createHmacSha256Key(k);
System.out.println(hmacSha256(s, sk256));
}
}