镀金天空-CSS偏移

镀金天空-CSS偏移

前言:

​ ①仅作学习所用,不可非法利用

​ ②网页结构的变化较多,代码的可用周期较短,仅作学习使用

​ ③如有侵权,请联系我删除!!谢谢

正文:

​ 最近我也是找到了一个有趣的网站,这个网站里有很多爬虫相关的练习题,和ACM赛制相似,采用的是在线OJ的方式来进行答案校检。glidedsky中包含了现在大部分网站所采用的反爬技术,难度较市面上书本上的练习题难上一些,油兴趣的朋友可以加我好友一起冲他,hh。

镀金天空-CSS偏移_第1张图片

​ 我也是好久没写博客了,最近是毕业季忙于找工作实习QAQ。目前也算是安定下来了,所以恢复计划博客更新,嘿嘿。好了,回归到正题,今天要讲的是glidedsky的一道练习题–CSS偏移,前端工程师总是用他们的奇淫技巧来防止我们采集数据,而我们也在不断地反爬中进步,css偏移就是前端工程师常用于数字(ps:价格、评论数等)的一种反爬手段,之后还会讲到字体反爬–SVG映射。

​ css有三种基本的布局机制:普通流、浮动和绝对定位。css利用定位可以准确地定义元素框相对于其正常位置应该出现的位置,或者相对于父元素、另一个元素甚至浏览器窗口本身的位置。但元素究竟如何定位,定位到什么位置,主要依靠top/right/bottom/left这四个偏移属性也就是我们常说的上下左右来敲定。

​ 了解到css是作用,那么前端工程师是如何利用css来进行反爬呢?直接看图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r0TwH421-1618643382081)(https://blog.jiangtian1217.cn/Image/ArticleImg/QQ截图20201228211425.png)]

​ 如图所见浏览器显示的数字为182,而Html源码中缺包含了8、1、3、2四个数字,如果说这个让你不解为什么Html源码中有四个数字而浏览器缺只显示了三个数字呢?没有发现什么明显的特征我们看下一个div下的情况:

镀金天空-CSS偏移_第2张图片

​ 在这个div中一共包含三个数字:6、3、8三个数字,按顺序应该显示为638,而浏览器中显示的却是386,这是怎么回事呢??这就是css偏移了,前端工程师利用css代码和class将包含数字的div的顺序打乱了,本应显示为638的被偏移为386了。那么我们该如何获取浏览器所显示的数字呢?

​ 这很好理解,我们将div的class所对应的偏移(top/right/bottom/left)想象为一个字典,key为class,偏移量(offset)为value。事实上我也是如此对付css偏移反爬的。那么浏览器最后的显示结果又是如何呈现的呢?我们不妨再大胆一点,将通过css偏移显示的结果想象为一个数组,当传入一个数字和它所对应的class我们就将class传入字典中获取该数字的偏移量,将其写入列表中偏移后的的位置。

​ 选择这种解决方法之后会很容易的想到几个问题:

  1. 如果有两个数字经过class的偏移之后定位到了相同的index该如何处理呢?当然是不会出现这样的问题啦,如果出现覆盖情况两个数字会叠在一起分辨不清的,为了有更直观的体验修改了Html源码使两个字体出现重叠。效果就像下图一样:
    镀金天空-CSS偏移_第3张图片
  2. 数字的位数可以确定吗?确定是固定只有三位数还是四位数?答案是不确定的,这个我们得通过div的大小和字体的大小来判断,如下图所示,一个div的大小为61.5 * 23,而一个字体是1em,浏览器默认1em大小为16px,所以我们可以确定一个div最多容纳3.8个数字,为了显示效果需要舍弃小数,因此基本可以确定一个div最多显示三位数。镀金天空-CSS偏移_第4张图片
  3. 有没有特殊情况呢?有的,除了偏移之外还有一个特别的关键字before:{content:“182”}。当出现这个content属性时,浏览器会将182覆盖写入到这个div中。如下图的182覆盖了1:
    镀金天空-CSS偏移_第5张图片

​ 既然理清了思路那么接下来就是将思路转换为代码了~~

代码

import re

from lxml import etree

import useragentutil
from glidedskyLogin import login


def leftDict(cssText):
    dictLetf = {}
    for text in cssText:
        leftText = re.findall(r'\.(.*)\{(.*):(.*)\}', text.replace(' ', ''))
        if leftText != []:
            outkey = leftText[0][0].replace(':before', '')
            inkey = leftText[0][1]
            invalue = leftText[0][2]
            # print(leftText[0][1])
            if outkey not in dictLetf.keys():
                dictLetf[outkey] = {}
            dictLetf[outkey][inkey] = invalue

    # for var in dictLetf.items():
    #     print(var)
    return dictLetf


def getRealNum(numDivs, deviation):
    result = 0
    for divs in numDivs:
        # 初始化div索引位置
        numIndex = 1
        # 返回字典{'value':'偏移量'}列表
        resultList = []
        for div in divs.xpath('./div'):
            className = div.xpath('./@class')[0]
            textNum = ''.join(div.xpath('./text()'))
            numFlag = numIndex

            divDict = deviation.get(className)
            if divDict.get('content'):
                num1 = divDict.get('content')
                # print('content', num1)
            elif divDict.get('left'):
                num1 = divDict.get('left')
                # print('left', num1)
                numFlag += int(num1.replace('em', ''))
            elif divDict.get('margin-right'):
                num1 = divDict.get('margin-right')
                # print('right', num1)
                numFlag -= int(num1.replace('em', ''))
            else:
                pass

            if textNum == '': numFlag = int(eval(num1))
            resultList.append({'value': textNum
                                  , 'offset': numFlag})
            numIndex += 1
        #     字典列表按照offset排序
        resultList = sorted(resultList, key=lambda i: i['offset'])
        resultStr = ''
        for var in resultList[-3:]:
            value = var.get('value')
            offset = var.get('offset')

            if value == '':
                resultStr = str(offset)
                break
            resultStr += str(value)

        result += int(resultStr)
        # print(resultStr)
    return result


def code(request, url):
    req = request.get(url, headers=useragentutil.get_headers())
    tree = etree.HTML(req.text)
    cssText = ''.join(tree.xpath('/html/head/style[1]/text()')).split('\n')
    # 格式化CSS偏移量为字典
    deviation = leftDict(cssText)

    # 获取数字框
    tree = etree.HTML(req.text)
    numDivs = tree.xpath('//div[@class="col-md-1"]')

    result = getRealNum(numDivs, deviation)
    return result


if __name__ == '__main__':
    count = 0
    request = login()
    for page in range(1, 1001):
        targetUrl = f'http://glidedsky.com/level/web/crawler-css-puzzle-1?page={page}'
        result = code(request, targetUrl)
        print(result)
        count += result
    print(count)

你可能感兴趣的:(爬虫,镀金天空,python,爬虫)