整理一下之前做过的两道Css偏移的题,顺便巩固一下前端的知识

GlidedSky Css反爬

题目:

从这里可以看到,各个小标签和显示的数字不是一一对应的,不过大标签的数量是对应的,都是12个,通过观察发现,里面混合了偏移、伪类、透明度等属性,我们一个一个来看:

伪类(::before)

这个标签里面又没任何内容,只有一个 ::before 的字样,这是个什么东西呢?

在css中,::before 是一个伪类元素,利用::before可以把需插入的内容插入到元素的其他内容之前,需要配合content属性来指定内容的值。传送门

所以只要找到 content 值就行了,直接拿 class值去搜索:

爬取的时候只要提取出 content 就行了

透明度(opacity)

有的标签只有两个数字,却有三个标签,比如上图的8透明度为0,在网页完全是不显示的,这是通过设置opacity(取值0-1)来实现的,爬取的时候需要把这些标签识别出来,然后剔除掉

偏移(left)

观察上图,按照标签地顺序,网页显示的值应该是 512 才对,但因为 left属性作了偏移,才显示为 152

看到leftwidth的单位都是 em这是一个相对的度量单位,对于浏览器来说,1em=16px,16px为浏览器的默认字体大小。

其中,5的left值为1em,1的left值为-1em,2没有left值,这就相当于

  • 5 向右移动一个单位 (以右下为正)
  • 1 向左移动一个单位
  • 2 保留原位置

所以就由 512 变成了 152,在代码方面,我们可以将各个数字的 初始位置 + 偏移量 算成一个数值 score,然后根据这个数值进行排序,得到真正显示在网页上的数字

代码整理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
from collections import defaultdict

import requests
from parsel import Selector

LOGIN_URL = "http://www.glidedsky.com/login"
LEVEL_URL = "http://www.glidedsky.com/level/web/crawler-css-puzzle-1"
EMAIL = ""
PASSWORD = ""


def glidedsky_login(em, pa):
"""
网站登录,才能看到题目
"""
session = requests.session()
resp1 = session.get(LOGIN_URL)
dom1 = Selector(resp1.text)
_token = dom1.css("meta[name='csrf-token']::attr(content)").get()
form_data = {
"_token": _token,
"email": em,
"password": pa,
}
session.post(LOGIN_URL, data=form_data)
return session


def get_real_nums(css_data, div_lst):
"""
根据CSS样式数据筛选数字
"""
real_nums = []
for col_md in div_lst:
number_score_list = []
for index, div in enumerate(col_md.css("div>div")):
name = div.attrib.get("class")
number = div.css("div::text").get()
_css = css_data.get(name)
if _css:
if _css.get("content"):
real_nums.append(int(_css.get("content").strip('"')))
break
elif _css.get("opacity"):
continue
else:
offset = int(_css.get("left").strip("em"))
score = index + offset
number_score_list.append((number, score))
else:
# 正常标签
number_score_list.append((number, index))

if number_score_list:
new_list = sorted(number_score_list, key=lambda x: x[1])
real_number = "".join((t[0] for t in new_list))
real_nums.append(real_number)

return real_nums


def parse_css(text):
"""
解析CSS文本
"""
css_data = defaultdict(dict)
lines = text.splitlines()
for li in lines:
name, _, con, _ = li.strip().split(" ")
p, v = con.split(":")
name = name.replace(":before", "").lstrip(".")
if p in ['content', 'left', 'opacity']:
css_data[name][p] = v
return dict(css_data)


def main():
session = glidedsky_login(EMAIL, PASSWORD)
for url in (LEVEL_URL + f'?page={index}' for index in range(1, 11)):
resp = session.get(url)
dom = Selector(resp.text)
css_text = dom.css("style::text").get().strip()
css_data = parse_css(css_text)
div_list = dom.css('div.col-md-1')
real_nums = get_real_nums(css_data, div_list)
print(real_nums)


if __name__ == '__main__':
main()

  • 用session登录的时候,要注意域名的变化,我写的时候没察觉到 www.glidedsky.comglidedsky.com ,卡了一下午 😭😭😭

  • 代码只获取了前十页的结果,要获取全部结果和计算总和需要读者自行补充后续逻辑

运行结果