爬取一个超大级规划图的历程,嗯,有点坑。Python实现,低配电脑多线程方案。

本文详细记录了从分析网页元素到实现地图色块图片爬取的全过程,包括解决反爬措施、多线程下载技术及异常处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

         三天前,在一个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 #121
    y = ypx+3
    socket.setdefaulttimeout(30)
    while True:
        num = 0
        while 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 += 1
                        time.sleep(1)
                        num = 1
                    except ConnectionResetError  as  rese:
                        print(n,rese)
                        xpx -= 1
                        time.sleep(5)
                        print(n,"————————————————————————————————————————等待五秒。。。。")
                        continue
                    except socket.timeout as  stout:
                        print(n,stout)
                        xpx -= 1
                        time.sleep(5)
                        print(n,"————————————————————————————————————————等待五秒。。。。")
                        continue
                    except TimeoutError as  timeout:
                        print(n, timeout)
                        xpx -= 1
                        time.sleep(5)
                        print(n, "————————————————————————————————————————等待五秒。。。。")
                        continue


                else:
                    xpx = 187121
                    break
            except error.HTTPError as e:
                print(n,"HttpError 跳过 ", e)
                time.sleep(0.5)
                xpx += 1
                print(n , "线程    ",xpx)
                # html.close()
                if num == 1:
                    xpx = 187121
                    break
                # continue
        ypx += 1
        if 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 = target
        self.args = args
        self.krgs= krgs

    def run(self):
        self.target(self.args,self.krgs)

由于不需要太多的其他处理,所以简单定义一下。

下面是定义多个线程,开始线程的函数:
 

def main():
    mh1 = MyThread(mian,134599,1)  #574 开始+10
    mh2 = MyThread(mian,134602,2)  #584
    mh1.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 name
    else:
        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()


经过多层分析,以及处理,用我的小电脑用了三个多小时爬取下来了这些图片。

 

可是又有一个问题,需要我处理,我的电脑配置有点低,我应该怎么给他合成呢?有点难度,但是,通过查看前辈们的博客,还是找到了处理方法。下一篇文章继续写。爬取这一块已经完成。如果有更好的处理方法的话,还请大佬们多多赐教。小弟在这里提前感谢一下。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值