爬取知网数据遇到图片验证码的解决方法
详细描述以及思路:
1:使用selenium爬取知网数据过程中,刚开始是采用线程休眠的方法来骗,但是后来发现,这个方法没法解决页数超过一百多的问题。后来就转战想要解决图片验证码的方法,刚开始想过使用OCR来识别,但是效果不好。最后想到的方法是调用第三方接口来识别验证码,思路如下:
1)首先截图,将验证码通过截图的方式截取下来存到一定位置。
2)调用第三方接口来识别截图中的验证码,并填写到验证码输入框中。因为不是每次都百分百的识别出来,所以,循环多次识别,知道识别正确位置。
注意:第三方接口使用的是百度的文字识别接口,详细请点击:
http://ai.baidu.com/tech/ocr
部分核心代码如下:
// if (i % 15 == 0) {
// Thread.sleep(20000);
WebElement bodyEle = driver.findElement(By.tagName("body"));
List<WebElement> list = bodyEle.findElements(By.tagName("input"));
/////////////////////////////////////////////////////
// 注意:截图和识别是一个连续的过程,如果验证码识别出错,那么久无法进行到下一步,那么就该继续截图识别
// java 截图
// 获取验证码的位置在屏幕中 //*[@id="CheckCodeImg"]
// WebElement checkImage = driver.findElement(By.id("CheckCodeImg"));
// int x = checkImage.getLocation().getX();
// int y = checkImage.getLocation().getY();
// System.out.println(x);
// System.out.println(y);
/*WebElement checkImage2 = driver.findElement(By.id("CheckCodeImg"));
String message = checkImage2.getAttribute("src");
System.out.println("X:" + checkImage2.getLocation().getX());
System.out.println("Y:" + checkImage2.getLocation().getY());
*/
//WebElement checkImage2 = driver.findElement(By.id("CheckCodeImg"));
// /html/body/p[1]/label
WebElement text1 = driver.findElement(By.xpath("/html/body/p[1]/label"));
String text1Str = text1.getText();
System.out.println(text1.getText());
// while (text1Str.equals("请输入验证码")) { // 验证码一直存在,就一直截图验证
do {
pageCount++;
WebElement checkImage2 = driver.findElement(By.id("CheckCodeImg"));
// String message = checkImage2.getAttribute("src");
int X = checkImage2.getLocation().getX();
int Y = checkImage2.getLocation().getY();
System.out.println(X + " " + Y);
// System.out.println("X:" + checkImage2.getLocation().getX());
// System.out.println("Y:" + checkImage2.getLocation().getY());
File scrFile = ((RemoteWebDriver) driver).getScreenshotAs(OutputType.FILE);
byte[] bytes = File2byte(scrFile);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
BufferedImage image = ImageIO.read(bais);
// 修改图片存放的位置
File pathCheck = new File(ResourceUtils.getURL("classpath:").getPath());
if (!pathCheck.exists()) pathCheck = new File("");
// System.out.println("path:"+path1.getAbsolutePath());
File uploadCheck = new File(pathCheck.getAbsolutePath(), "src/main/webapp/checkCodeImage");
if (!uploadCheck.exists()) uploadCheck.mkdirs();
String pathKey = uploadCheck.getAbsolutePath() + "\\screenfile.png"; // 这里最终是 detailUrl.txt
// 路径文件
// File filekey = new File(pathKey);
File screenFile = new File(pathKey);
// 如果文件夹路径不存在,则创建
if (!screenFile.getParentFile().exists()) {
screenFile.getParentFile().mkdirs();
}
// 图片的像素为 63 22 坐标为:469 39
// 截取这儿是一个问题 因为如果第一次不对 那么 图片的位置就会变化
// 首先获取图片的位置 两次图片的位置 如: 412, 40, 63, 22
// 第一次: X:469 Y:39
// 第二次: X:469 Y:76
BufferedImage subimage = image.getSubimage((X - 63), Y, 63, 22);
ImageIO.write(subimage, "png", screenFile);
// ImageIO.write(image, "png", screenFile);
// Thread.sleep(10000);
// 验证码的识别
// 初始化一个AipOcr
AipOcr client = new AipOcr(APP_ID, API_KEY, SECRET_KEY);
// 可选:设置网络连接参数
client.setConnectionTimeoutInMillis(2000);
client.setSocketTimeoutInMillis(60000);
// 可选:设置log4j日志输出格式,若不设置,则使用默认配置
// 也可以直接通过jvm启动参数设置此环境变量
System.setProperty("aip.log4j.conf", "path/to/your/log4j.properties");
// 调用接口
//String path = "E:\\Images\\screenfile.png";
org.json.JSONObject res = client.basicGeneral(pathKey, new HashMap<String, String>());
net.sf.json.JSONObject myJson = net.sf.json.JSONObject.fromObject(res.toString());
Map m = myJson;
Object object = m.get("words_result");
JSONArray json = JSONArray.fromObject(object);
List<Map<String, Object>> mapListJson = json;
Map<String, Object> checkMap = mapListJson.get(0);
String key = (String) checkMap.get("words");
System.out.println(key);
// 输入框
WebElement inputEle = driver.findElement(By.id("CheckCode"));//list.get(0);
//inputEle.sendKeys("123");
inputEle.sendKeys(key);
Thread.sleep(5000);
// 提交按钮
WebElement submitButn = driver.findElement(By.xpath("/html/body/p[1]/input[2]"));//list.get(1);
submitButn.click();
// 如果页数超过60那么就休息两分钟
if (i % 60 == 0 || i % 70 == 0 || i % 90 == 0){
Thread.sleep(12000);
}
} while (text1Str.equals("请输入验证码"));
// }
System.out.println("退出循环了,验证通过了");
/*File scrFile = ((RemoteWebDriver) driver).getScreenshotAs(OutputType.FILE);
byte[] bytes = File2byte(scrFile);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
BufferedImage image = ImageIO.read(bais);
File screenFile = new File("E:\\Images\\screenfile.png");
// 如果文件夹路径不存在,则创建
if (!screenFile.getParentFile().exists()) {
screenFile.getParentFile().mkdirs();
}
// 图片的像素为 63 22 坐标为:469 39
BufferedImage subimage = image.getSubimage(412, 40, 63, 22);
ImageIO.write(subimage, "png", screenFile);
// Thread.sleep(10000);
// 验证码的识别
// 初始化一个AipOcr
AipOcr client = new AipOcr(APP_ID, API_KEY, SECRET_KEY);
// 可选:设置网络连接参数
client.setConnectionTimeoutInMillis(2000);
client.setSocketTimeoutInMillis(60000);
// 可选:设置log4j日志输出格式,若不设置,则使用默认配置
// 也可以直接通过jvm启动参数设置此环境变量
System.setProperty("aip.log4j.conf", "path/to/your/log4j.properties");
// 调用接口
String path = "E:\\Images\\screenfile.png";
org.json.JSONObject res = client.basicGeneral(path, new HashMap<String, String>());
net.sf.json.JSONObject myJson = net.sf.json.JSONObject.fromObject(res.toString());
Map m = myJson;
Object object = m.get("words_result");
JSONArray json = JSONArray.fromObject(object);
List<Map<String, Object>> mapListJson = (List) json;
Map<String, Object> checkMap = mapListJson.get(0);
String key = (String) checkMap.get("words");
System.out.println(key);
// 输入框
WebElement inputEle = list.get(0);
inputEle.sendKeys(key);
Thread.sleep(2000);
// 提交按钮
WebElement submitButn = list.get(1);
submitButn.click();*/
//Thread.sleep(5000);
代码中只涉及到实现截图和识别部分,并且其中有许多注释的部分并没有删除,想详细记录下来,以备后期思考。至于模拟用户搜索等功能如有网友需要,请在下方评论或私聊,给与帮助解决。