现在的位置: 首页 > 综合 > 正文

httpclient 模拟登录人人网, 发心情(刷屏)

2013年08月31日 ⁄ 综合 ⁄ 共 7881字 ⁄ 字号 评论关闭

登录的站点是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);
	}
}

抱歉!评论已关闭.