基于python分布式爬虫并解决假死的问题

作者:持久决心 时间:2021-06-28 03:38:28 

python版本:3.5.4

系统:win10 x64

通过网页下载视频

方法一:使用urllib.retrieve函数

放函数只需要两个参数即可下载相应内容到本地,一个是网址,一个是保存位置


import urllib.request
url = 'http://xxx.com/xxx.mp4'
file = 'xxx.mp4'
urllib.request.retrieve(url, file)

但是博主在使用过程中发现,该函数没有timeout方法。使用时,可能由于网络问题导致假死!

方法二:使用urllib.request.urlopen函数

使用方法如下:


import urllib.request
url = 'http://xxx.com/xxx.mp4'
file = 'xxx.mp4'
response = urllib.request.urlopen(url, timeout=5)
data = response.read()
with open(file, 'wb') as video:
   video.write(data)

此函数有timeout设置,可以避免假死。

使程序并行化

伪代码如下:


import urllib.request
import socket
from urllib import error
from queue import Queue
from threading import Thread
import os
class DownloadWorker(Thread):  #定义一个类,继承自thread类,重写其run函数
   def __init__(self, queue):
       Thread.__init__(self)
       self.queue = queue     #标准的多线程实现方法都使用了queue
   def run(self):
       while True:
           link, file = self.queue.get() #从队列中获取一组网址以及对应的保存位置
           try: #使用try except方法进行各种异常处理
               response = urllib.request.urlopen(link, timeout=5)
               data = response.read()
               with open(file, 'wb') as video:
                   video.write(data)
           except error.HTTPError as err:
               print('HTTPerror, code: %s' % err.code)
           except error.URLError as err:
               print('URLerror, reason: %s' % err.reason)
           except socket.timeout:
               print('Time Out!')
           except:
               print('Unkown Error!')
           self.queue.task_done() #标记队列中的一个元素已经被处理
def main():
   queue = Queue() #定义队列
   for x in range(8): #开启8个线程
       worker = DownloadWorker(queue)
       worker.daemon = True
       worker.start()
   for lineData in txtData: #向队列中放入数据
       link = lineData[0]
       file = lineData[1]
       queue.put((link, file))
   queue.join() #等待队列中的数据被处理完毕
if __name__ == '__main__':
   main()

补充:基于python的一个大规模爬虫遇到的一些问题总结

前些天在某个论坛看到一些很感兴趣的信息,想要将其爬取下来,预估了下规模,想要做的是:将整个论坛的所有文章爬取下来,保存为本地的txt。

一开始写了个爬虫,大致思路是:

先从论坛的起始页开始爬起,得到所有分区版面的网址

然后从分区版面得到该区总共的页码数,根据网址规律得到分区版面所有页数的网页

从上面的分区版面的某一页的网页中得到该页所有文章的网址,然后抓取这些文章,保存为本地txt

上面的思路是典型的自上而下的思路,这样第一版本的代码就写好了。

下面进入正题,总结一下遇到的问题:

一、大规模爬虫被网站封禁的问题

上面的爬虫在调试阶段表现还是不错的,后来实测中,跑起来发现,跑了一段时间后就会发生http错误,由于使用的是有线网,且检查后不是网络本身的错误,所以判定为本网站封禁了,于是开始研究这个问题。

一般来说,python爬虫将自己伪装为浏览器时,使用的方法是在urllib2.Request函数中加入headers参数,也即类似于


"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1"

的user_agent代码片段,但是这样在大规模爬取中,就会被网站判定为一个用于长期快速访问,容易被封禁。本来在开始的代码中,爬虫访问两个网页之间是加入了0.5s的时间延时,就是为了防止这一问题的,结果还是不可以,而如果将延时加大,将会影响到爬虫的效率,而且如此大规模的爬取更是不知要何时才能结束。

于是,考虑伪装成多个浏览器的访问的方法来解决这一问题,具体做的就是,找许多user_agent,保存为一个列表,而在访问网页时,轮流使用以上user_agent,这样就伪装成了许多浏览器。附上具体子函数如下:


