搜狗微信采集

10月29日搜狗微信改版了,无法通过搜索公众号名字获取对应文章了,不过通过搜索主题获取对应文章还是可以的

目的:获取搜狗微信中搜索主题返回的文章。

  涉及反爬机制:cookie设置,js加密。

 按照正常的采集流程,此时按F12打开浏览器的开发者工具,利用选择工具点击列表中文章标题,查看源码中列表中文章url的所在位置,再用xpath获取文章url的值,也就是这个href的值,为避免混乱,我们称之为“列表页面的文章url”。

 可以看到“列表页面的文章url”需要拼接,一般这种情况需要在浏览器中正常访问一下这篇文章,对比观察跳转后的url(我们称之为“真实的文章url”),再缺头补头缺脚补脚即可。下面是两个url的对比:

列表中的URL:

/link?url=dn9a_-gY295K0Rci_xozVXfdMkSQTLW6cwJThYulHEtVjXrGTiVgSxXWO5jMDx5JEf8m2C7b5SMTWh5L4nxL0VqXa8Fplpd9WUVPGtaUij32IWbSS4PlqhHR25kJ9aGxdoUK5-HQJHSBUdf-_CsD_hzCsZ6RhgCTjzjsLxbGyyz3rRO5tNfFYA0Do8PmX4rKqC0uMe50XyeKPeFRq4j4_a_ZPEIMWLv_k45Jm2PIBwT5mKmu2NYgixeplxJSDXjNAcSZfw2J2-OAFV_3u-a3OQ..&type=2&query=%E5%BE%90%E6%82%B2%E9%B8%BF%E5%A5%B3%E5%84%BF%E5%8E%BB%E4%B8%96

真实的URL:

mp.weixin.qq.com/s?src=11&timestamp=1574832410&ver=1999&signature=WFJg65*rck3iW8Cj0Ibp6U5*M8wjhHQougTWnYP8*t39Pt7JeknMUpas*YfNdKV1MEGRDw6THCuefEWhYUDqWrewr-dno2Y0B7yp-3ox-8*A0OaEC6VGcPMdJCYwEw*0&new=1

这里很明显两个url的路径不一致,应该是中间经过了一些调转,python的requests库是带自动调转功能,我们先把域名https://mp.weixin.qq.com补上试一下访问

直接点击列表URL,会被拦截,让输出验证码,明显这里做了反爬限制,那么这里开始,我们就需要抓包分析了。这里用到的工具是Firefox浏览器的开发者工具。抓包观察的是从搜索结果页面列表文章点击跳转到文章页面的过程,这里点击文章超链接会在新窗口打开,我们只需要在网页源码中把对应a标签的target属性改为空,就可以在一个窗口中观察整个流程的数据包了。

 fiddle抓包工具分析:

 

 通过抓包我们可以找到搜索结果页面跳转到文章页面的过程,这里观察发现,“列表页面的文章url”返回的结果中就包含了“真实的文章url”的信息,这意味着我们只需要正确访问到“列表页面的文章url”,根据返回的数据就能拼接出“真实的文章url”并访问了,这样我们就实现从“列表页面的文章url”到“真实的文章url”的跳转了!

抓包分析手动获取的“列表也米娜的文章URL”无法访问

此时我们的目标就从获取“真实的文章url”转变到正确的访问“列表页面的文章url”了,继续分析抓包数据中的“列表页面的文章url”信息:

抓包数据:
mothod:GET
/link?url=dn9a_-gY295K0Rci_xozVXfdMkSQTLW6cwJThYulHEtVjXrGTiVgSxXWO5jMDx5JEf8m2C7b5SMTWh5L4nxL0VqXa8Fplpd9WUVPGtaUij32IWbSS4PlqhHR25kJ9aGxdoUK5-HQJHSBUdf-_CsD_hzCsZ6RhgCTjzjsLxbGyyz3rRO5tNfFYA0Do8PmX4rKqC0uMe50XyeKPeFRq4j4_a_ZPEIMWLv_k45Jm2PIBwT5mKmu2NYgixeplxJSDXjNAcSZfw2J2-OAFV_3u-a3OQ..&
type=2&
query=%E5%BE%90%E6%82%B2%E9%B8%BF%E5%A5%B3%E5%84%BF%E5%8E%BB%E4%B8%96&
k=69&
h=F

