爬虫解决方案之爬取“搜狗引擎”

作者: 云爬虫技术研究笔记 | 来源:发表于2018-10-29 22:36 被阅读61次
    主题.jpg

    1.爬取的背景

        为甚我们会提到“搜狗引擎爬虫呢”,一切根源来自于最近需要爬取领英的会员资料,我们可以通过人名 | 领英的方式具体的搜索以查询结果,这只是爬取领英的其中一个方法,具体的方法我们之后会讲,大家可以期待一下哦!,好了,讲回正题,我们该如何爬取“搜狗引擎”呢,我们先去首页看看

    搜狗首页.png

    我们可以看到每个链接的右下角都会有对应的时间快照,这个是什么意思呢,因为我们爬取领英时需要登录,如果我们不登陆的去访问领英,可以看到的只是一部分信息,但是这些信息也是足够了,问题在于我们不是每次访问领英都可以看得到具体的会员页,它会根据IP访问次数来限制你查看的会员页,比如:你查看10个左右的会员页之后会自动跳转到登录页,而我们并不想登录,原因之后再说。所以,在我们没有足够的IP去访问领英页面的时候怎么办???

        答案就是:去爬“搜狗引擎”已经爬取好的缓存页-也就是快照页,这样我们等于变相去“假装”拥有大量IP去爬取领英了。

    2.搜狗的反爬措施

        搜狗虽然名气没有百度的大,但是也算是搜索引擎中比较知名的,毕竟可以背靠“腾讯”大佬,靠着微信搜索上一波流量,可以说,搜索在反爬这一方面也是挺下功夫的,大家可以看看“搜狗”关于爬虫的招聘

    搜狗招聘.png
    具体可以参考这里
        不过他们貌似对于搜索方面的反爬限制的不是很严格,毕竟对于搜索来说,过于严苛的反爬反而对于用户的交互不是很理想, 我们来看看搜索之后的请求Cookie。
    cookie.png
    经过我测试,发现其中有几个参数异常重要,也是影响搜索反爬虫措施的关键参数,SUID、SNUID以及SUV。

    SUID

    SUID具体的含义可以自行百度,这里只讲述它生成的过程。当我们访问sogou搜索首页的时候,set-cookies中便会生成一个SUID参数的内容,除非重启浏览器,不然短时间内SUID并不会改变。SUID的值应该是sogou服务端随便分配的,只有当重新开启一个session时它的值才会更新。

    SNUID

    SNUID是sogou反爬虫的重点,sogou也是对同一个SNUID访问次数做了限制,而超过限制后,会跳转到验证码页面,只有输入验证码重新验证以后,SNUID才会更新,访问才能继续进行。那么SNUID是如何生成的呢?经过测试,应该是由javascript生成的,当然前提是要有SUID,SUID是生成SNUID的基础。

    SUV

    SUV参数内容是由javascript生成的,测试并没有发现其对于反爬虫有何影响,故本文不做详细介绍。

    被屏蔽现象

    同样,要解决反爬虫问题,我们先来看看触发反爬虫的现象。当同一个SNUID访问次数受限后,继续访问sogou会跳转到一个验证码页面。
    URL地址:

    http://www.sogou.com/antispider/?from=%2fweb%3Fquery%3d152512wqe%26ie%3dutf8%26_ast%3d1488957312%26_asf%3dnull%26w%3d01029901%26p%3d40040100%26dp%3d1%26cid%3d%26cid%3d%26sut%3d578%26sst0%3d1488957299160%26lkt%3d3%2C1488957298718%2C1488957298893
    

    大家可以看一下,是因为访问的过于频繁,导致需要验证码。


    验证.png

    这里,搜狗有个心机,他们说是由于IP问题,导致大多数人会从IP这点去考虑,然而,真相往往隐藏在深处.....
    看看这个页面的源码

    HTTP/1.1 200 OK
    Server: nginx
    Date: Thu, 27 Oct 2016 04:41:19 GMT
    Content-Type: text/html
    Connection: keep-alive
    X-Powered-By: PHP/5.3.3
    Expires: Thu, 19 Nov 1981 08:52:00 GMT
    Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
    Pragma: no-cache
    Content-Length: 5130
    <!DOCTYPE HTML>
    <html>
    <head>
        <meta charset="utf-8">
        <link rel="shortcut icon" href="//www.sogou.com/images/logo2014/new/favicon.ico" type="image/x-icon">
        <title>搜狗搜索</title>
        <link rel="stylesheet" href="static/css/anti.min.css?v=1"/>
        <script src="//dl.web.sogoucdn.com/common/lib/jquery/jquery-1.11.0.min.js"></script>
        <script src="static/js/antispider.min.js?v=2"></script>
        <script>
            var domain = getDomain();
            window.imgCode = -1;
            (function() {
                function checkSNUID() {
                    var cookieArr = document.cookie.split('; '),
                        count = 0;
                    for(var i = 0, len = cookieArr.length; i < len; i++) {
                        if (cookieArr[i].indexOf('SNUID=') > -1) {
                            count++;
                        }
                    }
                    return count > 1;
                }
                if(checkSNUID()) {
                    var date = new Date(), expires;
                    date.setTime(date.getTime() -100000);
                    expires = date.toGMTString();
                    document.cookie = 'SNUID=1;path=/;expires=' + expires;
                    document.cookie = 'SNUID=1;path=/;expires=' + expires + ';domain=.www.sogou.com';
                    document.cookie = 'SNUID=1;path=/;expires=' + expires + ';domain=.weixin.sogou.com';
                    document.cookie = 'SNUID=1;path=/;expires=' + expires + ';domain=.sogou.com';
                    document.cookie = 'SNUID=1;path=/;expires=' + expires + ';domain=.snapshot.sogoucdn.com';
                    sendLog('delSNUID');
                }
                if(getCookie('seccodeRight') === 'success') {
                    sendLog('verifyLoop');
                    setCookie('seccodeRight', 1, getUTCString(-1), location.hostname, '/');
                }
                if(getCookie('refresh')) {
                    sendLog('refresh');
                }
            })();
            function setImgCode(code) {
                try {
                    var t = new Date().getTime() - imgRequestTime.getTime();
                    sendLog('imgCost',"cost="+t);
                } catch (e) {
                }
                window.imgCode = code;
            }
            sendLog('index');
            function changeImg2() {
                if(window.event) {
                    window.event.returnValue=false
                }
            }
        </script>
    </head>
    <body>
    <div class="header">
        <div class="logo"><a href="/"><img width="180" height="60" src="//www.sogou.com/images/logo2014/error180x60.png"></a></div>
        <div class="other"><span class="s1">您的访问出错了</span><span class="s2"><a href="/">返回首页&gt;&gt;</a></span></div>
    </div>
    <div class="content-box">
        <p class="ip-time-p">IP:183.129.218.233<br>访问时间:2016.10.27 12:41:19</p>
        <p class="p2">用户您好,您的访问过于频繁,为确认本次访问为正常用户行为,需要您协助验证。</p>
        <p class="p3"><label for="seccodeInput">验证码:</label></p>
        <form name="authform" method="POST" id="seccodeForm" action="/">
            <p class="p4">
                <input type=text name="c" value="" placeholder="请输入验证码" id="seccodeInput">
                <input type="hidden" name="tc" id="tc" value="">
                <input type="hidden" name="r" id="from" value="%2Fweb%3Fquery%3D%E6%9F%90%E8%8D%A3%26ie%3Dutf8%26_ast%3D1477536768%26_asf%3Dnull%26w%3D01029901%26cid%3D" >
                <input type="hidden" name="m" value="0" >            <span class="s1">
                    <script>imgRequestTime=new Date();</script>
                    <a onclick="changeImg2();" href="javascript:void(0)">
                        <img id="seccodeImage" onload="setImgCode(1)" onerror="setImgCode(0)" src="util/seccode.php?tc=1477543279" width="100" height="40" alt="请输入图中的验证码" title="请输入图中的验证码">
                    </a>
                </span>
                <a href="javascript:void(0);" id="change-img" onclick="changeImg2();" style="padding-left:50px;">换一张</a>
                <span class="s2" id="error-tips" style="display: none;"></span>
            </p>
        </form>
        <p class="p5">
            <a href="javascript:void(0);" id="submit">提交</a>
            <span>提交后没解决问题?欢迎<a href="http://fankui.help.sogou.com/index.php/web/web/index?type=10&anti_time=1477543279&domain=www.sogou.com" target="_blank">反馈</a>。</span>
        </p>
    </div>
    <div id="ft"><a href="http://fuwu.sogou.com/" target="_blank">企业推广</a><a href="http://corp.sogou.com/" target="_blank">关于搜狗</a><a href="/docs/terms.htm?v=1" target="_blank">免责声明</a><a href="http://fankui.help.sogou.com/index.php/web/web/index?type=10&anti_time=1477543279&domain=www.sogou.com" target="_blank">意见反馈</a><br>&nbsp;&copy;&nbsp;2016<span id="footer-year"></span>&nbsp;SOGOU&nbsp;-&nbsp;<a href="http://www.miibeian.gov.cn" target="_blank" class="g">京ICP证050897号</a>&nbsp;-&nbsp;京公网安备1100<span class="ba">00000025号</span></div>
    <script src="static/js/index.min.js?v=0.1.3"></script>
    </body>
    </html><!--zly-->
    

    自动化生成SNUID

    虽然知道了SNUID值生成的过程,但只有实现了自动化生成,才能正真绕过反爬虫的限制。

    通过访问验证码页面获取

    当访问验证码页面,并填写验证码完成验证后,会重新生成一个新的SNUID,而此请求可以重复发送(不需要再次输入验证码),每次发送都会生成一个新的SNUID。

    通过模拟浏览器访问,执行javascript

    可以利用phantomjs去爬取sogou页面,也能获取SNUID值。

    获取SNUID代码

    #! -*- coding:utf-8 -*-
    '''
    获取SNUID的值
    '''
    import requests
    import json
    import time
    import random
    '''
    方法(一)通过phantomjs访问sogou搜索结果页面,获取SNUID的值
    '''
    def phantomjs_getsnuid():
        from selenium import webdriver
        d=webdriver.PhantomJS('D:\python27\Scripts\phantomjs.exe',service_args=['--load-images=no','--disk-cache=yes'])
        try:
            d.get("https://www.sogou.com/web?query=")
            Snuid=d.get_cookies()[5]["value"]
        except:
            Snuid=""
        d.quit()
        return Snuid
    '''
    方法(二)通过访问特定url,获取body里面的id
    '''
    def Method_one():
        url="http://www.sogou.com/antispider/detect.php?sn=E9DA81B7290B940A0000000058BFAB0&wdqz22=12&4c3kbr=12&ymqk4p=37&qhw71j=42&mfo5i5=7&3rqpqk=14&6p4tvk=27&eiac26=29&iozwml=44&urfya2=38&1bkeul=41&jugazb=31&qihm0q=8&lplrbr=10&wo65sp=11&2pev4x=23&4eyk88=16&q27tij=27&65l75p=40&fb3gwq=27&azt9t4=45&yeyqjo=47&kpyzva=31&haeihs=7&lw0u7o=33&tu49bk=42&f9c5r5=12&gooklm=11&_=1488956271683"
        headers={"Cookie":
        "ABTEST=0|1488956269|v17;\
        IPLOC=CN3301;\
        SUID=E9DA81B7290B940A0000000058BFAB6D;\
        PHPSESSID=rfrcqafv5v74hbgpt98ah20vf3;\
        SUIR=1488956269"
        }
        try:
            f=requests.get(url,headers=headers).content
            f=json.loads(f)
            Snuid=f["id"]
        except:
            Snuid=""
        return Snuid
    '''
    方法(三)访问特定url,获取header里面的内容
    '''
    def Method_two():
        url="https://www.sogou.com/web?query=333&_asf=www.sogou.com&_ast=1488955851&w=01019900&p=40040100&ie=utf8&from=index-nologin"
        headers={"Cookie":
        "ABTEST=0|1488956269|v17;\
        IPLOC=CN3301;\
        SUID=E9DA81B7290B940A0000000058BFAB6D;\
        PHPSESSID=rfrcqafv5v74hbgpt98ah20vf3;\
        SUIR=1488956269"
        }
        f=requests.head(url,headers=headers).headers
        print f
    '''
    方法(四)通过访问需要输入验证码解封的页面,可以获取SNUID
    '''
    def Method_three():
        '''
        http://www.sogou.com/antispider/util/seccode.php?tc=1488958062 验证码地址
        '''
        '''
        http://www.sogou.com/antispider/?from=%2fweb%3Fquery%3d152512wqe%26ie%3dutf8%26_ast%3d1488957312%26_asf%3dnull%26w%3d01029901%26p%3d40040100%26dp%3d1%26cid%3d%26cid%3d%26sut%3d578%26sst0%3d1488957299160%26lkt%3d3%2C1488957298718%2C1488957298893
        访问这个url,然后填写验证码,发送以后就是以下的包内容,可以获取SNUID。
        '''
        import socket
        import re
        res=r"id\"\: \"([^\"]*)\""
        s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        s.connect(('www.sogou.com',80))
        s.send('''
    POST http://www.sogou.com/antispider/thank.php HTTP/1.1
    Host: www.sogou.com
    Content-Length: 223
    X-Requested-With: XMLHttpRequest
    Content-Type: application/x-www-form-urlencoded; charset=UTF-8
    Cookie: CXID=65B8AE6BEE1CE37D4C63855D92AF339C; SUV=006B71D7B781DAE95800816584135075; IPLOC=CN3301; pgv_pvi=3190912000; GOTO=Af12315; ABTEST=8|1488945458|v17; PHPSESSID=f78qomvob1fq1robqkduu7v7p3; SUIR=D0E3BB8E393F794B2B1B02733A162729; SNUID=B182D8EF595C126A7D67E4E359B12C38; sct=2; sst0=958; ld=AXrrGZllll2Ysfa1lllllVA@rLolllllHc4zfyllllYllllljllll5@@@@@@@@@@; browerV=3; osV=1; LSTMV=673%2C447; LCLKINT=6022; ad=6FwTnyllll2g@popQlSGTVA@7VCYx98tLueNukllll9llllljpJ62s@@@@@@@@@@; SUID=EADA81B7516C860A57B28911000DA424; successCount=1|Wed, 08 Mar 2017 07:51:18 GMT; seccodeErrorCount=1|Wed, 08 Mar 2017 07:51:45 GMT
    c=6exp2e&r=%252Fweb%253Fquery%253Djs%2B%25E6%25A0%25BC%25E5%25BC%258F%25E5%258C%2596%2526ie%253Dutf8%2526_ast%253D1488957312%2526_asf%253Dnull%2526w%253D01029901%2526p%253D40040100%2526dp%253D1%2526cid%253D%2526cid%253D&v=5
        ''')
        buf=s.recv(1024)
        p=re.compile(res)
        L=p.findall(buf)
        if len(L)>0:
            Snuid=L[0]
        else:
            Snuid=""
        return Snuid
    def getsnuid(q):
        while 1:
            if q.qsize()<10:
                Snuid=random.choice([Method_one(),Method_three(),phantomjs_getsnuid()])
                if Snuid!="":
                    q.put(Snuid)
                    print Snuid
                    time.sleep(0.5)
    if __name__=="__main__":
        import Queue
        q=Queue.Queue()
        getsnuid(q)
    

    cookie问题解决方案

    1. SUID的值获取比较简单,直接访问sogou即可获取。
    2. 获取到SUID的值后,再去获取SNUID值(可通过以上几种方式)
    3. 获取到SNUID后,可保存到队列中。

    说明:SNUID的值如果不去使用它,可以存放很久,直到使用它到上限才会作废;SUID一般不会做次数限制,可以一直使用。

    3. 最终成型方案

    本次爬取采用的就是最简单、暴力、易懂的策略,即强行多次试验发现,SNUID的值的上限次数大概是100次左右,我们保险一点,我们每爬取50页我们就重新以无cookie身份去获取依次SNUID,这样我们就能保证SNUID的稳定性,另外还有一点重要的是,最近搜狗加紧了限制,对于IP也做了限制,因此,不是我们每次去访问都会获取SNUID,因此,我们需要多个IP,这点大家可以考虑使用一些开源的IP池,本人目前也在开发开源的IP池,大家感兴趣可以关注一波,我的终身项目哦哦!!!

    相关文章

      网友评论

        本文标题:爬虫解决方案之爬取“搜狗引擎”

        本文链接:https://www.haomeiwen.com/subject/djgitqtx.html