Java使用HttpClient抓取新浪邮箱

/

前一段时间的无聊就简单实现了一个新浪邮箱的邮件爬取功能,主要是处理一些无法通过邮件协议读取邮件的问题,

本文以新浪邮件为例,其他邮箱实现思路大致相同

应用场景

当你有一堆通过某些途径得到了一些邮件的用户名和密码,而这个查看邮件又不需要人工去操作,那么第一想到的程序实现了,当然这就不排除有些情况没有办法通过邮件协议 POP3 读取邮件的。于是就想到了通过爬虫来完成这样的事情

思路

  • 通过使用HttpClient发送Http请求来完成对浏览器请求的模拟
  • 使用Jsoup的来完成网页的解析功能

想到这里于是就开干起来了。这里就完整的呈现当时处理的思路

对请求的分析

这里使用了firefox比较牛B的一个插件firebug

打开新浪邮箱的首页

其他的请求这里被清除了, 我们需要关心的是,当点击登陆按钮的时候浏览器都做了什么,
还是简单的输入用户名和密码就开始登陆了。

呵呵呵,第一眼一看我擦则个没有撒输入的用户和密码呢,这也藏的太深了把。一大堆奇奇乖乖的参数,难道这就行行不通了。

不过再认真一看,也就spsu,nonce,rsakv 这几个参数有一些怪怪的。
似乎使用 HttpClient + Jsoup 这条路就走不通了。

不过想了一下,这些事情都是浏览器完成的,那么按道理是可能通过程序的方式来完成的(无非也就是使用 javascritp 做一些对密码进行加密撒的吧)

  • 如果 JavaScript 的代码不是那么复杂就自己改写为Java代码
  • 过于复杂,就是 Java8 的 nashorn(JavaScript的一个运行环境) 来做吧
  • 查看手机端的请求方式是否会简单一些(实际是一样的登陆流程)

那么就需要早是那段 JavaScript 代码在做这些事情,那么就需要看都发了那些 Js 到浏览器了

看名字就可以知道了 ssilogin.js 和 login.js 嫌疑比较的大,后面通过 javascript 断点调试的方式发现是使用了 ssilogin.js
由于这个文件的内容比较的多于是选择上面的后者(Java8 解释 JavaScript的方式来完成)

中途有想过使用第三方的库 selenium 来做,不过考虑到依赖浏览器(对于运行环境都有一些要求,于是就这样被 Pass掉了)


整理完当时的思路,就来看看代码使怎样实现这个看似有些复杂的事情把。

  1. package com.fzb.fetch.mail;
  2. import com.fzb.common.IOUtil;
  3. import flexjson.JSONDeserializer;
  4. import org.apache.log4j.Logger;
  5. import javax.script.ScriptEngine;
  6. import javax.script.ScriptEngineManager;
  7. import javax.script.ScriptException;
  8. import java.net.URLEncoder;
  9. import java.util.*;
  10. public class SinaEmail {
  11. private static Logger LOGGER = Logger.getLogger(SinaEmail.class);
  12. private static ScriptEngine engine;
  13. static {
  14. ScriptEngineManager engineManager = new ScriptEngineManager();
  15. engine = engineManager.getEngineByName("nashorn");
  16. try {
  17. engine.eval(IOUtil.getStringInputStream(HttpUtil.class.getResourceAsStream("/osslogin.js")));
  18. } catch (ScriptException e) {
  19. LOGGER.error("load sina login.js error", e);
  20. }
  21. }
  22. public static void fetch(String userName, String pwd) throws Exception {
  23. long serverTime = System.currentTimeMillis() / 1000L;
  24. int nonce = new Random(100000L).nextInt() + 100000;
  25. String password = engine.eval("password('" + serverTime + "','" + nonce + "','" + pwd + "')").toString();
  26. String su = URLEncoder.encode(new String(Base64.getEncoder().encode(URLEncoder.encode(userName, "UTF-8").getBytes())), "UTF-8");
  27. String url = "http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.18)&_=" + serverTime;
  28. String str = "entry=cnmail&gateway=1&savestate=30&useticket=0&su=" + su + "&service=sso&servertime=" + serverTime + "&nonce=" + nonce + "&pwencode=rsa2&rsakv=1330428213&sp=" + password + "&encoding=UTF-8&cdult=3&domain=sina.com.cn&prelt=562&returntype=TEXT";
  29. ResponseData<String> t = new ResponseData<String>() {
  30. };
  31. HttpUtil.postContentType(url, str.getBytes(), t, null);
  32. Map json = (Map) new JSONDeserializer().deserialize(t.getT());
  33. List<String> urls = (List) json.get("crossDomainUrlList");
  34. urls.add("http://mail.sina.com.cn/cgi-bin/sla.php?vt=0");
  35. for (String accessUrl : urls) {
  36. accessUrl = accessUrl + "&callback=sinaSSOController.doCrossDomainCallBack&scriptId=ssoscript2&client=ssologin.js(v1.4.18)&_=1444529802162";
  37. if (!accessUrl.contains("passport.weibo.cn")) {
  38. HttpUtil.getContentType(accessUrl, t, null);
  39. }
  40. }
  41. HttpUtil.postContentType("http://m1.mail.sina.com.cn/wa.php?a=list_mail", "fid=new&order=htime&sorttype=desc&type=0&pageno=1&tag=-1&webmail=1".getBytes(), t, null);
  42. Map ssJson = (Map) new JSONDeserializer().deserialize(t.getT());
  43. Long count = (Long) ((HashMap) ((Map) ssJson.get("data")).get("total")).get("count");
  44. LOGGER.info(ssJson);
  45. LOGGER.info("email count " + count);
  46. }
  47. public static void main(String[] args)
  48. throws Exception {
  49. fetch("wozcc94@sina.cn", "hhhhhhhhh");
  50. }
  51. }

nashorn 对 javascript的支持是有限的,比如document,windows.location,判断浏览器timeout 这些都是无法完成,如果出现类似的请求,先的删掉对应的 js 代码

看上去也还是挺精简的,整个代码做了那些事情。

  • 使用 Java8 nashorn 加载新浪的 osslogin.js
  • 调用自己的计算 sp(sinapassword) 代码
  • 组装其他请求参数,发送 post 请求
  • 得到需要访问的几个 URL 列表(这里有一个 cookie 需要请求来生成)
  • 查看获取邮件的请求,模拟发起请求得到对应的 Json 数据

完整代码移步到

http://git.oschina.net/94fzb/fetch-sina-mail

转载请注明作者和出处,并添加本页链接。
原文链接: https://blog.94fzb.com/post/http-fetch-sina-email