2025年3月13日 星期四 甲辰(龙)年 月十二 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > Python

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

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

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

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

  • #链接的正则表达式,注意是在标签中的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)
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门