在设计一个爬虫的时候,在第一步对网站的大概浏览了解情况是会发现有些网站在访问之前是需要登录的,否则是无法访问到有我们需要的数据的子页面的,这个时候就要在之前的基础上增加一个模拟登录的步骤。
其实模拟登录的步骤跟之前所说的httpclient基本是一样的,只不过现在网站登录基本用的是post方法,同时在里面携带登录所需要的参数如账号密码,所以我们只需要模拟实际操作,将待爬取网站所需要的参数对应的设置到httppost中,下面以模拟知乎登录为例:
1. 确定登录所需要携带的参数:
首先确定登录所需要携带的参数,这里仍用到之前提过的抓包工具fiddler,通过对登录数据的抓取,最后发现需要携带以下参数:
之后分析一下这些参数:
- _xsrf:起初我也对这个参数很疑惑具体是哪来的,但经过对登录页面网页源代码的查找发现这是在每次发起www.zhihu.com请求时在网页源代码中携带返回的一个参数且每次都是不一样的如图
- captcha:显而易见,验证码。
- captcha_type:验证码的类型。
- email:账号。
- password:密码。
2.获取参数:
/**
* 获取_xsrf
* getPageHtml()是下载页面方法
*/
public String get_xsrf("https://www.zhihu.com") {
String page = getPageHtml("https://www.zhihu.com");
Document doc = Jsoup.parse(page);
Elements srfs = doc.getElementsByAttributeValue("name", "_xsrf");
String xsrf = srfs.first().attr("value");
return xsrf;
}
- captcha:验证码。起初登录时发现知乎登录用的是中文验证码,需要找出倒写的汉字,发送到服务器的参数是鼠标点击的位置,服务器会根据位置是否与图片倒写汉字位置匹配来判断正确与否。嫌参数分析麻烦(其实是太菜。。)多次实验发现知乎还有输入数字字母的验证码,这个就简单多了而且这种验证码少携带一个captcha_type参数:
所以只需要将验证码图片下载到本地在对应的设置参数就行了。首先是下载验证码图片,通过对页面源代码的分析并没有找到图片,那就应该是通过js传过来的,抓包分析果然找到了加载验证码的步骤:
知道了来源,那就简单了只需要像_xsrf一样请求相应的地址就行了,然而观察地址又难住了,这个r参数是哪儿来的。。经过多次抓包发这个r每次都不一样而且毫无规律可循,于是猜测这可能是个随机数,通过浏浏览器将这个r改成随机数请求果然得到了一张验证码图片:
接下来就是按部就班的下载了;得到了图片第二步就是设置验证码参数。由于技术有限无法做到智能识别输入,所以选择从控制台输入的方式设置验证码参数。。即在首次登录是下载验证码到本地之后人工查看验证码之后控制台输入验证码设置到请求参数中。下载输入代码如下:
/**
* 下载验证码到本地
* @param url
* @param desFileName
* @return
* @throws MalformedURLException
*/
public boolean downloaderCapter(String url,String desFileName) throws MalformedURLException {
boolean flag = false;
String page = getPageHtml(url);
Document doc = Jsoup.parse(page);
Elements capchas = doc.select("img.Captcha-image");
System.out.println(capchas.size());
if (capchas.size()==0) {
System.out.println("不需要验证码");
}else {
String caurl = "";
Random rnd = new Random();
StringBuilder sb = new StringBuilder();
for(int i=0; i < 13; i++) {
sb.append((char)('0' + rnd.nextInt(10)));
}
String id = sb.toString();
System.out.println(id);
caurl = "https://www.zhihu.com/captcha.gif?r="+id+"&type=login";
URL captcha_url = new URL(caurl);
System.out.println(captcha_url);
File file = new File(desFileName);
if (file.exists()) {
file.delete();
}
try {
URLConnection con = captcha_url.openConnection();
InputStream is = con.getInputStream();
byte[] bs = new byte[1024];
int len;
OutputStream os = new FileOutputStream(file);
while ((len = is.read(bs)) != -1) {
os.write(bs, 0, len);
}
is.close();
os.close();
flag = true;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return flag;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
3.登录实现:
在获取所有参数之后就简单了只要按照httpclient模拟请求就可以了(原以为),代码如下:
import java.io.BufferedReader
import java.io.File
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
import java.io.OutputStream
import java.io.UnsupportedEncodingException
import java.net.MalformedURLException
import java.net.URL
import java.net.URLConnection
import java.security.cert.CertificateException
import java.security.cert.X509Certificate
import java.util.ArrayList
import java.util.HashMap
import java.util.List
import java.util.Map
import java.util.Random
import javax.net.ssl.SSLContext
import org.apache.http.Header
import org.apache.http.HttpEntity
import org.apache.http.HttpHost
import org.apache.http.NameValuePair
import org.apache.http.client.ClientProtocolException
import org.apache.http.client.config.CookieSpecs
import org.apache.http.client.config.RequestConfig
import org.apache.http.client.entity.UrlEncodedFormEntity
import org.apache.http.client.methods.CloseableHttpResponse
import org.apache.http.client.methods.HttpGet
import org.apache.http.client.methods.HttpPost
import org.apache.http.conn.ssl.SSLConnectionSocketFactory
import org.apache.http.conn.ssl.SSLContextBuilder
import org.apache.http.conn.ssl.TrustStrategy
import org.apache.http.cookie.Cookie
import org.apache.http.impl.client.CloseableHttpClient
import org.apache.http.impl.client.HttpClientBuilder
import org.apache.http.impl.client.HttpClients
import org.apache.http.message.BasicNameValuePair
import org.apache.http.util.EntityUtils
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.select.Elements
public class ZhiHu3 {
private String mainurl = "https://www.zhihu.com"
private String email = ""
private String password = ""
private String _xsrf = ""
boolean daili = false
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create()
//CloseableHttpClient httpClient = httpClientBuilder.build()
CloseableHttpClient httpClient = createSSLClientDefault()
private HttpHost proxy = new HttpHost("127.0.0.1",8888,"http")
private RequestConfig config = RequestConfig.custom().setProxy(proxy).build()
private String useage = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36"
private RequestConfig configtime=RequestConfig.custom().setCircularRedirectsAllowed(true).setSocketTimeout(10000).setConnectTimeout(10000).build()
public ZhiHu3() {
}
public ZhiHu3(String email, String password) {
this.email = email
this.password = password
}
// client工具函数,信任对方(https)所有证书
private CloseableHttpClient createSSLClientDefault(){
try {
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
//信任所有证书
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true
}
}).build()
SSLConnectionSocketFactory sslFactory = new SSLConnectionSocketFactory(sslContext)
return HttpClients.custom().setSSLSocketFactory(sslFactory).build()
} catch (Exception e) {
}
return HttpClients.createDefault()
}
public String getPageHtml(String url) {
String html=""
HttpGet httpget = new HttpGet(url)
httpget.addHeader("User-Agent", useage)
httpget.setConfig(configtime)
try {
CloseableHttpResponse response = httpClient.execute(httpget)
HttpEntity entity = response.getEntity()
html = EntityUtils.toString(entity, "utf-8")
httpget.releaseConnection()
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace()
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace()
}
return html
}
public boolean downloaderCapter(String url,String desFileName) throws MalformedURLException {
boolean flag = false
String page = getPageHtml(url)
Document doc = Jsoup.parse(page)
Elements capchas = doc.select("img.Captcha-image")
System.out.println(capchas.size())
if (capchas.size()==0) {
System.out.println("不需要验证码")
}else {
String caurl = ""
//生成随机数
Random rnd = new Random()
StringBuilder sb = new StringBuilder()
for(int i=0
sb.append((char)('0' + rnd.nextInt(10)))
}
String id = sb.toString()
caurl = "https://www.zhihu.com/captcha.gif?r="+id+"&type=login"
//下载验证码图片
File file = new File(desFileName)
if (file.exists()) {
file.delete()
}
try {
HttpGet getCaptcha = new HttpGet(caurl)
CloseableHttpResponse imageResponse = httpClient.execute(getCaptcha)
byte[] bs = new byte[1024]
int len
OutputStream os = new FileOutputStream(file)
while ((len = imageResponse.getEntity().getContent().read(bs)) != -1) {
os.write(bs,0,len)
flag = true
}
os.close()
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace()
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace()
}
}
return flag
}
public String get_xsrf(String url) {
String page = getPageHtml(url)
Document doc = Jsoup.parse(page)
Elements srfs = doc.getElementsByAttributeValue("name", "_xsrf")
String xsrf = srfs.first().attr("value")
return xsrf
}
public void login() throws IOException {
List<NameValuePair> para = new ArrayList<NameValuePair>()
_xsrf=get_xsrf(mainurl)
System.out.println(_xsrf)
para.add(new BasicNameValuePair("_xsrf", _xsrf))
Map<String, String> header = new HashMap<String, String>()
header.put("Content-Type", "application/x-www-form-urlencoded")
header.put("Referer","https://www.zhihu.com/")
header.put("User-Agent", useage)
header.put("X-Requested-With", "XMLHttpRequest")
header.put("Host", "www.zhihu.com")
header.put("Origin", "https://www.zhihu.com")
boolean flag = downloaderCapter(mainurl, "D:\\image.png")
if (flag) {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in))
System.out.println("请输入验证码:")
String captcha = br.readLine()
para.add(new BasicNameValuePair("captcha",captcha))
}
para.add(new BasicNameValuePair("email", email))
para.add(new BasicNameValuePair("password", password))
para.add(new BasicNameValuePair("rememberme", "true"))
HttpPost httppost = new HttpPost("https://www.zhihu.com/login/email")
for (String string : header.keySet()) {
httppost.addHeader(string, header.get(string))
}
httppost.addHeader("X-Xsrftoken", _xsrf)
if (daili) {
httppost.setConfig(config)
}
httppost.setEntity(new UrlEncodedFormEntity(para,"utf-8"))
CloseableHttpResponse res = httpClient.execute(httppost)
int statuts_code = res.getStatusLine().getStatusCode()
System.out.println(statuts_code)
System.out.println(EntityUtils.toString(res.getEntity(),"utf-8"))
httppost.releaseConnection()
}
public static void main(String[] args) {
ZhiHu3 zhihu = new ZhiHu3("xxxxxxxxx@qq.com","xxxxxxxx")
try {
zhihu.login()
String html = zhihu.getPageHtml("https://www.zhihu.com/question/following")
//System.out.println(html)
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace()
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
原来创建httpclient用的是httpClientBuilder.build()方法,但是在实践中发现偶尔会报关于SSL证书问题的错误,所以加了一个工具函数。登录成功后就可抓取自己想要的数据了,我这里是在登录成功后访问我的关注页面。输出返回码为200,表示登录成功。