博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
基于搜狗微信的爬虫知识总结
阅读量:6096 次
发布时间:2019-06-20

本文共 11231 字,大约阅读时间需要 37 分钟。

  hot3.png

webdriver chrome国内下载地址

禁止图片加载

设置参数禁用

// 禁用图片加载HashMap
images = 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= 这种验证码是访问这样页面会出来的,如果没出来多刷新几次就会出来了 enter description here

即使手动输入验证码也是没有效果了

输入验证码后的效果

情况一: 405 Not Allowed nginx

情况二: 不管怎么输入验证码还是在这个页面,但是验证码内容变了,这样就还是不行。

情况三: 只有在这种情况下,输入验证码后就正常了,IP也被解封了

  • 如果出现这样的验证码 这种是具体的公众号文章列表页,多刷新几次就会出来了 enter description here

这个输入验证码后就能继续使用了,而且这个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的安装就行,然后运行 enter description here

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&timestamp=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();        }    }

关于搜狗的反爬机制

  1. 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 就可以访问了,但是访问多次仍然会出验证码。

  2. 公众号文章列表页url中的签名值 当从搜索页面点击某个公众号,进入到公众号首页的时候,观察url地址,QTuO5W5nyIf-FFynVFSspNgXnVkdy3psJyQS8EMRSkDe823z7J8R8ijw611KOhv*aQ== 里面有个timestamp和signature字段是动态生成的,这也防止了爬虫爬取固定的公众号首页地址。这里的解决办法是用webDriver进行前面的模拟动作,当出现真正的文章页面的时候url就是固定了的。

  3. 验证码的动态生成 这个是比较变态的地方,即使出现了验证码,想要获取这个验证码进行识别也是比较困难的,每次访问的时候都会产生新的图片,即使通过右键 -> 图片另存为保存也是会请求一张新的图片,需要通过webdirver的截图功能进行保存。

  4. 短时高频访问 在短时间内多次爬取,或者页面上连续刷新时,会触发搜狗的封IP机制,这个时候就会需要输入验证码了。输入正确的验证码会解封IP,也可以更换代理IP。

关于搜狗微信爬取的总结

  1. 前期的所有操作必须使用webDriver进行模拟(至少我是这么做的)。
  2. 如果只是需要爬取特定的几个公众号,用sug=n&sug_type= 类似这样的带query的网址直接进去就行了,不用从weixin.sogou.com模拟输入进入。
  3. 验证码只能通过webdriver抠图实现,页面上每次获取就变成新的了,就跟当前页面的验证码不一致了。
  4. 在公众号文章的列表页里,那些文章的url可以从页面上获取,而且可以通过httpclient直接访问(这个是不会出验证码了),虽然也会有timestamp和signature参数,但是在过期时间内足够爬虫爬取页面内容了。这里建议用httpclient爬取,如果用webDriver模拟点击的话,效率挺低的。PS:另外这个文章页面上的有些数据可能是ajax动态填充的,httpclient的获取页面源码里可能是没有的,这样就考虑使用webDriver模拟打开获取吧。
  5. 验证码使用斐斐打码,基本上可以识别搜狗数字 +英文的6位验证码和4位纯英文的两种验证码。如果识别不正确的话,就重新刷新页面识别一次。
  6. 效率问题暂时不考虑,至少功能上先实现。

转载于:https://my.oschina.net/OttoWu/blog/3057807

你可能感兴趣的文章
科技巨头的交通争夺战
查看>>
当中兴安卓手机遇上农行音频通用K宝 -- 卡在“正在通讯”,一直加载中
查看>>
Shell基础之-正则表达式
查看>>
JavaScript异步之Generator、async、await
查看>>
讲讲吸顶效果与react-sticky
查看>>
c++面向对象的一些问题1 0
查看>>
直播视频流技术名词
查看>>
iOS13-适配夜间模式/深色外观(Dark Mode)
查看>>
网易跟贴这么火,背后的某个力量不可忽视
查看>>
品友互动受邀2018商汤人工智能峰会
查看>>
企业级java springboot b2bc商城系统开源源码二次开发-hystrix参数详解(八)
查看>>
java B2B2C 多租户电子商城系统- 整合企业架构的技术点
查看>>
IOC —— AOP
查看>>
比特币现金将出新招,推动比特币现金使用
查看>>
数据库的这些性能优化,你做了吗?
查看>>
某大型网站迁移总结(完结)
查看>>
mysql的innodb中事务日志(redo log)ib_logfile
查看>>
部署SSL证书后,网页内容造成页面错误提示的处理办法
查看>>
MS SQLSERVER通用存储过程分页
查看>>
60.使用Azure AI 自定义视觉服务实现物品识别Demo
查看>>