最近做爬虫时碰到了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)); } }