headers:
GET /link?url=dn9a_-gY295K0Rci_xozVXfdMkSQTLW6cwJThYulHEtVjXrGTiVgSxXWO5jMDx5JEf8m2C7b5SMTWh5L4nxL0VqXa8Fplpd9WUVPGtaUij32IWbSS4PlqhHR25kJ9aGxdoUK5-HQJHSBUdf-_CsD_hzCsZ6RhgCTjzjsLxbGyyz3rRO5tNfFYA0Do8PmX4rKqC0uMe50XyeKPeFRq4j4_a_ZPEIMWLv_k45Jm2PIBwT5mKmu2NYgixeplxJSDXjNAcSZfw2J2-OAFV_3u-a3OQ..&type=2&query=%E5%BE%90%E6%82%B2%E9%B8%BF%E5%A5%B3%E5%84%BF%E5%8E%BB%E4%B8%96&k=69&h=F HTTP/1.1
Host: weixin.sogou.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Referer: https://weixin.sogou.com/weixin?type=2&ie=utf8&s_from=hotnews&query=%E5%BE%90%E6%82%B2%E9%B8%BF%E5%A5%B3%E5%84%BF%E5%8E%BB%E4%B8%96
Cookie: 
    ABTEST=0|1574763082|v1; 
    IPLOC=CN3100; 
    SUID=B535A9B43E18960A000000005DDCFA4A; 
    SUID=B535A9B42B12960A000000005DDCFA4A; 
    weixinIndexVisited=1; 
    SUV=006B15E7B4A935B55DDCFA4AE52C6946; 
    SNUID=75F46975C0C559893A3FC125C16C5243; 
    sct=5; 
    JSESSIONID=aaa8EnASm4RZA51Sj2y4w
Upgrade-Insecure-Requests: 1

这里的重点有三个:

请求参数:对比我们获取的“列表页面的文章url”分析可以发现,这里多了两个参数“k”、“h”,这是需要我们设法获取的。headers:经过测试该网站对User-Agent敏感,一次访问前后User-Agent需要一致。

Cookie:Cookie中参数需要获取才能正确访问该url。这些参数分别是:ABTEST、IPLOC、JSESSIONID、SNUID、SUID、SUV

获取参数“k”、“h”

按照经验,从一个url转变成另一个url有两种情况:跳转和javascript字符串处理。经过多次抓包分析发现,搜索结果页面点击文章超链接到我们现在的目标url并没有存在跳转情况,抓包数据中的“列表页面的文章url”和我们获取的“列表页面的文章url”可以判定为同一个url,所以猜测为javascript字符串处理。经过一番搜寻,发现搜索结果页面的源码中有一段非常可疑的代码:

 

 这其中最重要的代码就是:this.href+="&k="+b+"&h="+a,这代码就是在点击事件发生时给a标签href属性的内容添加"&k="、"&h=",正是用这段代码对该url的参数进行js加密和添加的。我们只需要把这段代码用python实现就可以解决这个问题了,下面是实现python实现代码

def get_k_h(url):     
  b = int(random.random() * 100) + 1
   a = url.find("url=")
   url = url + "&k=" + str(b) + "&h=" + url[a + 4 + 21 + b: a + 4 + 21 + b + 1]
  reuturn url

获取Cookie的参数

观察抓包数据可以发现,当我们一开始访问时并没有带任何cookie,但经过一系列请求,到我们的目标请求时候,浏览器已经通过前面请求的返回数据包的Set-Cookie属性把Cookie构造出来了,而我们要做的就是在Cookie构造从无到有这个过程中找到所有ResponseHeaders中带SetCookie属性的而且参数是我们需要的参数的请求,并模拟访问一遍,就能得到所有参数并构建出我们需要的Cookie了。

 

例如搜狗微信搜索接口的请求的ResponseHeaders就有5个Set-Cookie字段,其中ABTEST、SNUID、IPLOC、SUID都是我们最终构造Cookie所需的参数(和最后的Cookie值对比可以发现,这里的SUID值还不是我们最终需要的,要在后面的数据包中继续发掘)。

  经过分析,经过四个请求获取到的ResponseHeaders后我们就能正确构建Cookie了:

1. 得到ABTEST、SNUID、IPLOC、SUID:
  https://weixin.sogou.com/weixin?type=2&query=%E5%92%B8%E8%9B%8B%E8%B6%85%E4%BA%BA&ie=utf8&s_from=input&_sug_=n&_sug_type_=1&w=01015002&oq=&ri=1&sourceid=sugg&sut=750912&sst0=1573092594229&lkt=0%2C0%2C0&p=40040108
2. 需要IPLOC、SNUID,得到SUID:
  https://www.sogou.com/sug/css/m3.min.v.7.css
3. 需要ABTEST、IPLOC、SNUID、SUID,得到JSESSIONID:
  https://weixin.sogou.com/websearch/wexinurlenc_sogou_profile.jsp
4. 需要IPLOC、SNUID、SUID,得到SUV
  https://pb.sogou.com/pv.gif

这四个请求都能根据前面请求获取到的Cookie参数来构造自己需要的Cookie去正确访问。值得注意的是最后一个请求,除了需要正确拼接Cookie外,还需要获取正确的请求参数才能正常访问:

 那么根据这些解析出来的参数和前面三个请求得到的Cookie参数就能正确访问第四个请求并得到所需的所有Cookie参数啦!

构造正确的请求信息

此时,我们已经分析出所有正确模拟请求的流程了,梳理一下:

  1. 获取“k”、“h”参数,传入搜索结果页面得到的“列表页面的文章ur”,调用get_k_h()即可。
  2. 获取所需Cookie参数,构造正确的Cookie,按照流程三给出的4个url,分别构造请求获取ResponseHeaders中的SetCookie即可。
  3. 构造正确的请求访问“列表页面的文章url”。
  4. 根据3中请求返回的数据,拼接出“真实的文章url”,也就是流程二。
  5. 请求“真实的文章url”,得到真正的文章页面数据。

  至此,所有分析结束,可以愉快的码代码啦!

结语:此次采集涉及到的反爬技术是Cookie构造和简答的js加密,难度不大,最重要的是耐心和细心。此外提醒各位看客大人遵循爬虫道德,不要对他人网站造成伤害,peace!

import random
from urllib import parse
import requests
from lxml import etree
import re
import json
import urllib3

urllib3.disable_warnings()


# 获取查询请求所需的K 和 h参数
# function() {
#   var b = Math.floor(100 * Math.random()) + 1,
#     a = this.href.indexOf("url="),
#     c = this.href.indexOf("&k=");
#     - 1 !== a && -1 === c && (a = this.href.substr(a + 4 + parseInt("21") + b, 1), this.href += "&k=" + b + "&h=" + a)
# }

def get_k_h(url):
    b = int(random.random() * 100) + 1
    a = url.find("url=")
    url = url + "&k=" + str(b) + "&h=" + url[a + 4 + 21 + b:a + 4 + 21 + b + 1]
    return url


def log(content):
    with open(r"F:\个人总结\log\sgwx.log", "a+", encoding="utf-8") as file:
        file.write(str(content) + "\n")


def get_proxy():
    for i in range(20):
        print("get proxy try time:", i + 1)
        proxy_url = requests.get(
            "http://svip.kdlapi.com/api/getproxy/?orderid=936273988936860&num=1&protocol=1&method=2&an_an=1&an_ha=1&quality=2&sep=1").text
        proxy = {"https": "https://%s" % proxy_url}
        try:
            requests.get("https://www.baidu.com/", proxies=proxy, timeout=5)
            print(proxy)
            return proxy
        except:
            continue
    return None


def get_uigs_para(response):
    log("从查询响应页面获取uigs_para")
    uigs_para = re.findall('var uigs_para = (.*?);', response.text, re.S)[0]
    if 'passportUserId ? "1" : "0"' in uigs_para:
        uigs_para = uigs_para.replace('passportUserId ? "1" : "0"', '0')
    uigs_para = json.loads(uigs_para)
    exp_id = re.findall('uigs_para.exp_id = "(.*?)";', response.text, re.S)[0]
    uigs_para['right'] = 'right0_0'
    uigs_para['exp_id'] = exp_id[:-1]
    log(uigs_para)
    return uigs_para


def get_cookie(response, uigs_para, UserAgent):
    SetCookie = response.headers['Set-Cookie']
    cookie_params = {
        "ABTEST": re.findall('ABTEST=(.*?);', SetCookie, re.S)[0],
        "SNUID": re.findall('SNUID=(.*?);', SetCookie, re.S)[0],
        "IPLOC": re.findall('IPLOC=(.*?);', SetCookie, re.S)[0],
        "SUID": re.findall('SUID=(.*?);', SetCookie, re.S)[0]
    }
    log("获取查询后页面的响应headers:{}".format(cookie_params))
    url = "https://www.sogou.com/sug/css/m3.min.v.7.css"
    headers = {
        "Accept": "text/css,*/*;q=0.1",
        "Accept-Encoding": "gzip, deflate, br",
        "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
        "Connection": "keep-alive",
        "Cookie": "SNUID={}; IPLOC={}".format(cookie_params['SNUID'], cookie_params['IPLOC']),
        "Host": "www.sogou.com",
        "Referer": "https://weixin.sogou.com/",
        "User-Agent": UserAgent
    }
    response_css = requests.get(url, headers=headers, verify=False)
    SetCookie = response_css.headers['Set-Cookie']
    cookie_params['SUID'] = re.findall('SUID=(.*?);', SetCookie, re.S)[0]
    log("请求m3.min.v.7.css之后获取headers:{}".format(cookie_params))

    url = "https://weixin.sogou.com/websearch/wexinurlenc_sogou_profile.jsp"
    headers = {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate, br",
        "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
        "Connection": "keep-alive",
        "Cookie": "ABTEST={}; SNUID={}; IPLOC={}; SUID={}".format(cookie_params['ABTEST'], cookie_params['SNUID'],
                                                                  cookie_params['IPLOC'],
                                                                  cookie_params['SUID']),
        "Host": "weixin.sogou.com",
        "Referer": response.url,
        "User-Agent": UserAgent
    }
    response_jps = requests.get(url, headers=headers, verify=False)
    SetCookie = response_jps.headers['Set-Cookie']
    cookie_params['JSESSIONID'] = re.findall('JSESSIONID=(.*?);', SetCookie, re.S)[0]
    log("获取wexinurlenc_sogou_profile.jsp的headers:{}".format(cookie_params))

    url = "https://pb.sogou.com/pv.gif"
    headers = {
        "Accept": "image/webp,*/*",
        "Accept-Encoding": "gzip, deflate, br",
        "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
        "Connection": "keep-alive",
        "Cookie": "SNUID={}; IPLOC={}; SUID={}".format(cookie_params['SNUID'], cookie_params['IPLOC'],
                                                       cookie_params['SUID']),
        "Host": "pb.sogou.com",
        "Referer": "https://weixin.sogou.com/",
        "User-Agent": UserAgent
    }
    response_gif = requests.get(url, headers=headers, params=uigs_para, verify=False)
    SetCookie = response_gif.headers['Set-Cookie']
    cookie_params['SUV'] = re.findall('SUV=(.*?);', SetCookie, re.S)[0]
    log("获取pv.gif的headers:{}".format(cookie_params))
    return cookie_params


def main_page(list_url, UserAgent):
    headers = {
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
        "Accept-Encoding": "gzip, deflate, br",
        "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
        "Connection": "keep-alive",
        "Host": "weixin.sogou.com",
        "Upgrade-Insecure-Requests": "1",
        "User-Agent": UserAgent,
    }
    # 请求查询
    response = requests.get(url=list_url, headers=headers, verify=False)
    log("查询后的response headers:{}".format(response.headers))
    html = etree.HTML(response.text)
    # 获取查询到的文章链接
    urls = ['https://weixin.sogou.com' + i for i in html.xpath('//div[@class="img-box"]/a/@href')]
    uigs_para = get_uigs_para(response)
    params = get_cookie(response, uigs_para, UserAgent)

    approve_url = 'https://weixin.sogou.com/approve?uuid={}'.format(uigs_para['uuid'])
    headers2 = {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate, br",
        "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
        "Connection": "keep-alive",
        "Cookie": "ABTEST={}; IPLOC={}; SUID={}; SUV={}; SNUID={}; JSESSIONID={};".format(params['ABTEST'],
                                                                                          params['IPLOC'],
                                                                                          params['SUID'], params['SUV'],
                                                                                          params['SNUID'],
                                                                                          params['JSESSIONID']),
        "Host": "weixin.sogou.com",
        "Referer": response.url,
        "User-Agent": UserAgent,
        "X-Requested-With": "XMLHttpRequest"
    }
    for url in urls:
        log("https://weixin.sogou.com/approve?uuid=")
        requests.get(approve_url, headers=headers2, verify=False)
        url = get_k_h(url)
        headers3 = {
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
            "Accept-Encoding": "gzip, deflate, br",
            "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
            "Connection": "keep-alive",
            "Cookie": "ABTEST={}; SNUID={}; IPLOC={}; SUID={}; JSESSIONID={}; SUV={}".format(params['ABTEST'],
                                                                                             params['SNUID'],
                                                                                             params['IPLOC'],
                                                                                             params['SUID'],
                                                                                             params['JSESSIONID'],
                                                                                             params['SUV']),
            "Host": "weixin.sogou.com",
            "Referer": list_url,
            "Upgrade-Insecure-Requests": "1",
            "User-Agent": UserAgent
        }
        response3 = requests.get(url, headers=headers3, verify=False)

        fragments = re.findall("url \+= '(.*?)'", response3.text, re.S)
        itemurl = ''
        for i in fragments:
            itemurl += i
        # 文章url拿正文
        headers4 = {
            "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
            "accept-encoding": "gzip, deflate, br",
            "accept-language": "zh-CN,zh;q=0.9,en;q=0.8",
            "cache-control": "max-age=0",
            "user-agent": UserAgent
        }
        response4 = requests.get(itemurl, headers=headers4, verify=False)
        html = etree.HTML(response4.text)
        print(response4.url)
        print(response4.status_code)
        print(html.xpath('//meta[@property="og:title"]/@content')[0])


if __name__ == '__main__':
    keys = "具荷拉身亡"
    page = 1
    UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0"
    url = "https://weixin.sogou.com/weixin?type=2&s_from=input&query={}&_sug_=n&_sug_type_=&page={}".format(
        parse.quote(keys), page)
    print("搜索生成连接:", url)
    main_page(url, UserAgent)

参考:https://www.cnblogs.com/hyonline/p/11812977.html

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值