webdriver chrome国内下载地址
禁止图片加载
设置参数禁用
// 禁用图片加载HashMapimages = new HashMap();images.put("images", 2);HashMap prefs = new HashMap();prefs.put("profile.default_content_setting_values", images);options.setExperimentalOption("prefs", prefs);
有效果
利用chrome插件
ChromeOptions op = new ChromeOptions(); op.addExtensions(new File("C:\\whatever\\Block-image_v1.0.crx")); driver = new ChromeDriver(op);
未尝试,效果未知
无法输入数字1的问题
问题现象与 这个的现象一致,表现为遇到数字1,则1和之后的字符都无法输入了,但是前面的是不受影响。
- 它的一个解决办法
send_keys(Keys.NUMPAD3)
在我这里是没效果 - 它的第二个解决办法执行script,可以用 这里的形式写,更符合代码逻辑。测试通过,而且中文也是支持的。
import org.openqa.selenium.JavascriptExecutor;String script = "arguments[0].value=arguments[1]";WebElement passwordBox = driver.findElement(By.name("Password"));JavascriptExecutor js = (JavascriptExecutor) driver;js.executeScript(script, passwordBox, user.password); // 这里arguments[0]就是passwordBox,arguments[1]就是user.password// send a space or TAB to trigger key eventpasswordBox.sendKeys(" "); // passwordBox.sendKeys(Key.TAB);
PS,测试环境为
- 阿里云CentOS Linux release 7.5.1804 (Core),google-chrome-stable.x86_64 74.0.3729.169-1,74版本的webdriver。无法输入1
- 阿里云CentOS Linux release 7.5.1804 (Core),google-chrome-stable.x86_64 71,71版本的webdriver。无法输入1
- 虚拟机CentOS Linux release 7.5.1804,google-chrome-stable.x86_64 74.0.3729.157-1,74版本的webdriver。无法输入1
headless问题
当在服务器上运行不带headless的图形化浏览器时,用xmanager的xshell连接,就能在xmanager里打开那个浏览器,弹出来的桌面选项好像要选 "virtural window" 这个选项。这个有助于服务器的可视化错误调试。
webDriver 防检测的方式
据说通过webDriver访问网站的时候,会使用一些特殊的js变量,正常的访问下,这些变量是没有值的,但是通过webdriver访问,这些变量是有值的,网站也就认为是通过webdriver进行访问的。这里我没进行特别具体的尝试。
通过修改源码方式
- 可以通过检出源码的方式,把一些变量的名字全修改掉,重新编译。我没试过。
- 使用vim直接修改打包好的webdriver文件(vim功能真是强大!) 下面这篇文章提到了vim修改内置变量名的方法,但是没试过是否有效。
通过开启开发者模式(这个是测试过有效果的)
这篇文章提到了开启浏览器的开发者模式,绕过携程的webdriver检测机制,我测试了的确是有效果的,原先浏览器会显示“目前通过自动化软件进行控制”,开启了开发者模式之后,就没有这个提示了,现在右上角会有“在开发者模式下运行插件不安全”的提示。
原文是python的代码,下面给出java的代码。
ChromeOptions options = new ChromeOptions();options.setExperimentalOption("excludeSwitches", new String[]{"enable-automation"});
PS: edit at 2019-06-03 这个开发者模式好像不能在headless模式下运行。
搜狗微信尝试
- 代理尝试的时候,如果出现这样的图片 sug=n&sug_type= 这种验证码是访问这样页面会出来的,如果没出来多刷新几次就会出来了
即使手动输入验证码也是没有效果了
输入验证码后的效果
情况一: 405 Not Allowed nginx
情况二: 不管怎么输入验证码还是在这个页面,但是验证码内容变了,这样就还是不行。
情况三: 只有在这种情况下,输入验证码后就正常了,IP也被解封了
- 如果出现这样的验证码 这种是具体的公众号文章列表页,多刷新几次就会出来了
这个输入验证码后就能继续使用了,而且这个ip也会被解封了
代理ip信息
说明,HTTPS的代理不能给HTTP的使用。
名称 | 类型 | 费用 | 说明 |
---|---|---|---|
站大爷 | http | ||
快代理 | http | ||
代理云 | 未知 | 合租版 100一天,3000一个月,独享版1500一个月 | |
xici代理/西瓜代理 | http/https | 个人版9每天,98月,专业版19天,198月,企业版49天,498月 | 只能筛选出https,无法筛选出http |
神鸡代理 | http/https | 个人版147每个月,专业版228每个月 | |
89免费代理 | HTTP | ||
旗云代理 | http/https | 开放代理6天,30周,80月,399年,私密代理 39天,199周,699月,5999年 | 暂时测试这个吧,测试过之后发现基本上没办法使用,全部代理IP都会出验证码 |
只测试了旗云,无效,所有代理IP都出验证码,但是觉得神鸡代理可能效果会好点(因为免费的账号的质量比较好,基本上都能联通,出验证码的,手动输入下这个IP也能用了,但是只有按月收费的,所以没测试)
验证码自动识别尝试(暂时失败)
验证码以搜狗的验证码为例,搜狗有两种验证码,一种是数字 + 英文,一般在搜索的页面出来,另一种纯英文的是具体的文章页面。
安装 tesserocr 和 tesseract
tesseract 是google开源的Ocr的一个库 tesserocr 则是在tesseract的Python操作包
安装conda
tesseract里面用Pip 无法安装tesserocr ,需要先装conda,用conda来安装。
windows的比较简单,直接 找到windows的安装就行,然后运行
PS:这篇文章提到了手动下载字库的方法, ,但是只有基于4.0.0的训练数据,没有我现在装的5.0.0的
运行识别代码
运行下面的代码,注意,由于tesserocr和 pillow都是在conda里面安装的,这里要在 Anaconda Powershell Prompt 打开python
import tesserocrfrom PIL import Imageimage=Image.open(r'C:\Users\whl\Desktop\验证码\1.jpg')print(tesserocr.image_to_text(image))
-
报错1 PIL找不到 这里原生的PIL是没有python3.7版本的,要按照pillow才行。前面用了conda安装了tesserocr,所以这里也只能用conda安装pillow, 运行命令
conda install pillow
其他安装方式参照 -
报错2,没解决
RuntimeError: Failed to init API, possibly an invalid tessdata path: C:\ProgramData\Miniconda3\
尝试1 失败:添加TESSDATA_PREFIX环境变量为Tesseract-OCR路径,参考 文章底下的评论1,报错路径的确变成了新的TESSDATA_PREFIX配置的路径,但是还是一样的错误 尝试2 失败:将尝试1中的tessdata文件夹直接复制到报错的C:\ProgramData\Miniconda3\路径下,还是不行。
验证码自动识别尝试2(使用了尝试3之后已经成功)
由于上面的conda方式失败了,这里使用pip3来装一下试试 参考这篇文章处理
PIL还是要用下面命令安装扩展版的pillow pip3 install pillow
-
报错1 ERROR: tesserocr-2.4.0-cp37-cp37m-win_amd64.whl is not a supported wheel on this platform. 这个是我的python版本是32位的,所以tesserocr也要选择32位的python32位版本
-
报错2
RuntimeError: Failed to init API, possibly an invalid tessdata path: D:\Program Files\Tesseract-OCR/
还是一样的错,这次甚至路径都是对了,还是失败了
验证码自动识别尝试3(其实是在尝试2的基础上改的)
这里只用到了文章中的一点,就是把tessdata拷贝到 C:\Python36\
!!!! 竟然是放到python的根目录,而不是报错的目录!!!! 我这边的Python安装路径是C:\Users\whl\AppData\Local\Programs\Python\Python37-32
,放到这个目录下果然就好了。
这样尝试2利用PIP的方式就是好了的。 尝试1还是不行,但是有篇文章说也是放到了C:\\Miniconda3
这样的一个目录,文章暂时找不到了,也不尝试了。
PS:这篇文章里说到语言包,还是有用的
验证码自动识别尝试4(未进行)
还没尝试,前面的尝试已经可以了,就暂时先测试准确率了。 再来,这次用JAVA的这篇文章再试试
搜狗验证码识别的准确率测试
先用 这个网址访问,可以得到一批搜狗的验证码。
#!/bin/bashfor((i=1;i<=10;i++));do wget http://mp.weixin.qq.com/mp/verifycode?cert=1559012250995.9602 -O ./verifycode/$i.jpg done
用这个脚本下载若干个验证码,然后还是用下面的脚本进行识别,基本上不能用!没有一个对的,有些甚至识别不出来
import tesserocrfrom PIL import Imageimage=Image.open(r'C:\Users\whl\Desktop\验证码\1.jpg')print(tesserocr.image_to_text(image))
进行手工的训练
(这篇文章最详细,还讲解了识别不全的处理办法) 用这些文章的方法进行训练。
训练好的数据,进行测试 tesseract test.PNG test -l zwp
注意,这里的-l 需要指定新训练的训练集的名字,否则会默认用eng。PS:python的代码没有进行测试,懒得找跟-l zwp等价的python api了。
测试结果显示,只有一个是对的,而且还是训练出有结果的那个。
打码平台测试
这里测试了 斐斐打码平台。测试效果很理想,测试了10个都是对的(这里测试的是纯英文的那种验证码),10块钱对应1W分,10分可以进行一次识别,换算过来就是一分钱识别一次,算是比较划算的。ps:如果是6位英文 + 数字的要15积分一次,也就是1.5分钱一次。
另外它的API需要上传图片的string数据,可以用下面的JAVA代码对图片进行string的转换
/** * 字符串转图片 * * @param imgStr --->图片字符串 * @param filename --->图片名 * @return */ public static boolean generateImage(String imgStr, String filename) { if (imgStr == null) { return false; } BASE64Decoder decoder = new BASE64Decoder(); try { // 解密 byte[] b = decoder.decodeBuffer(imgStr); // 处理数据 for (int i = 0; i < b.length; ++i) { if (b[i] < 0) { b[i] += 256; } } OutputStream out = new FileOutputStream("D:/Systems/" + filename); out.write(b); out.flush(); out.close(); return true; } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return false; } /** * 图片转字符串 * * @param filePath --->文件路径 * @return */ public static String getImageStr(String filePath) { InputStream inputStream = null; byte[] data = null; try { inputStream = new FileInputStream(filePath); data = new byte[inputStream.available()]; inputStream.read(data); inputStream.close(); } catch (IOException e) { e.printStackTrace(); } // 加密 BASE64Encoder encoder = new BASE64Encoder(); return encoder.encode(data); } /* * 测试代码 */ public static void main(String[] args) { String imageStr = getImageStr("C:\\Users\\whl\\Desktop\\verifycode\\1.jpg"); System.out.println(imageStr); boolean generateImage = generateImage(imageStr, "001.jpg"); System.out.println(generateImage); }
验证码获取
如果是文章列表页的纯英文的验证码,是访问 这样的Url生成一个全新的验证码图片,同时在cookie里记录下当前图片对应的sig,点提交的时候把sig和正确的验证码校验的。 但是公众号的首页的英文 + 数字的验证码,这个没有仔细研究,因为这两种验证码都没办法直接在页面上进行获取了。
尝试使用webDriver的截图功能实现,下面这篇文章正是利用了这个思路。
保存验证码的关键代码
web = webdriver.Chrome() web.maximize_window() #页面最大化 web.get(list_response.url) #请求验证码页面 web.save_screenshot("D:\\quan.png")#截取全屏并保存到该路径 imgs = web.find_element_by_id('verify_img') #验证码页面定位验证码图片元素位置 #第一步取参数 place = imgs.location #验证码的坐标位置 size = imgs.size #验证码的大小 #第二部整理参数(数据为元组) rangle = (int(place['x']), int(place['y']), int(place['x'] + size['width']), int(place['y'] + size['height'])) # 写成我们需要截取的位置坐标 #第三步导入PIL,打开截图 i = Image.open("D:\\quan.png") #第四部进行抠图操作 frame4 = i.crop(rangle) # 使用Image的crop函数,从截图中再次截取我们需要的区域 #第五步 保存抠下来的验证码 frame4.save('D:\\cropped.png') # 保存我们接下来的验证码图片 进行打码 web.find_element_by_id('input').send_keys(yundama())#调用云打码返回参数并发送到input框 time.sleep(1) web.find_element_by_id('bt').click()#点击提交 time.sleep(2) web.close() #关闭浏览器
下面是测试过的JAVA代码,可以正确扣下相应的验证码了。
/** * 测试抠图获取验证码 */ @Test public void testGetVerifyCodeImg() throws InterruptedException { String verifyCodeUrl = "http://mp.weixin.qq.com/profile?src=3×tamp=1559040313&ver=1&signature=v55DcSJ3r49YkKB8nu*QTuO5W5nyIf-FFynVF*SspNgXnVkdy3psJyQS8EMRSkDeBJVQW4bCm2fcHJ44C-M0Gw=="; String imagePath = "E:\\snapshot.jpg"; String verifyImagePath = "E:\\verify.jpg"; WebDriver webDriver = null; try { webDriver = webDriverPool.getActiveWebDriver(); webDriver.get(verifyCodeUrl); // 窗口最大化 webDriver.manage().window().maximize(); // 获取当前页面的截图 File scrFile = ((TakesScreenshot) webDriver).getScreenshotAs(OutputType.FILE); // 把图片保存到本地 try { FileUtils.copyFile(scrFile, new File(imagePath)); } catch (IOException e) { e.printStackTrace(); } // 获取图片的element WebElement verifyElement = webDriver.findElement(By.id("verify_img")); // 获取图片的位置和大小 Point point = verifyElement.getLocation(); Dimension dimension = verifyElement.getSize(); // 截图出验证码 try { BufferedImage image = ImageIO.read(new File(imagePath)); BufferedImage verifyImage = image.getSubimage(point.x, point.y, dimension.width, dimension.height); ImageIO.write(verifyImage, "jpg", new File(verifyImagePath)); } catch (IOException e) { e.printStackTrace(); } System.out.println("done"); } finally { webDriver.quit(); } }
关于搜狗的反爬机制
-
cookie的校验 例如直接访问 sug=n&sug_type= 这样的公众号页面,搜狗是一定会出验证码的。这里是可以通过设置一系列的header 和 cookie的值进行规避的,这个网上有很多文章讲到了,一般是SUV,SUID,SNUID之类的。 我本地用postman测试了设置headers中的User-Agent = Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36 COOKIE中的SUV = 0074178B73C13FEB5A1BC4917B21D967 就可以访问了,但是访问多次仍然会出验证码。
-
公众号文章列表页url中的签名值 当从搜索页面点击某个公众号,进入到公众号首页的时候,观察url地址,QTuO5W5nyIf-FFynVFSspNgXnVkdy3psJyQS8EMRSkDe823z7J8R8ijw611KOhv*aQ== 里面有个timestamp和signature字段是动态生成的,这也防止了爬虫爬取固定的公众号首页地址。这里的解决办法是用webDriver进行前面的模拟动作,当出现真正的文章页面的时候url就是固定了的。
-
验证码的动态生成 这个是比较变态的地方,即使出现了验证码,想要获取这个验证码进行识别也是比较困难的,每次访问的时候都会产生新的图片,即使通过右键 -> 图片另存为保存也是会请求一张新的图片,需要通过webdirver的截图功能进行保存。
-
短时高频访问 在短时间内多次爬取,或者页面上连续刷新时,会触发搜狗的封IP机制,这个时候就会需要输入验证码了。输入正确的验证码会解封IP,也可以更换代理IP。
关于搜狗微信爬取的总结
- 前期的所有操作必须使用webDriver进行模拟(至少我是这么做的)。
- 如果只是需要爬取特定的几个公众号,用sug=n&sug_type= 类似这样的带query的网址直接进去就行了,不用从weixin.sogou.com模拟输入进入。
- 验证码只能通过webdriver抠图实现,页面上每次获取就变成新的了,就跟当前页面的验证码不一致了。
- 在公众号文章的列表页里,那些文章的url可以从页面上获取,而且可以通过httpclient直接访问(这个是不会出验证码了),虽然也会有timestamp和signature参数,但是在过期时间内足够爬虫爬取页面内容了。这里建议用httpclient爬取,如果用webDriver模拟点击的话,效率挺低的。PS:另外这个文章页面上的有些数据可能是ajax动态填充的,httpclient的获取页面源码里可能是没有的,这样就考虑使用webDriver模拟打开获取吧。
- 验证码使用斐斐打码,基本上可以识别搜狗数字 +英文的6位验证码和4位纯英文的两种验证码。如果识别不正确的话,就重新刷新页面识别一次。
- 效率问题暂时不考虑,至少功能上先实现。