user_agent_list = [
       "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
       "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
       "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
       "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
       "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",

"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
       "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
       "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
       "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
       "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",

"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
       "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
       "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
       "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
       "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",

"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",
       "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
       "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
       'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.2; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0)',
       'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; Media Center PC 6.0; InfoPath.2; MS-RTC LM 8)',
       'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; InfoPath.2)',
       'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0 Zune 3.0)',
       'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; MS-RTC LM 8)',
       'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3)',
       'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; MS-RTC LM 8)',
       'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET CLR 4.0.20402; MS-RTC LM 8)',
       'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET CLR 1.1.4322; InfoPath.2)',
       'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729)',
       'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Win64; x64; Trident/4.0; .NET CLR 2.0.50727; SLCC2; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0)',
       'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Win64; x64; Trident/4.0; .NET CLR 2.0.50727; SLCC2; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET CLR 3.0.04506; Media Center PC 5.0; SLCC1)',
       'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Win64; x64; Trident/4.0; .NET CLR 2.0.50727; SLCC2; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0)',
       'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Win64; x64; Trident/4.0)',
       'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0; .NET CLR 3.0.04506; Media Center PC 5.0; SLCC1)',
       'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; FDM; Tablet PC 2.0; .NET CLR 4.0.20506; OfficeLiveConnector.1.4; OfficeLivePatch.1.3)',
       'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET CLR 3.0.04506; Media Center PC 5.0; SLCC1; Tablet PC 2.0)',
       'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET CLR 1.1.4322; InfoPath.2)',
       'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.3029; Media Center PC 6.0; Tablet PC 2.0)',
       'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2)',
       'Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 6.0)',
       'Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)',
       'Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1; Media Center PC 3.0; .NET CLR 1.0.3705; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.1)',
       'Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1; FDM; .NET CLR 1.1.4322)',
       'Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1; .NET CLR 1.1.4322; InfoPath.1; .NET CLR 2.0.50727)',
       'Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1; .NET CLR 1.1.4322; InfoPath.1)',
       'Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1; .NET CLR 1.1.4322; Alexa Toolbar; .NET CLR 2.0.50727)',
       'Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1; .NET CLR 1.1.4322; Alexa Toolbar)',
       'Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)',
       'Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.40607)',
       'Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1; .NET CLR 1.1.4322)',
       'Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1; .NET CLR 1.0.3705; Media Center PC 3.1; Alexa Toolbar; .NET CLR 1.1.4322; .NET CLR 2.0.50727)',
       'Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 6.0; en-US)',
       'Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 6.0; el-GR)',
       'Mozilla/5.0 (MSIE 7.0; Macintosh; U; SunOS; X11; gu; SV1; InfoPath.2; .NET CLR 3.0.04506.30; .NET CLR 3.0.04506.648)',
       'Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 6.0; WOW64; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; c .NET CLR 3.0.04506; .NET CLR 3.5.30707; InfoPath.1; el-GR)',
       'Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; c .NET CLR 3.0.04506; .NET CLR 3.5.30707; InfoPath.1; el-GR)',
       'Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 6.0; fr-FR)',
       'Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 6.0; en-US)',
       'Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 5.2; WOW64; .NET CLR 2.0.50727)',
       'Mozilla/4.79 [en] (compatible; MSIE 7.0; Windows NT 5.0; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 1.1.4322; .NET CLR 3.0.04506.30; .NET CLR 3.0.04506.648)',
       'Mozilla/4.0 (Windows; MSIE 7.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)',
       'Mozilla/4.0 (Mozilla/4.0; MSIE 7.0; Windows NT 5.1; FDM; SV1; .NET CLR 3.0.04506.30)',
       'Mozilla/4.0 (Mozilla/4.0; MSIE 7.0; Windows NT 5.1; FDM; SV1)',
       'Mozilla/4.0 (compatible;MSIE 7.0;Windows NT 6.0)',
       'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0)',
       'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0;)',
       'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; YPC 3.2.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; InfoPath.2; .NET CLR 3.5.30729; .NET CLR 3.0.30618)',
       'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; YPC 3.2.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506)',
       'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; WOW64; SLCC1; Media Center PC 5.0; .NET CLR 2.0.50727)',
       'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; WOW64; SLCC1; .NET CLR 3.0.04506)',
       'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; WOW64; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; InfoPath.2; .NET CLR 3.5.30729; .NET CLR 3.0.30618; .NET CLR 1.1.4322)',
      ]

上面大概有60多个user_agent,这样就伪装成了60多个浏览器。尝试这种方法后,发现在此长时间爬取,出错或者访问速度变慢的情况就很少出现了,基本解决了这一问题。

但是,需要注意的是,如果网站不是根据user_agent,而是根据用户的IP来封禁的话,那就不好办了,网上的一些解决办法是云计算之类的,貌似略麻烦,不太适合个人用户,有兴趣的可以看一下相关资料。

二、长时间无人值守网络不稳定的问题

由于规模略大,不可能一直守在电脑前,所以,代码的稳定性(容错性)需要较高,这里,python的try……except……语法就发挥了很好的作用。

前几天的实践证明,出错大多数是由于一时的网络不稳定而出现的,而解决办法也很简单,重新访问以下就好了,于是将抓取网页的函数写成下面的形式


def get_page_first(url):
   global user_agent_index
   user_agent_index+=1
   user_agent_index%=len(user_agent_list)
   user_agent =  user_agent_list[user_agent_index]
   #print user_agent
   print user_agent_index
   headers = { 'User-Agent' : user_agent }
   print u"正在抓取"+url
   req = urllib2.Request(url,headers = headers)
   try:
       response = urllib2.urlopen(req,timeout=30)
       page = response.read()
   except:
       response = urllib2.urlopen(req,timeout=30)
       page = response.read()
   print u"抓取网页"+url
   return page

这里,如果访问一个网页30s无响应,就重新访问。基本解决了这一问题。

