并不是所有网站信息都可直接访问,有相当一部分的数据是需要用户授权登陆后才可以拿到的,比如某某网站开通vip会员才能进行下载操作
背景
获取尚德个人主页的课程列表
分析
- token
登陆验证一般有两种形式,一种是对方授权你一个具有时效性的token字符串,每次请求时需要带上这个字符串,格式如下
http://aaaa/list.do?token=bbb
每次只需要在对应的url后面拼接对应的token就可以了,当token失效时根据认证接口再次获取就可以了,这种数据API因为走的是http协议,所以具备很强的跨平台优势,一般多用于无页面的数据服务之间的互相请求
- 认证登陆
认证登陆一般也分两种,一种是需要验证码认证的(之后讲),一种不需要验证码,后者相对比较简单,做法和上面比较类似,不同的是这种是把认证存储到cookie信息里而且需要通过selenium模拟登陆方式获取授权后的cookie信息
代码演示
public class RobotLogin {
public static void main(String[] args) throws IOException, InterruptedException {
//geckodriver配置
FirefoxBinary firefoxBinary = new FirefoxBinary();
firefoxBinary.addCommandLineOptions("--headless");
System.setProperty("webdriver.gecko.driver", "target/classes/drivers/geckodriver.exe");
//声明使用的是火狐浏览器
FirefoxOptions firefoxOptions = new FirefoxOptions();
firefoxOptions.setBinary(firefoxBinary);
FirefoxDriver driver = new FirefoxDriver(firefoxOptions);
//使用火狐浏览器打开尚德个人主页
String url = "http://yi.sunlands.com/ent-portal-war/new_pt_uc/my_lesson/lessonHome.action?auth=true&ticket=ST-417335-WD7Vp4cViOIaCiW6uHxd-cas";
driver.get(url);
//元素定位,提交用户名以及密码
driver.findElementByName("username").clear(); //清空后输入
driver.findElementByName("username").sendKeys("bbb");
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
driver.findElementById("password").clear(); //清空后输入
driver.findElementById("password").sendKeys("***");
//元素定位,点击登陆按钮
driver.findElementById("msgBtn").click();
Thread.sleep(5*1000); //休息一段时间,使得网页充分加载。注意这里非常有必要
Set<Cookie> cookies = driver.manage().getCookies();
//获取登陆的cookies
String cookieStr = "";
for (Cookie cookie : cookies) {
cookieStr += cookie.getName() + "=" + cookie.getValue() + "; ";
}
System.out.println(cookieStr);
//基于Jsoup,使用cookies请求个人信息页面
Document doc = Jsoup //添加一些header信息
.connect(url)
.header("Host", "yi.sunlands.com")
.header("Connection", "keep-alive")
.header("Cache-Control", "max-age=0")
.header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3")
// .header("Origin", "http://www.******.com")
.header("Referer", "http://yi.sunlands.com/")
.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0")
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Accept-Encoding", "gzip, deflate, br")
.header("Upgrade-Insecure-Requests", "1")
.cookie("Cookie", cookieStr)
.get();
System.out.println(doc.html());
//解析数据
org.jsoup.select.Elements elements = doc.select("div[class=side-nav]")
.select("ul[class=classify]");
for (Element element : elements) {
System.out.println(element.select("a").text());
}
driver.quit(); // 关闭浏览器
}
}
改进
使用selenium实现的,为了减少依赖和保障后期的爬取效率我们也可以不用
-
首先分析登录页面结构
image.png
打开尚德个人登录页面,通过chrom检查定位,不难发现这是一个表单登录
-
通过抓包分析提交参数
表单提交走的是post请求,当点击完”立即登录”我们通过抓包工具可以看到对应的表单参数
image.png
这里需要主要的是尚德为了防止别人刷登录页面在这里加了一个登录验证,lt是虽登录页面固定返回的一个隐藏字段(在上面截图可以看到),每次刷新这个字段的值都会不一样,所以需要先爬取登录页面获取lt对应的述职字符串
- 伪造表单请求
接下来这一步就是根据组装好的表单参数去请求服务器了,过程就不多说了,直接上一个例子一看便知:
public static void testSDLogin() throws IOException {
Map<String, String> data = new HashMap<>();
data.put("execution","e1s1");
data.put("_eventId","submit");
data.put("username","aaa");
data.put("password","**");
data.put("sign","");
data.put("isgw","");
Map<String,String> headerMap = new HashMap<String, String>();
headerMap.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36");
headerMap.put("Content-Type", "application/json; charset=utf-8");
Document doc = Jsoup.connect("http://passport.sunlands.com/login")
.headers(headerMap).ignoreContentType(true)
.timeout(5000).get() ;
String lt = doc.select("div[id=loginUserDiv]").select("input[name=lt]").attr("value");
System.out.println(lt);
data.put("lt",lt);
Document doc2 = Jsoup.connect("http://yi.sunlands.com/ent-portal-war/new_pt_uc/my_lesson/lessonHome.action?auth=true&ticket=ST-611339-ftbcmMbB5x41xTJnrnDc-cas")
.headers(headerMap).ignoreContentType(true)
.data(data)
.method(Connection.Method.POST)
.timeout(5000).post() ;
System.out.println(doc2.html() );
}
小结
以上例子仅供学习参考,转发请注明出处
网友评论