本文主要是介绍爬取一个超大级规划图的历程,嗯,有点坑。Python实现,低配电脑多线程方案。,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
三天前,在一个Python技术交流群里面,一个朋友发出了一条消息,说爬取一个网站的放大级别最大的图,爬取下来。而且是有偿,当时我就想,这个地图我应该可以试试,然后点击网址(当时把网址也一起发出来了)然后放大到最大,看了一下,感觉不会太好爬取,然后我就问跟他私聊了一下,想试一下,如果有比我速度更快的请跟我说,我先分析一下,看看多久能爬去完。跟他说了以后,就开始分析网页,然后一点一点的抓取网页内的各种资源信息,一点一点的分析看看哪一些是我需要的资源。
我的分析思路是这样的:
点进网址看到的是这样的,分析网页元素:
现在需要我做的就是将地图色块放大到最大,然后将整张图片下载下来。
然后我就看,这个网页的元素不算太多,会不会是用js控制div显示的图片呢?然后我继续分析网页,看到有一个iframe标签,,将鼠标放在上面好,正好在网页中是地图的区域,果断用链接进去,看到的网页是这样的。
这一下把好多没用的网页标签元素都去掉了,让人眼前一清晰,然后继续上面那一步,开始分析网页元素,用浏览器的Select an Element in the Page (快捷键ctrl+shift+C)然后定位到一个svg标签,这不是定义用于网络的基于矢量的图形的标签吗?果断进去看每个标签分析,然后继续用select an element 里面的但是,进去后,啥也没有啊,
而且根据里面的分析,还是一个hidden的svs 下面的g标签也是“display:block”的,很明显没显示啊,估计是用来故意防止反爬的吧,但是看到图片就在眼前不可能没有数据接口啊。所以我继续努力分析,
点开network以后发现里面又好多png的图片啊,
这应该是我所想要的图片了吧,我将图片打开,点开之后是一个下载地址,嗯,果断下载下来看一下,还真让我找到了,还真是,不过是缩小到最小的图的一部分,我然后将他放大以后再试试,放大以后,继续点开图片,下载,是我想要的图,但是,下载下来不是一整张图,而是一整张图的一小块。这就很尴尬了,放大到这么大,然后下载下来的图片只有这一小部分,这就有点尴尬了。虽然找到图了,但是对于一整张下载下来的话,得有多少张这样小的图呢?而且,从左上角开始分析,第一张图里面的并没有什么东西啊。有点小难度啊。
然后我开始跟那兄弟沟通,是不是只需要有色块的就可以,红线的就可以不需要了?经过一系列的沟通获取同意后,然后我开始测试,分析网站地址http://27.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/tile/1/1051/1463 后边的数保留,每次下载的图片都是最后数的数值为名称的png图片,我把数字简单修改以后,一样也可以下载,但是那么大的图,肯定不可能以前多张,是不是倒数二级目录也可以修改下载呢?这样我就下载了一下1050/1463 1051/1463 1050/1462 这三个地址所对应的图片,然后根据图片分析,倒数第二个目录代表y轴最后一级的目录代表的是x轴,所以,倒数第三个目录又是什么?我放大缩小之后查看network里面png所对应的地址,发现,当第三级有1-9个,当到了9的时候也就说明图片级别最大了。所以我将地图放大到9级然后,查看色块区域,这里我又返回到了element里面查看元素,既然又图片肯定应该有img标签元素或者是其他标签元素来将图片显示吧?经过我的努力分析还真让我给找到了。
就在svg标签上一层的div标签里面。既然人家需要有色块的区域内,那我就将这一块区域内上下左右的最靠边的四张图片的xy轴所对应的目录级别找到就可以了啊。将整个有色区域开始圈在一个矩形内。
找到后,好了,分析完成了,那我们就开始写代码吧。由于考虑到分析不全,怕上下左右找到的不是最靠边的,所以我就分别往外为扩展了一下,找到的坐标分别是上(187228,134402)下(187167,134620)左(187122,134455)右(187380,134539),我的天呢。这样一算,大概需要220*260张图片,大概57200张图片,根据测试下载下来的图片宽高像素都是256,这样一算,图片不小啊。
然后开始写代码,由于前面主要的分析思路已经完成了,现在主要就是访问图片地址获取图片就可以了。
好了,开始我的代码表演,直接上Request模块,都不需要用bs4或者是selenium模块的,因为直接获取的就是图片。
导入模块:
import threading
import time
from urllib import request
from urllib import error
import os
import socket
定义函数,ypx为Y轴。xpx为X轴。从上往下开始,横向下载,当没行x爬取完成后会y加一,然后继续爬取,当然了,是一行一行爬取的,所以我就需要每一行都要保存在一个文件加下面,就以y轴为名称,建立文件夹,保存在里面吧。
def mian(ypx,n): #url8 = '资源网址/tile/8/'headers = {"Referer": "资源网址/map.html","User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"}path = "I:\map"xpx = 187121 #121y = ypx+3socket.setdefaulttimeout(30)while True:num = 0while True:try:if xpx < 187382:url = url8 + str(ypx) + "/" + str(xpx)print(n,"线程的下载",url)try:req = request.Request(url, headers=headers)html = request.urlopen(req)savePng(mkdirAndReturnNmae(path + "\\" + str(ypx), xpx), html.read())html.close()xpx += 1time.sleep(1)num = 1except ConnectionResetError as rese:print(n,rese)xpx -= 1time.sleep(5)print(n,"————————————————————————————————————————等待五秒。。。。")continueexcept socket.timeout as stout:print(n,stout)xpx -= 1time.sleep(5)print(n,"————————————————————————————————————————等待五秒。。。。")continueexcept TimeoutError as timeout:print(n, timeout)xpx -= 1time.sleep(5)print(n, "————————————————————————————————————————等待五秒。。。。")continueelse:xpx = 187121breakexcept error.HTTPError as e:print(n,"HttpError 跳过 ", e)time.sleep(0.5)xpx += 1print(n , "线程 ",xpx)# html.close()if num == 1:xpx = 187121break# continueypx += 1if ypx >= y:print(n,"线程 即将结束 ",ypx)print(n,".。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。请求结束。。。。。")exit(0)time.sleep(1)
嗯,因为考虑到服务器端那边可能会防止爬取,所以,每次获取一次图片,都会sleep(1),因为这么高强度,持续性的获取图片,服务器可能会认为是恶意攻击,所以,会有ConnectionResetError的错误(就是服务器强制断开链接),避免出现这个错误,利用try except 避免,except执行的函数,当出现这个错误的时候,会x轴返回一个,然后,sleep(5),,continue继续执行,而且,在服务器的reponse的返回后,获取完了图片要close()一下。这样可以在一定程度上避免服务器强制断开链接。在运行的时候,多次遇到TimeoutError,所以我导入了socket模块,设置一下等待时间socket.setdefaulttimeout(30),好像是我设置位置的问题,并没有有效避免这个问题,经常被except捕获到TimeoutError。所以在时间上,有很大程度的限制。嗯,避免这些问题以后,就开始爬取了,但是我的电脑配置有点底呀,而且,自己也没有服务器,分布式爬虫运用的现在也不太好,我就考虑运用多线程爬取。不过效果还行,如果是单线程话,五万多张近六万张图片每个sleep(1)一下,排除其他因素对时间的影响,还需要16个小时呢。如果多开几个线程的话,应该能够可以有效的节约一定的时间吧。所以,导入threading模块。
自定义线程类,继承thread类:
class MyThread(threading.Thread):"""属性:target: 传入外部函数, 用户线程调用args: 函数参数"""def __init__(self, target, args,krgs):super(MyThread, self).__init__() # 调用父类的构造函数self.target = targetself.args = argsself.krgs= krgsdef run(self):self.target(self.args,self.krgs)
由于不需要太多的其他处理,所以简单定义一下。
下面是定义多个线程,开始线程的函数:
def main():mh1 = MyThread(mian,134599,1) #574 开始+10mh2 = MyThread(mian,134602,2) #584mh1.start()mh2.start()k=time.time()
好了,下面的代码是创建目录保存图片data的函数,自行了解就好。
def mkdirAndReturnNmae(path, idname): # 创建保存目录并返回保存路径及名称if os.path.isdir(path):name = os.path.join(path, str(idname) + ".png")return nameelse:os.mkdir(path)name = os.path.join(path, str(idname) + ".png")return name
保存data
def savePng(name, data):file = open(name, 'wb')file.write(data)# print(name)file.close()
经过多层分析,以及处理,用我的小电脑用了三个多小时爬取下来了这些图片。
可是又有一个问题,需要我处理,我的电脑配置有点低,我应该怎么给他合成呢?有点难度,但是,通过查看前辈们的博客,还是找到了处理方法。下一篇文章继续写。爬取这一块已经完成。如果有更好的处理方法的话,还请大佬们多多赐教。小弟在这里提前感谢一下。
这篇关于爬取一个超大级规划图的历程,嗯,有点坑。Python实现,低配电脑多线程方案。的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!