三、保存为本地txt时名称出错的问题

由于txt的命名采用的是 “日期--作者——标题”的形式,而一些帖子的标题含有诸如?等txt中不允许出现的命名,这样就会发生报错。这里解决办法是,如果保存文件出错,则先尝试将名称改为“日期--作者——编号”形式,仍然出错,而保存为“日期--编号”的形式。具体代码如下:


try:
               if news_author[0]=='':
                   save_file(path+'//'+news_time[0]+'--'+news_title+'.txt',news)
               else:
                   save_file(path+'//'+news_time[0]+'--'+news_author[0]+u"——"+news_title+'.txt',news)
           except:
               try:
                   save_file(path+'//'+news_time[0]+'--'+news_title+'.txt',news)
               except:
                   save_file(path+'//'+news_time[0]+'--'+str(j)+'-'+str(index)+'.txt',news)

四、保存文件重名覆盖的问题

开始的代码考虑不周,没有想到同一天的帖子中会出现作者和名称都相同的情况,于是后来发现一些版面的总文章数和保存的txt数目不同,后来发现了这一问题。于是将保存文件的子函数修改如下,大致思路就是保存前先检查同名文件是否存在,不存在直接保存;存在的话,在名称后加(i)(i从1开始递增变化),重复上述步骤,直至同名文件不存在:


def save_file(path,inf):
   if not os.path.exists(path):
       f = file(path, 'w')
       f.write(inf)
       f.close
   else:
       i=0
       while(1):
           i+=1
           tpath=path[:-4]
           tpath+='('+str(i)+')'+'.txt'
           if not os.path.exists(tpath):
               break
       f = file(tpath, 'w')
       f.write(inf)
       f.close

五、多线程抓取以及无图版抓取 网页抓取速度的一些问题

理论上,大规模的爬虫可以采用多线程的方法加快抓取速度,但是考虑到不要对网站造成过大的压力,也为避免被网站封禁IP,所以主程序中未引入多线程的概念。但是又为了加快进度,就手动打开多个命令行窗口运行爬虫,来同时抓取不同的版面的文章。这样,当一个程序报错,其他的仍然能运行,也是增强了程序的容错性。

实际运行一段时间后,发现该程序的时间延迟最主要是发生在抓取网页的环节,也就是下载网页的时间上,想要提高效率也就是需要改善这一环节。当我正考虑应该采用什么办法解决这一问题时,忽然发现,原来该论坛还提供了无图版的网页(也就是类似于手机版),这样,每个网页的大小就减小了很多,而且文章内容之类所需信息仍然存在,所以就重新修改了代码。然后,发现速度确实有了极大的提升。所以,以后 抓取网页前一定要先看看是否存在类似于无图版(手机版)的网页,这样就可以很大的提高速度。

六、整体总结

经过后期一些完善,该代码基本上已经能够连续运行好几天而不出错了,稳定性基本解决了,而且没有漏抓的现象。经过大致十几天日夜兼程,终于抓完了。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。如有错误或未考虑完全的地方,望不吝赐教。

来源:https://blog.csdn.net/u013832707/article/details/81476609

标签:python,分布式,爬虫,假死
0
投稿

猜你喜欢

  • python类参数定义及数据扩展方式unsqueeze/expand

    2023-08-07 21:56:42
  • python实现复制文件到指定目录

    2022-09-25 20:53:12
  • TensorFlow实现卷积神经网络

    2022-05-21 02:17:02
  • Python判断两个list是否是父子集关系的实例

    2021-08-17 07:16:18
  • 用Python生成会跳舞的美女

    2023-04-04 11:52:09
  • javascript-表格排序(降序/反序)实现介绍(附图)

    2023-08-19 15:57:10
  • Go语言基于Socket编写服务器端与客户端通信的实例

    2023-07-12 08:44:11
  • js仿googl拖拽效果

    2007-09-29 13:16:00
  • python中文件操作与异常的处理图文详解

    2021-09-04 16:04:33
  • 详解python使用递归、尾递归、循环三种方式实现斐波那契数列

    2022-06-22 13:44:49
  • python中执行smtplib失败的处理方法

    2023-03-31 17:41:35
  • python文本进度条实例

    2023-05-15 17:18:16
  • 教你使用Pycharm配置远程Jupyter

    2023-02-09 19:43:06
  • Python后台管理员管理前台会员信息的讲解

    2023-11-06 09:59:41
  • Django项目之Elasticsearch搜索引擎的实例

    2022-02-26 17:30:28
  • Python模块汇总(常用第三方库)

    2023-05-21 16:25:37
  • JS实现简易图片轮播效果的方法

    2023-07-21 08:19:16
  • 浅谈一下python线程池简单应用

    2023-08-27 10:48:37
  • Python实现生成随机日期字符串的方法示例

    2023-10-27 11:51:37
  • 利用setuptools打包python程序的方法步骤

    2023-07-20 08:57:47
  • asp之家 网络编程 m.aspxhome.com