前言

并不是所有的网站都能够敞开心扉让你看个透彻,它们总要给你出些难题让你觉得有些东西是来之不易的,往往,这也更加激发你的激情和斗志!

《为了媳妇的一张号,我与百度医生杠上了》里就有网友提出可以通过获取cookie的方式来登录,不需要借助selenium这样的模拟浏览器操作了,到后来在公众号里一号友说豆瓣如何实现登录,以及近期园友都有提到想获取更多的网站数据是需要登录的……登录,一直是爬虫界躲不了也绕不开的话题。

之前已经试过通过启动浏览器,模拟人工操作填写用户名和密码并点击登录来完成登录。 这次准备由台前模式切换到幕后,研究下不用启动浏览器如何实现使用后台代码就模拟登录豆瓣电影君。


我们登录网站时发生了什么

类似这样的登录界面(movie.douban.com),我们只要填写用户名和密码,乐意的话选中“下次自动登录”,然后点击登录按钮,不出意外(如果你总是调戏人家,屡次输入错误,人家不会用小锤锤捶你胸,但是搞个验证码也够你喝一壶的了),你就登录成功了。

看着还是比较简单,但是在浏览器后面,实际上已经做了不少事儿~~~

如果是Chrome浏览器,可以按F12,切到Network选项,在点击页面中的登录后,你可以看到唰唰唰的请求,好比这样

其中最关键的发生在第一条,即通过发送HTTP Post请求与服务器交互,请求登录,在这条请求中你可以看到很熟悉的内容



没错,你在登录网站的时候,实际上是与服务器做了一次通讯,验证成功后,服务器才让你登录网站。


模拟登录

如果网站像上面这样的情况,其实很好登录,比如在postman中填写这些对应的参数就能够成功登录,但是有情操的网站都会有验证码,一般会出现在多次登录失败或者登录网站过于频繁就会出现验证码,好比这样

则对应的post请求如下

这时候我们使用postman实施登录,结果似乎不尽如人意,之所以产生这样的效果时因为这个captcha-id在每次请求的时候都会重新生成,感觉是和验证码绑定的,所以即使你在请求参数中带上了captcha-id也无济于事,因为这次的cookie已经不能使用上次的cookie,直白说就是这次我们又有了一个新的验证码,但是使用的captcha-solution却是上一次的(一张旧船票如何登上进入的新船)。

解决思路

旧船票登不上船是因为日期不对,就是这里的captcha-solution和captcha-id不匹配,所以需要

  • 预先请求获得验证码图片对应的captcha-id
  • 下载这张验证码的图片到本地,识别验证码的内容即captcha-solution
  • 有了今天的船票captcha-solution和今天的船captcha-solution,就可以扬帆起航了

代码

