基于搜狗微信的爬虫知识总结

webdriver chrome国内下载地址

http://npm.taobao.org/mirrors/chromedriver/

禁止图片加载

设置参数禁用

// 禁用图片加载
HashMap<String, Object> images = new HashMap();
images.put("images", 2);
HashMap<String, Object> 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的问题

问题现象与 https://stackoverflow.com/questions/47248275/chrome-webdriver-send-keys-does-not-send-3 这个的现象一致,表现为遇到数字1,则1和之后的字符都无法输入了,但是前面的是不受影响。

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 event
passwordBox.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进行访问的。这里我没进行特别具体的尝试。

通过修改源码方式

通过开启开发者模式(这个是测试过有效果的)

这篇文章提到了开启浏览器的开发者模式,绕过携程的webdriver检测机制,我测试了的确是有效果的,原先浏览器会显示“目前通过自动化软件进行控制”,开启了开发者模式之后,就没有这个提示了,现在右上角会有“在开发者模式下运行插件不安全”的提示。 https://blog.csdn.net/weixin_43582101/article/details/90416944

原文是python的代码,下面给出java的代码。

ChromeOptions options = new ChromeOptions();
options.setExperimentalOption("excludeSwitches", new String[]{"enable-automation"});

PS: edit at 2019-06-03 这个开发者模式好像不能在headless模式下运行。

搜狗微信尝试

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

输入验证码后的效果

情况一: 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来安装。

Linux安装方式

windows的比较简单,直接 https://docs.conda.io/en/latest/miniconda.html 找到windows的安装就行,然后运行 enter description here

PS:这篇文章提到了手动下载字库的方法,http://github.com/tesseract-ocr/tessdata ,但是只有基于4.0.0的训练数据,没有我现在装的5.0.0的

运行识别代码

运行下面的代码,注意,由于tesserocr和 pillow都是在conda里面安装的,这里要在 Anaconda Powershell Prompt 打开python

import tesserocr
from PIL import Image
image=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 其他安装方式参照 https://www.cnblogs.com/pcat/p/6790058.html

  • 报错2,没解决 RuntimeError: Failed to init API, possibly an invalid tessdata path: C:\ProgramData\Miniconda3\ 尝试1 失败:添加TESSDATA_PREFIX环境变量为Tesseract-OCR路径,参考 https://blog.csdn.net/l_longqihang/article/details/80621597 文章底下的评论1,报错路径的确变成了新的TESSDATA_PREFIX配置的路径,但是还是一样的错误 尝试2 失败:将尝试1中的tessdata文件夹直接复制到报错的C:\ProgramData\Miniconda3\路径下,还是不行。

验证码自动识别尝试2(使用了尝试3之后已经成功)

由于上面的conda方式失败了,这里使用pip3来装一下试试 参考这篇文章处理 http://www.mamicode.com/info-detail-2353336.html

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的基础上改的)

https://www.imooc.com/article/45278 这里只用到了文章中的一点,就是把tessdata拷贝到 C:\Python36\ !!!! 竟然是放到python的根目录,而不是报错的目录!!!! 我这边的Python安装路径是C:\Users\whl\AppData\Local\Programs\Python\Python37-32,放到这个目录下果然就好了。

这样尝试2利用PIP的方式就是好了的。 尝试1还是不行,但是有篇文章说也是放到了C:\\Miniconda3这样的一个目录,文章暂时找不到了,也不尝试了。

PS:这篇文章里说到语言包,还是有用的

验证码自动识别尝试4(未进行)

还没尝试,前面的尝试已经可以了,就暂时先测试准确率了。 https://blog.csdn.net/qq_18402475/article/details/80347203 再来,这次用JAVA的这篇文章再试试

搜狗验证码识别的准确率测试

先用 https://mp.weixin.qq.com/mp/verifycode?cert=1559036330018.0938 这个网址访问,可以得到一批搜狗的验证码。

#!/bin/bash

for((i=1;i<=10;i++));
do 
wget http://mp.weixin.qq.com/mp/verifycode?cert=1559012250995.9602 -O ./verifycode/$i.jpg 
done

用这个脚本下载若干个验证码,然后还是用下面的脚本进行识别,基本上不能用!没有一个对的,有些甚至识别不出来

import tesserocr
from PIL import Image
image=Image.open(r'C:\Users\whl\Desktop\验证码\1.jpg')
print(tesserocr.image_to_text(image))

进行手工的训练

https://blog.csdn.net/sylsjane/article/details/83751297 https://blog.csdn.net/a745233700/article/details/80175883 https://www.cnblogs.com/cnlian/p/5765871.html https://www.jianshu.com/p/5c8c6b170f6f (这篇文章最详细,还讲解了识别不全的处理办法) 用这些文章的方法进行训练。

训练好的数据,进行测试 tesseract test.PNG test -l zwp 注意,这里的-l 需要指定新训练的训练集的名字,否则会默认用eng。PS:python的代码没有进行测试,懒得找跟-l zwp等价的python api了。

测试结果显示,只有一个是对的,而且还是训练出有结果的那个。

打码平台测试

这里测试了 http://www.fateadm.com/ 斐斐打码平台。测试效果很理想,测试了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);
    }

验证码获取

如果是文章列表页的纯英文的验证码,是访问http://mp.weixin.qq.com/mp/verifycode?cert=1559040332692.3264 这样的Url生成一个全新的验证码图片,同时在cookie里记录下当前图片对应的sig,点提交的时候把sig和正确的验证码校验的。 但是公众号的首页的英文 + 数字的验证码,这个没有仔细研究,因为这两种验证码都没办法直接在页面上进行获取了。

尝试使用webDriver的截图功能实现,下面这篇文章正是利用了这个思路。 https://www.cnblogs.com/lvye001/p/9873796.html

保存验证码的关键代码

					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的校验 例如直接访问 https://weixin.sogou.com/weixin?type=1&s_from=input&query=%E8%A1%A2%E5%B7%9E%E6%96%87%E6%97%85&ie=utf8&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地址,http://mp.weixin.qq.com/profile?src=3&timestamp=1559039238&ver=1&signature=v55DcSJ3r49YkKB8nuQTuO5W5nyIf-FFynVFSspNgXnVkdy3psJyQS8EMRSkDe823z7J8R8ijw611KOhv*aQ== 里面有个timestamp和signature字段是动态生成的,这也防止了爬虫爬取固定的公众号首页地址。这里的解决办法是用webDriver进行前面的模拟动作,当出现真正的文章页面的时候url就是固定了的。

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

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

关于搜狗微信爬取的总结

  1. 前期的所有操作必须使用webDriver进行模拟(至少我是这么做的)。
  2. 如果只是需要爬取特定的几个公众号,用https://weixin.sogou.com/weixin?type=1&s_from=input&query=%E8%A1%A2%E5%B7%9E%E6%96%87%E6%97%85&ie=utf8&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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值