Android(Java) 模拟登录知乎并抓取用户信息
前不久。看到一篇文章我用爬虫一天时间“偷了”知乎一百万用户。仅仅为证明PHP是世界上最好的语言,该文章中使用的登录方式是直接复制cookie到代码中,这里呢,我不以爬信息为目的。仅仅是简单的介绍使用java来进行模拟登录的基本过程。之前写过的文章android 项目实战——打造超级课程表一键提取课表功能事实上就是模拟登录的范畴。再加上近期在知乎上看到非常多人问关于超级课程表的实现,事实上本质就是模拟登录,掌握了这篇文章的内容,你不再操心抓不到信息了。然后,这篇文章会使用到之前的一篇Cookie保持的文章Android OkHttp的Cookie自己主动化管理,还有Jsoup的使用Jsoup库使用全然解析,为了简单处理,直接使用javaSE来,而不再使用Android进行。假设要移植到Android,唯一的处理可能就是把网络请求工作扔到子线程中去 。
首先使用Chrome打开知乎首页 , 点击登录,你会看到以下这个界面
在Chorme中按F12,调出开发人员工具,切到Network选项卡,勾选Preserve Log。记得一定要勾选,不然你会看不到信息。
一切就绪后,在输入框中输出账号密码点击登录。登录成功后你会看到这么一条记录
点击图中的email,在最下方你会看到本次请求提交了4个參数,以及在上方,你会看到本次请求的地址是http://www.zhihu.com/login/email
你会吃惊的发现知乎的密码是明文传输的,提交的參数的意思也非常easy,email就是账号,password就是密码。remember_me就是是否记住,这里传true就能够了,另一个_xsrf參数,这个毛估估应该是防爬虫的。
因此在提交前我们要从源代码中将这个值抓取下来。该值在表单的隐藏域中
一切准备就绪后,你就兴高採烈的用代码去模拟登录,然后你会发现会返回一个验证码错误的信息。事实上,我们还须要提交一个验证码,其參数名为captcha,验证码的地址为,
http://www.zhihu.com/captcha.gif?r=时间戳
于是我们得出了这种一个数据。
- 请求地址
http://www.zhihu.com/login/email
- 请求參数
_xsrf 表单中提取的隐藏域的值
captcha 验证码
email 邮箱
password 密码
remember_me 记住我
另一个问题。验证码的值怎么得到呢。答案是人工输入。将验证码保存到本地进行觉得识别,输入后进行登陆就可以。
这里的网络请求使用OkHttp。以及解析使用Jsoup,然后我们会使用到Gson,将他们增加maven依赖
com.squareup.okhttp
okhttp
2.4.0
org.jsoup
jsoup
1.8.3
com.google.code.gson
gson
2.3.1
" data-snippet-id="ext.954a1d7d6c4276c4faf06435bd4518df" data-snippet-saved="false" data-csrftoken="20AinXcm-dRsprSXazhrt6nBpMoaeJ3OOLFA" data-codota-status="done"> <dependencies>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>okhttp</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.8.3</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.3.1</version>
</dependency>
</dependencies>
在编码之前。我们得想想怎么维持登陆状态。没错,就是Cookie怎样保持,我们仅仅进行登陆一次,兴许都直接採集数据就能够了,因此须要将cookie持久化。对之前的文章中的一个Android类进行改造。使其变成java平台可用的类。能够看到我们将它从之前保存到SharePrefrences中改成了保存到文件里,并以json形式存储,这就是为什么会用到Gson的原因了
package cn.edu.zafu.zhihu;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import java.io.*;
import java.net.CookieStore;
import java.net.HttpCookie;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* User:lizhangqu(513163535@qq.com)
* Date:2015-07-18
* Time: 16:54
*/
public class PersistentCookieStore implements CookieStore {
private static final Gson gson= new GsonBuilder().setPrettyPrinting().create();
private static final String LOG_TAG = "PersistentCookieStore";
private static final String COOKIE_PREFS = "CookiePrefsFile";
private static final String COOKIE_NAME_PREFIX = "cookie_";
private final HashMap<String, ConcurrentHashMap<String, HttpCookie>> cookies;
private Map<String,String> cookiePrefs=new HashMap<String, String>();
/**
* Construct a persistent cookie store.
*
*/
public PersistentCookieStore() {
String cookieJson = readFile("cookie.json");
Map<String,String> fromJson = gson.fromJson(cookieJson,new TypeToken<Map<String, String>>() {}.getType());
if(fromJson!=null){
System.out.println(fromJson);
cookiePrefs=fromJson;
}
cookies = new HashMap<String, ConcurrentHashMap<String, HttpCookie>>();
// Load any previously stored cookies into the store
for(Map.Entry<String, ?> entry : cookiePrefs.entrySet()) {
if (((String)entry.getValue()) != null && !((String)entry.getValue()).startsWith(COOKIE_NAME_PREFIX)) {
String[] cookieNames = split((String) entry.getValue(), ",");
for (String name : cookieNames) {
String encodedCookie = cookiePrefs.get(COOKIE_NAME_PREFIX + name);
if (encodedCookie != null) {
HttpCookie decodedCookie = decodeCookie(encodedCookie);
if (decodedCookie != null) {
if(!cookies.containsKey(entry.getKey()))
cookies.put(entry.getKey(), new ConcurrentHashMap<String, HttpCookie>());
cookies.get(entry.getKey()).put(name, decodedCookie);
}
}
}
}
}
}
public void add(URI uri, HttpCookie cookie) {
String name = getCookieToken(uri, cookie);
// Save cookie into local store, or remove if expired
if (!cookie.hasExpired()) {
if(!cookies.containsKey(uri.getHost()))
cookies.put(uri.getHost(), new ConcurrentHashMap<String, HttpCookie>());
cookies.get(uri.getHost()).put(name, cookie);
} else {
if(cookies.containsKey(uri.toString()))
cookies.get(uri.getHost()).remove(name);
}
cookiePrefs.put(uri.getHost(), join(",", cookies.get(uri.getHost()).keySet()));
cookiePrefs.put(COOKIE_NAME_PREFIX + name, encodeCookie(new SerializableHttpCookie(cookie)));
String json=gson.toJson(cookiePrefs);
saveFile(json.getBytes(), "cookie.json");
}
protected String getCookieToken(URI uri, HttpCookie cookie) {
return cookie.getName() + cookie.getDomain();
}
public List<HttpCookie> get(URI uri) {
ArrayList<HttpCookie> ret = new ArrayList<HttpCookie>();
if(cookies.containsKey(uri.getHost()))
ret.addAll(cookies.get(uri.getHost()).values());
return ret;
}
public boolean removeAll() {
cookiePrefs.clear();
cookies.clear();
return true;
}
public boolean remove(URI uri, HttpCookie cookie) {
String name = getCookieToken(uri, cookie);
if(cookies.containsKey(uri.getHost()) && cookies.get(uri.getHost()).containsKey(name)) {
cookies.get(uri.getHost()).remove(name);
if(cookiePrefs.containsKey(COOKIE_NAME_PREFIX + name)) {
cookiePrefs.remove(COOKIE_NAME_PREFIX + name);
}
cookiePrefs.put(uri.getHost(), join(",", cookies.get(uri.getHost()).keySet()));
return true;
} else {
return false;
}
}
public List<HttpCookie> getCookies() {
ArrayList<HttpCookie> ret = new ArrayList<HttpCookie>();
for (String key : cookies.keySet())
ret.addAll(cookies.get(key).values());
return ret;
}
public List<URI> getURIs() {
ArrayList<URI> ret = new ArrayList<URI>();
for (String key : cookies.keySet())
try {
ret.add(new URI(key));
} catch (URISyntaxException e) {
e.printStackTrace();
}
return ret;
}
/**
* Serializes Cookie object into String
*
* @param cookie cookie to be encoded, can be null
* @return cookie encoded as String
*/
protected String encodeCookie(SerializableHttpCookie cookie) {
if (cookie == null)
return null;
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
ObjectOutputStream outputStream = new ObjectOutputStream(os);
outputStream.writeObject(cookie);
} catch (IOException e) {
System.out.println("IOException in encodeCookie"+ e);
return null;
}
return byteArrayToHexString(os.toByteArray());
}
/**
* Returns cookie decoded from cookie string
*
* @param cookieString string of cookie as returned from http request
* @return decoded cookie or null if exception occured
*/
protected HttpCookie decodeCookie(String cookieString) {
byte[] bytes = hexStringToByteArray(cookieString);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
HttpCookie cookie = null;
try {
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
cookie = ((SerializableHttpCookie) objectInputStream.readObject()).getCookie();
} catch (IOException e) {
System.out.println("IOException in decodeCookie"+e);
} catch (ClassNotFoundException e) {
System.out.println("ClassNotFoundException in decodeCookie"+e);
}
return cookie;
}
/**
* Using some super basic byte array <-> hex conversions so we don't have to rely on any
* large Base64 libraries. Can be overridden if you like!
*
* @param bytes byte array to be converted
* @return string containing hex values
*/
protected String byteArrayToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 2);
for (byte element : bytes) {
int v = element & 0xff;
if (v < 16) {
sb.append('0');
}
sb.append(Integer.toHexString(v));
}
return sb.toString().toUpperCase(Locale.US);
}
/**
* Converts hex values from strings to byte arra
*
* @param hexString string of hex-encoded values
* @return decoded byte array
*/
protected byte[] hexStringToByteArray(String hexString) {
int len = hexString.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i + 1), 16));
}
return data;
}
public static String join(CharSequence delimiter, Iterable tokens) {
StringBuilder sb = new StringBuilder();
boolean firstTime = true;
for (Object token: tokens) {
if (firstTime) {
firstTime = false;
} else {
sb.append(delimiter);
}
sb.append(token);
}
return sb.toString();
}
public static String[] split(String text, String expression) {
if (text.length() == 0) {
return new String[]{};
} else {
return text.split(expression, -1);
}
}
public static void saveFile(byte[] bfile, String fileName) {
BufferedOutputStream bos = null;
FileOutputStream fos = null;
File file = null;
try {
file = new File(fileName);
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
bos.write(bfile);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (bos != null) {
try {
bos.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
public static String readFile(String fileName) {
BufferedInputStream bis = null;
FileInputStream fis = null;
File file = null;
try {
file = new File(fileName);
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
int available = bis.available();
byte[] bytes=new byte[available];
bis.read(bytes);
String str=new String(bytes);
return str;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
return "";
}
}
然后新建一个OkHttp请求类,并设置其Cookie处理类为我们编写的类。
private static OkHttpClient client = new OkHttpClient();
client.setCookieHandler(new CookieManager(new PersistentCookieStore(), CookiePolicy.ACCEPT_ALL));
好了。能够開始获取_xsrf以及验证码了。验证码保存在项目根文件夹下名为code.png的文件
private static String xsrf;
public static void getCode() throws IOException{
Request request = new Request.Builder()
.url("http://www.zhihu.com/")
.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.134 Safari/537.36")
.build();
Response response = client.newCall(request).execute();
String result = response.body().string();
Document parse = Jsoup.parse(result);
System.out.println(parse + "");
result = parse.select("input[type=hidden]").get(0).attr("value")
.trim();
xsrf=result;
System.out.println("_xsrf:" + result);
String codeUrl = "http://www.zhihu.com/captcha.gif?r=";
codeUrl += System.currentTimeMillis();
System.out.println("codeUrl:" + codeUrl);
Request getcode = new Request.Builder()
.url(codeUrl)
.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.134 Safari/537.36")
.build();
Response code = client.newCall(getcode).execute();
byte[] bytes = code.body().bytes();
saveCode(bytes, "code.png");
}
public static void saveCode(byte[] bfile, String fileName) {
BufferedOutputStream bos = null;
FileOutputStream fos = null;
File file = null;
try {
file = new File(fileName);
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
bos.write(bfile);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (bos != null) {
try {
bos.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
然后将获取来的參数连同账号密码进行提交登录
public static void login(String randCode,String email,String password) throws IOException{
RequestBody formBody = new FormEncodingBuilder()
.add("_xsrf", xsrf)
.add("captcha", randCode)
.add("email", email)
.add("password", password)
.add("remember_me", "true")
.build();
Request login = new Request.Builder()
.url("http://www.zhihu.com/login/email")
.post(formBody)
.addHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.134 Safari/537.36")
.build();
Response execute = client.newCall(login).execute();
System.out.println(decode(execute.body().string()));
}
public static String decode(String unicodeStr) {
if (unicodeStr == null) {
return null;
}
StringBuffer retBuf = new StringBuffer();
int maxLoop = unicodeStr.length();
for (int i = 0; i < maxLoop; i++) {
if (unicodeStr.charAt(i) == '\\') {
if ((i < maxLoop - 5)
&& ((unicodeStr.charAt(i + 1) == 'u') || (unicodeStr
.charAt(i + 1) == 'U')))
try {
retBuf.append((char) Integer.parseInt(
unicodeStr.substring(i + 2, i + 6), 16));
i += 5;
} catch (NumberFormatException localNumberFormatException) {
retBuf.append(unicodeStr.charAt(i));
}
else
retBuf.append(unicodeStr.charAt(i));
} else {
retBuf.append(unicodeStr.charAt(i));
}
}
return retBuf.toString();
}
当看到以下的信息就代码登录成功了
之后你就能够获取你想要的信息了。这里简单获取一些信息,比方我要获取轮子哥的followers的昵称。分页自己处理下就ok了。
public static void getFollowers() throws IOException{
Request request = new Request.Builder()
.url("http://www.zhihu.com/people/zord-vczh/followees")
.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.134 Safari/537.36")
.build();
Response response = client.newCall(request).execute();
String result=response.body().string();
Document parse = Jsoup.parse(result);
Elements select = parse.select("div.zm-profile-card");
StringBuilder builder=new StringBuilder();
for (int i=0;i<select.size();i++){
Element element = select.get(i);
String name=element.select("h2").text();
System.out.println(name+"");
builder.append(name);
builder.append("\n");
}
}
下图就是获取到的信息。当然。仅仅要你登录了。什么信息你都能够获取到。
最后上源代码,Intelij的maven项目
http://download.csdn.net/detail/sbsujjbcy/8984375
Android(Java) 模拟登录知乎并抓取用户信息的更多相关文章
- Java模拟登录系统抓取内容【转载】
没有看考勤的习惯,导致我的一天班白上了,都是钱啊,系统也不发个邮件通知下.... 为了避免以后还有类似状况特别写了个java模拟登录抓取考勤内容的方法(部分代码来自网络),希望有人修改后也可以 ...
- Python爬虫初学(三)—— 模拟登录知乎
模拟登录知乎 这几天在研究模拟登录, 以知乎 - 与世界分享你的知识.经验和见解为例.实现过程遇到不少疑问,借鉴了知乎xchaoinfo的代码,万分感激! 知乎登录分为邮箱登录和手机登录两种方式,通过 ...
- 【爬虫】python requests模拟登录知乎
需求:模拟登录知乎,因为知乎首页需要登录才可以查看,所以想爬知乎上的内容首先需要登录,那么问题来了,怎么用python进行模拟登录以及会遇到哪些问题? 前期准备: 环境:ubuntu,python2. ...
- Python爬虫入门(基础实战)—— 模拟登录知乎
模拟登录知乎 这几天在研究模拟登录, 以知乎 - 与世界分享你的知识.经验和见解为例.实现过程遇到不少疑问,借鉴了知乎xchaoinfo的代码,万分感激! 知乎登录分为邮箱登录和手机登录两种方式,通过 ...
- 2020.10.20 利用POST请求模拟登录知乎
前两天学习了Python的requests模块的相关内容,对于用GET和PSOT请求访问网页以抓取需要的内容有了初步的了解,想要再从一些复杂的网站积累些经验.最开始我采用最简单的get(url)方法想 ...
- requests_模拟登录知乎
如何登录知乎? 首先要分析,进行知乎验证的时候,知乎服务器需要我们提交什么数据,提交的地址.先进行几次登录尝试,通过浏览器中network中查看数据流得知,模拟登录知乎需要提供5个数据,分别是_xsr ...
- 利用scrapy模拟登录知乎
闲来无事,写一个模拟登录知乎的小demo. 分析网页发现:登录需要的手机号,密码,_xsrf参数,验证码 实现思路: 1.获取验证码 2.获取_xsrf 参数 3.携带参数,请求登录 验证码url : ...
- Java爬虫——模拟登录知乎
登录界面,首先随意输入一个账号,登录查看发送表单的请求 可以发现请求是Post : https://www.zhihu.com/login/phone_num 发送的表单是 _xsrf: passwo ...
- 使用selenium模拟登录知乎
网上流传着许多抓取知乎数据的代码,抓取它的数据有一个问题一定绕不过去,那就是模拟登录,今天我们就来聊聊知乎的模拟登录. 获取知乎内容的方法有两种,一种是使用request,想办法携带cookies等必 ...
随机推荐
- LeetCode 136 Single Number(仅仅出现一次的数字)
翻译 给定一个整型数组,除了某个元素外其余元素均出现两次. 找出这个仅仅出现一次的元素. 备注: 你的算法应该是一个线性时间复杂度. 你能够不用额外空间来实现它吗? 原文 Given an array ...
- 购买VPS时机房的选择
购买VPS时机房的选择 对于配置差不多的VPS,距离越近的一般网速越快,所以有必要使用谷歌地图,查看城市的所在位置 比如ChicagoVPS,不时有邮件销售它的VPS,虽然说不上怎么好,但是一般的使用 ...
- Filebeat的下载(图文讲解)
第一步:进入Elasticsearch的官网 https://www.elastic.co/ 第二步:点击downloads https://www.elastic.co/downloads 第三步: ...
- 1.20 Python基础知识 - python常用模块-1
一.time和datetime 1.time模块 1)time.process_time() >>> import time >>> time.process_ti ...
- 【hdu 4333】Revolving Digits
[链接]http://acm.hdu.edu.cn/showproblem.php?pid=4333 [题意] 就是给你一个数字,然后把最后一个数字放到最前面去,经过几次变换后又回到原数字,问在这些数 ...
- Lightoj 1043 - Triangle Partitioning【二分】
题目链接:http://lightoj.com/volume_showproblem.php? problem=1043 题意:一个三角形ABC,DE//BC.已知三角形ADE和四边形BDEC的面积的 ...
- iptables转发安卓手机热点的数据到指定的端口
iptables转发安卓手机热点的数据到指定的端口 手机安装了VPN,可以上GOOGLE的那种.然后我打开手机的热点,连上笔记本,想让本本上个google 没想到被GFW挡住了.看了一下手机的网络工作 ...
- JS学习笔记 - fgm练习 2-12- 全选反选 判断CheckBox是否选中 &&运算符
练习地址:http://www.fgm.cc/learn/lesson2/12.html 总结: 1. && 运算符,从左向右依次执行,如果遇到 false,就不再继续执行后面的语句 ...
- 洛谷—— P1062 数列
https://www.luogu.org/problem/show?pid=1062#sub 题目描述 给定一个正整数k(3≤k≤15),把所有k的方幂及所有有限个互不相等的k的方幂之和构成一个递增 ...
- ArcGIS 10.5 新功能
ArcGIS 10.5正式发布,打造智能的Web GIS平台 2017年新年来临之际,ArcGIS 10.5正式发布. 历经几个版本,ArcGIS10.5已经革新为一个智能的以Web为中心的地理平台, ...