您当前的位置:首页 > 计算机 > 编程开发 > Python

Python爬虫:深度、广度(多线程)爬取网页链接并控制层级

时间:08-23来源:作者:点击数:

在使用爬虫爬取多个页面时(比如爬取邮箱,手机号等),一般层级越高与我们原始目标数据之间准确率越低,所以很有必要控制爬取层级达到有效爬取

无论是深度还是广度爬取,都需要以下变量和方法

#链接的正则表达式,注意是在标签中的href属性里的才是真正的链接
PATTERN_URl = "<a.*href=\"(https?://.*?)[\"|\'].*"

#获取网页源代码,注意使用requests时访问https会有SSL验证,需要在get方法时关闭验证
def getHtml(url):
    res = requests.get(url,verify=False)
    text = res.text
    return text
#有时还是会有警告,可以采用以下方式禁用警告
#import urllib3
#urllib3.disable_warnings()

#获取指定页面中含有的url
def getPageUrl(url,html=None):
    if html == None:
        html = getHtml(url)
    uList = re.findall(PATTERN_URl, html)
    return uList

深度:从首页爬取到所有url中,进入到第一个url(第1级)中再爬取所有url,进入第2级中的第一个url中继续操作,所以应该通过递归控制层级。

#深度字典,{url:层级,...},用来去重
depthDict = {}

def getUrlsDeep(url,depth = 3):
    try:
        if(depthDict[url]>=depth):
           return

        #避免碰到了下载链接,可见下文
        # if 'download' in str(url):
        #     return
        
        #获取此页中的所有连接
        clist = getPageUrl(url)
        print("\t\t"*depthDict[url],"#%d:%s"%(depthDict[url],url))
        for c in clist:
            #判断深度字典有有无此键,达到去重目的
            if c not in depthDict:
                depthDict[c]=depthDict[url]+1
                getUrlsDeep(c)

    except Exception as e:
        pass

if __name__ == '__main__':
    startUrl = 'https://gsh.cdsy.xyz'
    #爬取页面设置有第0级
    depthDict[startUrl] = 0
    #一共爬取2级
    getUrlsDeep(startUrl,depth=3)

关于防止爬到下载链接从而一直在下载,对于html页面类型Content Type的内容为"text/html",可以获取页面消息头信息来实现。

由于requests模块中的headers属性是要get完响应页面才能获取到,也就是说要等下载完才能知道是否为下载链接。所以可以使用urllib.request来获取消息头,参考https://www.cdsy.xyz/computer/programme/Python/230823/cd45622.html

运行结果

...

广度:在首页中爬取所有url,然后进入第1级中的第1个url中爬取url,再进入第1级中第2个页面爬取url。可以将未爬取的页面存放在一个临时列表中,每爬取一个页面则出队,将爬取到的进队,所以是通过循环控制层级

#临时存放url的列表,作为待爬列表,通过队列FIFO特性遍历url
tmplist = []
#存放爬取到的所有url
urlList = []
#同为深度字典
depthDict = {}


def getUrls(depth):
    while (len(tmplist)>0):
        #从临时列表中移除并获取对首元素链接
        url = tmplist.pop(0)
        #添加到链接列表
        urlList.append(url)
        if depthDict[url] < depth:
            #获取子页面url列表 
            subList = getPageUrl(url)
            for u in subList:
                if u not in depthDict:
                    depthDict[u] = depthDict[url]+1
                    tmplist.append(u)

if __name__ == '__main__': 
    startUrl = 'https://gsh.cdsy.xyz'
    depthDict[startUrl] = 0
    tmplist.append(startUrl)
    getUrls(3)
    for url in urlList:
        print("\t"*depthDict[url],"#%d:%s"%(depthDict[url],url))

运行结果

···

...

分析两者方式运行结果,深度与广度之间的逻辑区别清晰可见:)

可以发现通过以上的单线程广度爬取速度很慢,可以改进成多进程的:

tmplist = []
depthDict = {}
urlList = []
#线程列表
tlist = []

#获取指定url中的所有url,并加入到待爬列表中,作为线程的target
def getSonPageUrl(url):
    subList = getPageUrl(url)
    for u in subList:
        if u not in depthDict:
            depthDict[u] = depthDict[url] + 1
            tmplist.append(u)

def getUrls(depth):
    #当还有待爬网页或者还有运行中的线程
    while ((len(tmplist)>0) or (threading.activeCount()>1)):
        while len(tmplist) == 0:
            continue
        url = tmplist.pop(0)
        urlList.append(url)
        #print(threading.activeCount(),"\t" * depthDict[url], "#%d:%s" % (depthDict[url], url))
        if depthDict[url] <depth:
            t = threading.Thread(target=getSonPageUrl,args=(url,))
            tlist.append(t)
            t.start()


startUrl = 'https://gsh.cdsy.xyz'
if __name__ == '__main__':
    depthDict[startUrl] = 0
    tmplist.append(startUrl)
    getUrls(3)
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门