大幅提高爬虫效率 – 相似url识别

0x00 介绍

当我们在写爬虫的时候,我们会发现一个普遍事实: 有些url长得很像,比如'http://example.com/index.php?id=1', 'http://example.com/index.php?id=2'
其实这两个url或者网页几乎是一回事。只是查询的页面内容不一样而已。如果是一般的爬虫程序,假如id最多等于1000,那么爬虫就会从1一直爬取到1000,爬取每一个页面。其实我们知道,这种网页我们只需要爬一个就够了。
如果爬虫可以识别相似的url,那么就可以大大提高爬取的效率,比如上述例子,相比于爬取1000个页面,只需要爬取1个就可以达到目的,从而节省了大量的时间和资源。而且这种场景的出现是普遍存在的,所以具备识别相似url的能力几乎是这类爬虫必需的。
接下来,我将讲述如何实现这个看似复杂,其实简单的功能
本文讲述的爬虫功能是爬取某个站点中的所有url,所以和网页实质内容无关。

流程

识别url的大致流程如下
+ 给定一个目标url 和 一个url 列表
+ 将目标url和url列表中中的每一个都转化特征向量
+ 利用余弦相似度比较两个特征向量的相似度 最大为1,最小为0
+ 设定一个阈值
+ 大于阈值 认定为相似,否则认定为不相似

代码实现

dim = 75

'''
将url转化为特征向量后,计算url之间的余弦相似度
'''

def turn_num(url, length):
    '''
    转化为特征向量
    :param url: url
    :param length: 域名和协议的长度,我们只需要后面的部分,前面的部分每个url都一样,节省计算
    :return: url的特征向量
    '''
    url = url[length:]
    url = list("".join([j for j in url]))
    for i in range(0, len(url)):
        url[i] = ord(url[i])
    for i in range(len(url), dim):
        url.append(0)
    return url


def cos(vector1,vector2):
    '''
    余弦相似度计算
    :param vector1: url1
    :param vector2: url2
    :return: 相似度大小
    '''
    dot_product = 0.0
    normA = 0.0
    normB = 0.0
    for a,b in zip(vector1,vector2):
        dot_product += a*b
        normA += a**2
        normB += b**2
    if normA == 0.0 or normB==0.0:
        return None
    else:
        return dot_product / ((normA*normB)**0.5)


def similarities(data, url, length):
    '''
    将url与一组url比较相似度
    :param data: 数据集
    :param url: 目标url
    :param length: 长度
    :return: 判定结果
    '''
    url_list = [turn_num(i, length) for i in data]
    target_url = turn_num(url, length)
    for i in url_list:
        try:
            if cos(target_url, i) > 0.995:
                print('similar')
                return 1
        except:
            return 0
    return 0

细节: 变量dim – dimensionality 维度 旨在将每一个变量都转化为相同维度的特征变量,如果长度不够用0补齐。

实验后分析

优点:
+ 代码简洁直观,原理清晰简单
+ 具有很好的理解性

缺点:
+ 长度对结果影响很大
+ 只改变少些字母,且不改变长度,相似度仍然很高,出现误判

总结

上述实现了一个简单的相似url识别的功能
只是最简单的用余弦相似度比较两个特征向量的相似度

想法:如果在特征向量上下点功夫,采用更好的特征向量,应该可以取得更好的效果