package com.jackie.crawler.doubanmovie.utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map; import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
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.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Created by Jackie on 2017/3/12.
*/ public class LoginTest {
private static HttpClient httpClient=new DefaultHttpClient(); //主登录入口
public static void loginDouban(){
String redir="https://www.douban.com/people/your_home_page/"; // 输入你登录成功后要跳转的网页
String login_src="https://accounts.douban.com/login";
String form_email="your_username"; // 你的用户名
String form_password="your_password"; // 你的密码
String captcha_id=getImgID();
String login="登录";
String captcha_solution="";
System.out.println("请输入验证码:");
BufferedReader buff=new BufferedReader(new InputStreamReader(System.in));
try {
captcha_solution=buff.readLine();
} catch (IOException e) {
e.printStackTrace();
} //构建参数
List<NameValuePair> list=new ArrayList<NameValuePair>();
list.add(new BasicNameValuePair("redir", redir));
list.add(new BasicNameValuePair("form_email", form_email));
list.add(new BasicNameValuePair("form_password", form_password));
list.add(new BasicNameValuePair("captcha-solution", captcha_solution));
list.add(new BasicNameValuePair("captcha-id", captcha_id));
list.add(new BasicNameValuePair("login", login));
HttpPost httpPost = new HttpPost(login_src);
try {
httpPost.setEntity(new UrlEncodedFormEntity(list));
HttpResponse response=httpClient.execute(httpPost);
HttpEntity entity=response.getEntity();
String result=EntityUtils.toString(entity,"utf-8"); HttpGet httpGet=new HttpGet(redir);
HttpResponse response1=httpClient.execute(httpGet);
HttpEntity entity1=response1.getEntity();
String result1=EntityUtils.toString(entity1,"utf-8");
System.out.println(result1); } catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取验证码图片“token”值
* @return token
*/
private static String getImgID(){
String src="https://www.douban.com/j/misc/captcha";
HttpGet httpGet=new HttpGet(src);
String token="";
try {
HttpResponse response=httpClient.execute(httpGet);
HttpEntity entity=response.getEntity();
String content=EntityUtils.toString(entity,"utf-8");
Map<String,String> mapList=getResultList(content);
token=mapList.get("token");
String url="https:"+mapList.get("url");
downImg(url);
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return token;
}
/**
* 用JSON 把数据格式化,并生成迭代器,放入Map中返回
* @param content 请求验证码时服务器返回的数据
* @return Map集合
*/
public static Map getResultList(String content){
Map maplist=new HashMap(); try {
JSONObject jo=new JSONObject(content);
Iterator it = jo.keys();
String key="";
String value="";
while(it.hasNext()){
key=(String) it.next();
if ("r".equals(key)) {
value = jo.getBoolean(key) + "";
}else {
value=jo.getString(key);
}
maplist.put(key, value);
}
} catch (JSONException e) {
e.printStackTrace();
}
return maplist;
}
/**
* 此方法是下载验证码图片到本地
* @param src 给个验证图片完整的地址
*/
private static void downImg(String src){
File fileDir=new File("D://爬虫测试/jackie");
if(!fileDir.exists()){
fileDir.mkdirs();
}
File file=new File("D://爬虫测试/jackie/jackie.png");
if(file.exists()){
file.delete();
}
InputStream input = null;
FileOutputStream out= null;
HttpGet httpGet=new HttpGet(src);
try {
HttpResponse response=httpClient.execute(httpGet);
HttpEntity entity = response.getEntity();
input = entity.getContent();
int i=-1;
byte[] byt=new byte[1024];
out=new FileOutputStream(file);
while((i=input.read(byt))!=-1){
out.write(byt);
}
System.out.println("图片下载成功!");
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
} public static void main(String[] args) {
loginDouban();
}
}

以上代码参考自网友。

执行的结果如下:

由此可见,已经通过模拟登录的方式成功登录网站。

补充

上面的操作还是不够自动化, 主要是验证码这个环节, 需要人工输入下载到本地图片的验证码内容。本来想通过Tesseract-OCR技术来自动识别验证码,但是在分析验证码需要针对性的用到二值化、中值过滤等都遇到了困难,最终还是没能通过这种方式,实现全自动化模拟登录。

但是可以大概介绍下在这方面做的尝试:

  • 下载Tesseract-OCR
  • 安装完成后,可以到安装目录下执行类似tesseract 1.jpg result这样的命令将1.jpg输入通过tesseract处理将识别的讲过存入名为result的txt文件中。
  • 有关验证码的相关代码已经放在项目的ocr目录下,又需要的可以前往GitHub

代码已更新到GitHub

如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。(有些闲言碎语我可能会写在公众号)

Java豆瓣电影爬虫——模拟登录的前世今生与验证码的爱恨情仇的更多相关文章

  1. Java豆瓣电影爬虫——抓取电影详情和电影短评数据

    一直想做个这样的爬虫:定制自己的种子,爬取想要的数据,做点力所能及的小分析.正好,这段时间宝宝出生,一边陪宝宝和宝妈,一边把自己做的这个豆瓣电影爬虫的数据采集部分跑起来.现在做一个概要的介绍和演示. ...

  2. Java豆瓣电影爬虫——使用Word2Vec分析电影短评数据

    在上篇实现了电影详情和短评数据的抓取.到目前为止,已经抓了2000多部电影电视以及20000多的短评数据. 数据本身没有规律和价值,需要通过分析提炼成知识才有意义.抱着试试玩的想法,准备做一个有关情感 ...

  3. Java豆瓣电影爬虫——小爬虫成长记(附源码)

    以前也用过爬虫,比如使用nutch爬取指定种子,基于爬到的数据做搜索,还大致看过一些源码.当然,nutch对于爬虫考虑的是十分全面和细致的.每当看到屏幕上唰唰过去的爬取到的网页信息以及处理信息的时候, ...

  4. Java豆瓣电影爬虫——减少与数据库交互实现批量插入

    节前一个误操作把mysql中record表和movie表都清空了,显然我是没有做什么mysql备份的.所以,索性我把所有的表数据都清空的,一夜回到解放前…… 项目地址:https://github.c ...

  5. Java爬虫模拟登录——不给我毛概二的H某大学

    你的账号访问太频繁,请一分钟之后再试! 从大一开始 就用脚本在刷课 在专业课踢的只剩下一门C#的情况下 活活刷到一周的课 大二开始教务系统多了一个非常**的操作 退课池 and 访问频繁缓冲 难道,我 ...

  6. Java爬虫——模拟登录知乎

    登录界面,首先随意输入一个账号,登录查看发送表单的请求 可以发现请求是Post : https://www.zhihu.com/login/phone_num 发送的表单是 _xsrf: passwo ...

  7. python爬虫实战(四)--------豆瓣网的模拟登录(模拟登录和验证码的处理----scrapy)

    在利用scrapy框架爬各种网站时,一定会碰到某些网站是需要登录才能获取信息. 这两天也在学习怎么去模拟登录,通过自己码的代码和借鉴别人的项目,调试成功豆瓣的模拟登录,顺便处理了怎么自动化的处理验证码 ...

  8. 转:Scrapy安装、爬虫入门教程、爬虫实例(豆瓣电影爬虫)

    Scrapy在window上的安装教程见下面的链接:Scrapy安装教程 上述安装教程已实践,可行.(本来打算在ubuntu上安装Scrapy的,但是Ubuntu 磁盘空间太少了,还没扩展磁盘空间,所 ...

  9. Scrapy安装、爬虫入门教程、爬虫实例(豆瓣电影爬虫)

    Scrapy在window上的安装教程见下面的链接:Scrapy安装教程 上述安装教程已实践,可行.(本来打算在ubuntu上安装Scrapy的,但是Ubuntu 磁盘空间太少了,还没扩展磁盘空间,所 ...

随机推荐

  1. HDU-1862-EXCEL排序

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=1862 这个题考的就是你对sort函数的掌握:会用sort函数对字符串,数字排序,只要懂得话题目很简单 ...

  2. CSS 文件的4种引入方式

    (1)链接式  : 在网页的<head></head>标签对中使用<link>标记来引入外部样式表文件,使用html规则引入外部css  (用得比较多) : < ...

  3. 在ubuntu linux 中编写一个自己的bash脚本

    在ubuntu linux 中编写一个自己的简单的bash脚本. 实现功能:终端中输入简单的命令(以pm为例(play music)),来实现音乐的播放.注:本人ununut中安装了audacious ...

  4. iOS-直播开发(开发从底层做起)

    一直在忙, 也没写过几次播客! 但一直热衷于直播开发技术, 公司又不是直播方向的, 所以就年前忙里偷袭研究了一下直播开发, 然后翻阅了很多大神的技术博客等, 写了一个简单的Demo, 又根据网上大神们 ...

  5. ajax问题

    1. 代码:var i;for(i=0;i<10;i++){      ajaxServise(i);} 在for循环中调用ajax方法  补充页面上的数据,这样写是错误的,他不会每执行一次fo ...

  6. 很傻很二很简单的一个问题,json键值为变量如何取值

    js里的json的键值是变量的话,那么传统的做法似乎不管用了. 例如: var json={'a':'123','b':'999'}    json.a得到123,如果a用变量表示呢,有笨法for循环 ...

  7. 史上最牛逼的文件bom头清除代码,万能检测清除php,js等等

    做php开发,经常部分uft8的php文件因BOM头不同导致各种错误和错位.网上搜到的大部分所谓的去处bom工具都是2货,没有能用的.下面的一段代码,请复制到一个php文件里,然后上传到你的网站根目录 ...

  8. ScrollView嵌套ListView显示不完全、嵌套TextView不能滚动解决办法

    目录: 一.情景说明 二.最初做法 三.解决办法 一.情景说明 1.情景       最近项目刚好有一个需求,需要在一个界面中用ScrollView嵌套一个滚动的TextView和一个listView ...

  9. 【Ionic+AngularJS 开发】之『个人日常管理』App(二)

    准备工作 资源 预装工具 安装bower npm install -g bower 安装ngCordova bower install ngCordova (*由于网络获取资源的原因,后面几次建项目后 ...

  10. Swift 学习有用的学习链接(此贴随学习的深入会一直更新)

    Swift 字符串相关学习推荐下面这个链接内容 一:  http://www.jianshu.com/p/52e7580166ff (里面详细的介绍了2.0 和 2.0之前 的版本的一些不同的处) 二 ...