java实现百度网盘爬虫

逄俊贤
2023-12-01

         项目镇楼本文的项目都在此处哦

         工作嘛,就是不在需求中爆发,就在需求中灭亡。

         最近接了个奇怪的需求。要用java实现百度网盘(有提取码的)下载。。我估么着就是url和提取码太多他懒得自己一个一个下载emmmmmm反正有需求就得看着折腾。

        最开始寻思这种事情,可能目测得去官网查查SDK有木有。。。百度功能太多。。没看懂到底都是什么玩意。于是就只能自己爬虫了。

        爬虫实现首先还是首选Jsoup,因为别的我也不会emmmm

        思路上,首先你要获取到百度网盘的真实下载路径,然后再正常下载就行。遇事不决先百度。。获得了一个java获得百度网盘真实路径的好方法——不带提取码的这种获取百度网盘下载真实地址

       有了参考,带提取码的就可以扒一扒了

       首先先定义几个全局变量

//下载链接和提取码
private static String url = "https://pan.baidu.com/s/1x6q8VhFE5zzAlA5oH50wLA";
private static String pwd = "i076";
//这几个参数不要动
private static final String baseUrl = "https://pan.baidu.com/share/verify?surl=";
private static String params = "";
//下载参数,文件名及文件大小
private static String server_filename = null;
private static String size = null;
//从cookie中获取的重要参数 核心参数
private static String sekey = "";

       参数说明:url和pwd是网址和提取码,注意这个网址是百度给的那个分享的 不是复制到浏览器以后跳转到的链接,它们是不一样的,会影响获取到的surl参数。剩下的就不用动原样放着就行。baseUrl和param是第一波请求cookies需要的拼接网址,文件名及大小是下载需要的参数,sekey是个很重要的没它不行的参数,找了好几天去对了调试器中的js才发现它在cookie中。

       爬虫第一步,一定是要有cookie。打开那个网址(https://pan.baidu.com/s/1fYFbqAHY_NkxoM0y2pypnQ),会惊奇的发现地址栏url是https://pan.baidu.com/share/init?surl=fYFbqAHY_NkxoM0y2pypnQ

       对,你没有看错地址是不一样的。这就是上面强调的url一定注意获取的是什么。打开调试器,点网络-xhr然后输提取码,最好先输错的,来观察请求。获取到的post请求就是我那个baseUrl+surl+params三部分组成的。仔细观察的话,会发现其实不是,有两个参数我没有,一个logid,一个token。因为没有影响,所以我就没有加。划重点了,header中有个referer,这个如果不带,是提交不了提取码的,会返回errno112,这个经我解析js一行行找说的是(113===t||112===t)&&(e='页面已过期,请<a href="javascript:window.location.reload();">刷新</a>后重试'),解决办法就是把referer加上。看参数验证码是pwd,所以data加上pwd和提取码,这个返回的res进行打印res.body(),发现结果是errno:0,表示提取码已经提取完成了。详细代码如下:

String surl = url.split("/s/1")[1];
params += "&t="+System.currentTimeMillis()+"channel=chunlei&web=1&app_id=230528&clienttype=0";
		
Connection.Response res = Jsoup.connect(baseUrl+surl+params)
                               .header("Referer","https://pan.baidu.com/share/init?surl="+surl)
                               .data("pwd",pwd)
                               .method(Method.POST)
                               .ignoreContentType(true)
			       .execute();
Map<String, String> cookies = res.cookies();

           有了cookie就好办事了。在网页上输入正确的提取码,进入真正的https://pan.baidu.com/s/1x6q8VhFE5zzAlA5oH50wLA

           爬虫嘛,还是得根据实际的请求来的。打开调试器,网络 xhr 清空原来的,然后点下载。。emmm???要登录,登就登,登完了发现了一个post请求。看见sharedownload就是要找的请求。这个时候发现要带的参数好多sign??签名。。。怎么破自己合成么????不要放弃,先发送个请求试试,打印返回值,就会看见新天地。

Connection.Response res2 = Jsoup.connect(url)
				.method(Method.POST)
				.cookies(cookies)
				.ignoreContentType(true)
				.execute();
System.out.println(res2.body());

        没错是这个网页的源码,就是view-source:https://pan.baidu.com/s/1x6q8VhFE5zzAlA5oH50wLA 此时还不用绝望,ctrl+F搜sign,页面有签名,此刻我想起了张辽的那句话,没想到吧

        那就写个方法来获取所有需要的参数

public static Map<String, String> getBodyParams(String body) {
		Map<String, String> map = new HashMap<String, String>();

		String setData = "";
		Pattern pattern_setData = Pattern.compile("setData.*?;");
		Matcher matcher_setData = pattern_setData.matcher(body);
		if (matcher_setData.find()) {
			String tmp = matcher_setData.group(0);
			setData = tmp.substring(8, tmp.length() - 2);
			Gson gson = new Gson();
			SetDataBean bean = gson.fromJson(setData, SetDataBean.class);

			map.put("sign", bean.getSign());
			map.put("timestamp", bean.getTimestamp());
			map.put("bdstoken", bean.getBdstoken());
			map.put("app_id", bean.getFile_list().getList()[0].getApp_id());
			map.put("uk", bean.getUk());
			map.put("shareid", bean.getShareid());
			map.put("primaryid", bean.getShareid());
			map.put("fs_id", bean.getFile_list().getList()[0].getFs_id());
			map.put("fid_list", bean.getFile_list().getList()[0].getFs_id());
			map.put("server_filename", bean.getFile_list().getList()[0].getServer_filename());
			map.put("size", bean.getFile_list().getList()[0].getSize());
//			map.put("logid", logid);
			
		}

		return map;
	}

        坦白来说。。这个方法不是我写的,也是参考的人家的代码。就是最上面不是我项目下载的那个大神的。自己写需要先写一个实体类对应json值。写起来的话是页面ctrl+F找到yunData.setData然后把后面的一整行复制 打开在线json解析,规范格式,就知道都有什么属性了。

        有了全部参数,就可以获取post的url了。

public static String getPostUrl(Map<String, String> params) 
{
	StringBuffer sb1 = new StringBuffer();
	sb1.append("https://pan.baidu.com/api/sharedownload?");
	sb1.append("sign=" + params.get("sign"));
	sb1.append("&timestamp=" + params.get("timestamp"));
	sb1.append("&channel=chunlei");
	sb1.append("&web=1");
	sb1.append("&app_id=" + params.get("app_id"));
	sb1.append("&clienttype=0");
	String post_url = sb1.toString();
	return post_url;
}

        然后接着看调试器,还需要有一些参数加上,encrypt这个直接写死0,1表示打开百度云管家链接,并不会返回真实路径,0会返回真实路径。extra是个很烦的参数,没它不行,经过查调试器js,终于找到了它的来源,在cookie中。(t.extra=a.stringify({sekey:decodeURIComponent(i("BDCLND"))}))这是百度js的源码。i是cookie。。别问我怎么知道的。。这说起来全是眼泪。。那么就根据cookies来获取sekey这个参数。

private static void getSekeyBycookies(Map<String, String> cookies) throws UnsupportedEncodingException 
{
	String bdclnd = cookies.get("BDCLND");
	if(null != bdclnd && !"".equals(bdclnd)) 
	{
		sekey = java.net.URLDecoder.decode(bdclnd,"UTF-8");
	}
}

        其他参数在param里都能找到,就直接写个方法获取data吧。sekey是全局变量申明的。

public static Map<String, String> getPostData(Map<String, String> params) 
{
	// POST携带的参数(抓包可看到)
	Map<String, String> data = new HashMap<String, String>();
	data.put("encrypt", "0");
	data.put("product", "share");
	data.put("uk", params.get("uk"));
	data.put("primaryid", params.get("primaryid"));
	// 添加了[]
	data.put("fid_list", "[" + params.get("fid_list") + "]");
	data.put("path_list", "");// 可以不写
	data.put("extra", "{\"sekey\":\""+sekey+"\"}");
	return data;
}

        有了参数 url cookie 就可以模拟post请求获取真实路径了。

Response res3 = Jsoup.connect(post_url)
		     .method(Method.POST)
		     .header("Referer", url)
		     .cookies(cookies)
		     .data(data)
		     .ignoreContentType(true)
		     .execute();

        注意还是。。要有referer没有这个就112,然后没有sekey参数就118,输入超过3次就会让输验证码。。那部分我就没做了。

        最后就获取到真实路径

public static String parseRealDownloadURL(String responseJson)
 {
	String realURL = "";
	Pattern pattern = Pattern.compile("\"dlink\":.*?,");
	Matcher matcher = pattern.matcher(responseJson);
	if (matcher.find()) 
        {
		String tmp = matcher.group(0);
		String dlink = tmp.substring(9, tmp.length() - 2);
		realURL = dlink.replaceAll("\\\\", "");
	}
	return realURL;
}

      有了真实路径就下载吧。。。断点下载我没有写。。如果文件太大下不过来。。就自己想想办法,没准下周我能研究出来。。。。

       文件下载这个。。jsoup可以实现,但是可能。。下不完整,建议还是写个下载的方法。。我的项目里的那个因为也不是我自己写的,就不提供代码了。。自己下着用吧。。。

 类似资料: