爬虫网络请求之JS解密二(大众点评)

时间:2022-06-25
本文章向大家介绍爬虫网络请求之JS解密二(大众点评),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

- 前言

之前在做大众点评网数据的时候,发现数据在前端显示是用标签来替换。这样爬虫采集到的就是一堆标签加一点内容所混杂的脏数据,同时发现标签中的值也是随时改变的。所以这次也是花了一点时间来整理关于大众点评JS加密的内容,给大家简单讲解一下,以此来学习借鉴如何有效安全的防范爬虫。仅供学习参考,切勿用于商业用途

一、介绍

首先随便打开大众点评网一家店,看到数据都是正常状态如图1-1,然后我们用开发者工具定位到元素上会发现如图1-2所示:

图1-1 正常数据显示
图1-2 获取元素显示

我们可以看到数据都是用<bb>标签给替换了,同时我们还发现如图1-3 、1-4所示:

图1-3 电话号码显示
图1-4 评论

店面基础信息用<bb>标签表示、数字用<cc>表示、评论用<span>标签表示。并且这些被替换的文字也不是固定的,可能过一会被替换的文字被还原,其余未被替换的文字被替换。而且标签值也会随时间发生改变,这样即使每个标签都人工标记,过一段时间数据还是会乱。

需要爬虫数据采集服务的可以联系扣,739848314

二、页面分析

我们随便查看一个被替换了的标签元素,发现它对应了一个文件如图2-1所示:

图2-1 替换的标签所对应的链接

可以看到标签一些基本信息,长度高度还有和它相关的一个链接,打开这个链接,我们可以发现是一个乱序的中文数据表格。如图2-2所示:

图2-2 被替换数据的表格

基本上我们就可以推测出来,数据被隐藏替换的大概原理,通过标签来对应表格文字,其中class的值我们可以理解为被替换数据内容的key,标签类型就好比对应的数据表,这里有三种类别标签就是对应三张不同的数据表,这样我们还需要解决的问题是:

  • (1)不同种类的标签如何对应不同的表;
  • (2)如何通过标签的class值去对应被替换的数据。 所以到这一步,我们还少一些关键的线索,我们继续看到之前页面,发现图表链接包含在一个css表中 如图2-3所示:
图2-3 隐藏的css文件

可以看到有一个css文件,我们在元素中搜索这个表 如图2-4

图2-4 css文件所在位置

这样我们便可以从网页中找到指定的文件链接,关于如何根据key值找到对应的参照表。我们打开此链接如图2-5、2-6所示:

图2-5 key值对照表
图2-6key值对照表

我们可以从图2-5、图2-6看到cc标签、span标签、bb标签每个下面都有一个url。我们打开可以发现就是之前那种格式的对应表,我们稍后再看。我们先看到我们的key值(标签class属性对应的值),后面都接着一个坐标,格式统一都是.(标签class值){background: (横坐标)px(纵坐标) px;这样我们就已经获得key值所对应的坐标,到这里我们基本可以确定,我们找到key值和所对应的坐标就可以根据标签相对应的表,利用坐标就可以找到利用标签所替换的数据。

三、JS解密

接下来,我们就需要知道如何利用获取到的坐标来获取正确被替换的数据。我们首先根据那张表格依次打开链接,查看它们元素会发现有两种格式,一种格式有元素defs标签,另一种没有。如图3-1、图3-2、图3-3所示:

图3-1 含有defs标签格式数据表

图3-1 含有defs标签格式数据表

图3-2 无defs标签格式数据表
图3-3 含有defs标签格式数据表

图3-3 含有defs标签格式数据表

总共三张表对应三个不同标签,分两种格式,我分成有defs标签和无defs标签两类。注意,这里我并没有根据标签种类去划分表类别,因为每一次获取到的格式都是随机的。也就是说今天span标签是对应有defs标签数据表,可能明天就对应不含有defs标签类的数据表。数据表格式是随机变化的。

(1)含有defs标签类别数据表解密

以地址中的bb标签为例,看地址所对应的标签值为pzgoz,如图3-4所示:,以及bb标签所对应的svg数据表,如图3-6所示:

图3-4 朝所对应标签key

根据css表找到它的对应坐标x=-294,y=-113.0,如图3-5所示:

图3-5 找到对应坐标

最后根据标签类别找到对应svg数据表链接,打开链接,如图3-6所示:

图3-6 对应的svg数据表

将坐标转为正,先从y坐标看到defs标签d属性上,y=113应该排序在d=M0 90 H600与d=M0 128 H600之间,所以id应该为4,再看textPath标签xlink:href属性为#4的就是我们所要找的行,如果之前有注意看标签width的话,就知道每一个字长为14px,x=294去除以14,等于21,这一行做list的话,list[21]应该就是朝字。代码如下:

hight = html.xpath('//defs//path/@d')
      hight = [i.replace('M0','').replace('H600','').strip() for i in hight]
      hight.append(y)
      hight = sorted(hight, key=lambda i: int(i))
      y = hight.index(y)
      #若纵坐标与标签y相等相等时,取同行的高度
      if y != len(hight) - 1 and hight[y] == hight[y + 1]:
          y = y + 1
      content = html.xpath('//textpath/text()')[y]
      content = ''.join(content).strip()
      x = int(int(x) / 14)
      return content[x]
(2)不含有defs标签类别数据表解密

之前步骤一样,直接跳到找到对应的svg数据表,如图3-7所示:

图3-7 不含有defs标签类别数据表

比如数字3,x=-232.0px y=-140.0px,先全部取正数,y=140 对应在标签text属性y=118和y=163之间,同样排序在第四位,所以取第四行为list,同样232/14=16,list[16]=3;同时也可以数标签text属性x中232排在224-238之间,也就是16-17位之间,但要取16为下标。代码如下:

hight = html.xpath('//text/@y')
        hight.append(y)
        hight = sorted(hight, key=lambda i: int(i))
        y = hight.index(y)
        # 若纵坐标与标签y相等相等时,取同行的高度
        if y != len(hight) - 1 and hight[y] == hight[y + 1]:
            y = y + 1
        content = html.xpath('//text/text()')[y]
        content = ''.join(content).strip()
        x = int(int(x) / 14)
        return content[x]

四、代码实现

这里附上我测试的代码,仅供学习参考,切勿用于商业用途。直接使用前,请带上自己浏览器的请求头参数。

#coding=utf-8
import requests
import lxml.html
import re
from decimal import Decimal
#获取svg表格中被替换的数据
def get_hide_char(x,y,type,url):
    response = requests_middler(url)
    html = lxml.html.fromstring(response.content)
    #网页格式是否有defs元素
    if len(html.xpath('//defs//path/@d'))<1:#如果没有defs标签
        hight = html.xpath('//text/@y')
        hight.append(y)
        hight = sorted(hight, key=lambda i: int(i))
        y = hight.index(y)
        # 若纵坐标与标签y相等相等时,取同行的高度
        if y != len(hight) - 1 and hight[y] == hight[y + 1]:
            y = y + 1
        content = html.xpath('//text/text()')[y]
        content = ''.join(content).strip()
        x = int(int(x) / 14)
        return content[x]
    else:#如果含有defs标签
        hight = html.xpath('//defs//path/@d')
        hight = [i.replace('M0','').replace('H600','').strip() for i in hight]
        hight.append(y)
        hight = sorted(hight, key=lambda i: int(i))
        y = hight.index(y)
        #若hight相等时,取同行的高度
        if y != len(hight) - 1 and hight[y] == hight[y + 1]:
            y = y + 1
        content = html.xpath('//textpath/text()')[y]
        content = ''.join(content).strip()
        x = int(int(x) / 14)
        return content[x]

#请求url统一方式
def requests_middler(url):
    headers={'User-Agent':''}#加入自己的User-Agent
    html = requests.get(url, headers=headers)
    return html
#获取css表中key值对应的坐标,以及标签对应svg表链接
def get_position_xy(html,password,type,url):
    #获取key值对应的坐标
    rule = re.compile(password + '{background:-(.*?).0px -(.*?).0px;')
    result = re.findall(rule, html)[0]
    x = result[0]
    y = result[1]
    #获取标签所对应的svg表格
    rule = re.compile(type + '[class^="w+"]{(.*?)}', re.S)
    result = re.findall(rule,html)[0]
    rule = re.compile('url((.*?))')
    href = re.findall(rule, result)
    url = 'http:' + href[0]
    hide_char=get_hide_char(x,y,type,url)
    return hide_char

#解密前数据做好清洗,清洗成标签与文字组成的list,保证还原字符串顺序
def get_hide_string(s,url):
    result=[]
    a_list = re.split('</S+>',s)
   # print(a_list)
    b=[]
    for i in a_list:
        try:
            dex = i.index('<')
        except ValueError:
            b.append(i)
            continue
        if dex!=0:
            b.append(i[:dex])
        rule = re.compile('<(.*?) class="(.*?)"')
        tag = re.findall(rule,i[dex:])[0]
        b.append(tag)
    try:
        b.remove('')
    except ValueError:
        pass
    print(b)#b为已清洗好的list,接下来分别还原标签替换数据
    html = requests_middler(url)
    html = html.text
    for tag in b:
        #遇到类别标签则分类进行筛选
        if tag[0]=='span'or tag[0]== 'cc' or tag[0]=='bb':
            hide_char=get_position_xy(html,tag[1],tag[0],url)
            result.append(hide_char)
        #防止被其它标签干扰
        elif tag[0]==''or tag[0]=='p'or tag[0]=='div':
            continue
        #遇到中文及不含标签则直接加入list
        else:
            result.append(tag)
    return ''.join(result).strip('n').strip()
if __name__=='__main__':
    url ='https://www.dianping.com/shop/507576/review_all'
    cookies={}#自己加入当时访问的cookies
    headers = {'User-Agent': ''}#加入自己的User-Agent
    response=requests.get(url,headers=headers,cookies=cookies)
    html = lxml.html.fromstring(response.content)
    #找到标签数据,必须要保留标签,这里以商店地址为例
    rule = re.compile('<div class="address-info">(.*?)</div>', re.S)
    address=re.findall(rule,response.text)[0]
    address = address.strip().replace('n','').replace('&nbsp','')
    #找到css表,css表链接存放位置固定,所以直接获取
    css = html.xpath('//link[@rel="stylesheet"]/@href')[1]
    url ='http:'+css.strip()
    result = get_hide_string(address,url)
    result=result.replace(' ','')
    print('解密前数据:'+address)
    print('解密后数据:'+result)
代码运行结果 如图4-1所示:
图3-7 不含有defs标签类别数据表

图4-1 运行结果图

六、总结

总结一下,就是先找到被替换数据标签,发现替换时对应的css表,找到css表,根据标签属性class值作key值去找到对应的坐标,同时找到标签类别所对应的svg数据表链接,最后参照数据表坐标得到被隐藏的数据。 大众点评前端JS加密方法与平时遇到的都不太一样,所以花了一些时间来讲,之后遇到不一样的JS加密也会给大家一起学习探讨,同样如果发现文章的不足,欢迎指出。