登录的站点是3g.renren.com 因为是手机人人, 页面比较简单
首先用HttpGet取出"http://3g.renren.com"的html代码, 是用Jsoup解析出登录表单, 包括验证码的图片的url
因为没法做到绕过验证码,所以用验证码的url构建一个image, 显示出来让用户自己填写
构建image时一定要用httpget, 开始使用了ImageIO.read(new URL(url)); 这样, HttpClient实例中没有管理session
不写了, 全放到注释里去了, 直接上代码
因为程序很依赖html源码, 哪天人人的前台改动了html代码说不定就用不了了
package com.renren.main; import java.awt.Graphics; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.imageio.ImageIO; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JTextArea; import javax.swing.JTextField; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.ResponseHandler; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.BasicResponseHandler; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.protocol.HTTP; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; public class Login extends JFrame implements ActionListener{ private JTextField email; private JPasswordField password; private JTextField verifycode; private JButton login; private ImageBoxPanel imageBox; private Image image; private MsgBox box; private final HttpClient client; private HttpPost post; private HttpGet get; private HttpResponse response; private ResponseHandler<String> responseHandler; private Map<String, String> form_map; private boolean flag;//有没有验证码 private String html; public Login() { super("人人登录"); client = new DefaultHttpClient(); responseHandler = new BasicResponseHandler(); form_map = new HashMap<String, String>(); Object obj; setLayout(null); setDefaultCloseOperation(EXIT_ON_CLOSE); setResizable(false); email = new JTextField("<email>"); password = new JPasswordField("<password>"); verifycode = new JTextField(); login = new JButton("登录"); login.addActionListener(this); html = view("http://3g.renren.com"); init(); try { imageBox = new ImageBoxPanel(createBufferedImage()); } catch (Exception e) { e.printStackTrace(); imageBox = new ImageBoxPanel(); } //layout this.setBounds(500, 300, 280, 220); email.setBounds(10, 10, 250, 30); password.setBounds(10, 50, 250, 30); verifycode.setBounds(10, 90, 150, 30); imageBox.setBounds(205, 80, 54, 46); login.setBounds(10, 130, 250, 30); add(email); add(password); add(verifycode); add(imageBox); add(login); setVisible(true); } private BufferedImage createBufferedImage() { InputStream inputstream=null; try { String source = view("http://3g.renren.com"); String url = getVerifycodeUrl(source); if("error".equals(url)) { return null; } ///rndimg?post=_REQUESTFRIEND_de6073b242a6fc34b67d228abf982916&rnd=1335096399071 //分析登录表单可以发现如果某次登录需要验证码, 则表单中会有verifykey和verifycode //而verifykey 的值正好是验证码地址中的一部分(中间的32位字符), 所以把verifykey取出来 String key = url; key = key.replaceAll("http[\\d\\D]*ND_", ""); key = key.replaceAll("&[\\d\\D]*", ""); form_map.put("verifykey", key);//更新下verifykey,和当前验证码对应 System.out.println(key); get = new HttpGet(url); response = client.execute(get); if(HttpStatus.SC_OK == response.getStatusLine().getStatusCode()) {//以下两个if是网上摘来得 HttpEntity entity = response.getEntity(); if (entity != null) { inputstream = entity.getContent(); //本来返回的是一个InputStream, 但是在finally中调用get.abort()后好像会变成null, 没办法, 所以直接构造出BufferedImage返回 return ImageIO.read(inputstream); } } } catch (Exception e) { e.printStackTrace(); } finally { get.abort(); } return null; } /** * 获取某个url的html代码 * @param url * @return */ private String view(String url) { String html; try { get = new HttpGet("http://3g.renren.com/"); html = client.execute(get, responseHandler); } catch (Exception e) { e.printStackTrace(); html = "error"; } finally { get.abort(); } return html; } /** * 获取验证码图片的地址 * @param source 某个页面的页面源代码 * @return */ private String getVerifycodeUrl(String source) { String url; flag = true; try { Document doc = Jsoup.parse(source); //分析表单可知此句可用, 不过用这种方法来做比较不好的一点就是一旦人人页面稍微改动下, 这个程序就可能用不了了 Element e = doc.getElementsByAttributeValueContaining("alt", "此处为验证码").get(0); url = e.attr("src"); url = "http://3g.renren.com" + url; } catch (Exception e) { //本来会打印异常信息, 不过看着不舒服, 就删了 //大致就是有时没有验证码, 那么上面的get(0)肯定就行不通了 System.out.println("没有验证码~"); url = "error"; flag = false;//标记有没有验证码, 可以让verifykey 和 verifycode 两个属性是否通过表单传过去 } return url; } private void init() { String html = view("http://3g.renren.com"); Document doc = Jsoup.parse(html);//取出3g.renren.com代码 Element form = doc.getElementsByTag("form").get(0); String action = form.attr("action"); form_map.put("action", action); Elements es = form.getElementsByTag("input"); for(Element e: es) { form_map.put(e.attr("name"), e.attr("value")); } } @Override public void actionPerformed(ActionEvent e) { login(); System.out.println(view("http://3g.renren.com")); //登录成功后显示发心情的界面, 主要实现了个功能, 没有考虑到其他的方面 //包括是否登录成功啊, 是否发心情成功失败啊 //不过这些还是挺简单的, 无非就是解析post后的html代码, 看会不会出现哪些错误信息 box = new MsgBox(); } private boolean login() { post = new HttpPost(form_map.get("action")); //据说有些网站如果不设置头会被过滤掉, 毕竟人人还是蛮大的一个网站, 就加上吧 post.setHeader("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3"); List<NameValuePair> nvp = new ArrayList<NameValuePair>(); nvp.add(new BasicNameValuePair("origURL", form_map.get("origURL"))); nvp.add(new BasicNameValuePair("lbskey", form_map.get("lbskey"))); nvp.add(new BasicNameValuePair("c", form_map.get("c"))); nvp.add(new BasicNameValuePair("ref", form_map.get("ref"))); nvp.add(new BasicNameValuePair("email", email.getText())); nvp.add(new BasicNameValuePair("password", new String(password.getPassword()))); nvp.add(new BasicNameValuePair("pq", form_map.get("pq"))); if(flag) { nvp.add(new BasicNameValuePair("verifycode", verifycode.getText())); nvp.add(new BasicNameValuePair("verifykey", form_map.get("verifykey"))); } // System.out.println(form_map.get("verifykey")); try { post.setEntity(new UrlEncodedFormEntity(nvp, HTTP.UTF_8)); //response = client.execute(post); client.execute(post); } catch (Exception e) { e.printStackTrace(); return false; } finally { post.abort(); } return true; } public static void main(String[] args) { Login renren = new Login(); // System.out.println(renren.view("http://3g.renren.com")); } class MsgBox extends JFrame implements ActionListener { JTextArea msg; JButton submit; //某条心情发送次数, 本来想搞个刷屏的, 不过不太给力, 设置了休眠一段时间还是可以成功的 JTextField time; public MsgBox() { setLayout(null); setResizable(false); setBounds(500, 500, 365, 125); msg = new JTextArea(); submit = new JButton("发送~"); time = new JTextField("1"); msg.setBounds(10, 10, 250, 80); time.setBounds(270, 10, 81, 40); submit.setBounds(270, 50, 80, 40); submit.addActionListener(this); add(msg); add(submit); add(time); setVisible(true); } @Override public void actionPerformed(ActionEvent e) { for(int i = 0; i < Integer.parseInt(time.getText()); i++) { post = new HttpPost("http://3g.renren.com/status/wUpdateStatus.do");//反正都是从先认为的登录, 再把一些信息抓下来 post.setHeader("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3"); List<NameValuePair> nvp = new ArrayList<NameValuePair>(); nvp.add(new BasicNameValuePair("_rtk", "xxxxxxxx"));//这个不知道是怎么产生的, 可能每一个id都有对应一个把 nvp.add(new BasicNameValuePair("sour", "")); nvp.add(new BasicNameValuePair("loginbybm", "")); nvp.add(new BasicNameValuePair("status", msg.getText() + i));//其他几个都无关紧要, 不过还是留着 nvp.add(new BasicNameValuePair("pid", "")); nvp.add(new BasicNameValuePair("empty", "1")); try { post.setEntity(new UrlEncodedFormEntity(nvp, HTTP.UTF_8)); response = client.execute(post); System.out.println(response); Thread.sleep(1000L);//没有设置休眠应该是被人人过滤掉了,具体多少时间间隔可以发一次没有测试过, 不过估计500毫秒应该是没有问题的吧 } catch (Exception e1) { e1.printStackTrace(); } finally { post.abort(); } } } } } class ImageBoxPanel extends JPanel { private Image image; public ImageBoxPanel(Image image) { this.image = image; } public ImageBoxPanel() { } @Override protected void paintComponent(Graphics g) { g.drawImage(image, 0, 0, 54, 46, null); } }