看到群里讨论了一道爬虫题目,题目链接如下:
雪碧图爬虫

题目

题意

题意是爬取这一千页的数字并计算出这些数字的总和。

分析

先来分析一下页面的源码(第一页的源码可以通过附件下载).

源码分析

可以看出每个数字对应的 class 为至少七位的字符串,并且每次访问得到的值都不一样,从这里可以推断出,基本不可能通过抓取每个字符串的值去匹配每个数字。

再来看下CSS 图像:

CSS截图

格式化后成如下:

CSS代码格式化后

大概分为3部分,第一部分是ID对应切图的坐标,第二部分是高度及大小信息,第三部分是雪碧图原图。将雪碧图解码还原成图片如下:

base64转图片

解题

根据以上信息,这道题的思路就很清楚了。

数字分割

我们把图片里面的数字进行分割,切图偏移小于等于数字最左的像素值,现在只需要计算出每个数字左右两边的坐标即可,图像分割主要代码如下:

1
2
3
4
5
6
7
8
9
10
11
split_num = []
for x in range(0, width):
check_flag = False
for y in range(0, height):
pix_data = img.getpixel((x,y))
if not (pix_data[0] > 250 and pix_data[1] > 250 and pix_data[2] > 250):
check_flag = True
break
if is_sign ^ check_flag:
split_num.append(int(x))
is_sign = not is_sign

(这边白色区域判定使用 250 是因为防止有些图片色泽不清,可能引起误判,实际应该使用 255 )

测试数字边界识别结果如下:

数字边界识别结果

然后接下来测试一下第一页识别的数值对不对,主要代码如下:

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
image_data = BytesIO(base64.b64decode(image_base64))
img = Image.open(image_data)

width = img.size[0]
height = img.size[1]
is_sign = False
split_num = []
for x in range(0, width):
check_flag = False
for y in range(0, height):
pix_data = img.getpixel((x,y))
if not (pix_data[0] > 250 and pix_data[1] > 250 and pix_data[2] > 250):
check_flag = True
break
if is_sign ^ check_flag:
split_num.append(int(x))
is_sign = not is_sign
sum_num = 0
for block in block_arr:
rt_str = ''
for num_str in block:
for i in range(0, len(split_num)):
if int(self.position_dic[num_str]) < split_num[i]:
rt_str = rt_str + str(int(i/2))
break

运行结果:

第一页识别

接下来就是遍历一千页的事情了。题目出得还是比较简单的,要是图片中的数字是乱序,就很麻烦了。

附件

第一页源码

python源码