鱼C论坛

 找回密码
 立即注册
查看: 2691|回复: 1

python多线程爬虫

[复制链接]
发表于 2017-9-17 01:41:54 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x

新人刚学爬虫 练手了一下
win10 64位  python3
大致内容:爬了很多网页,,每个网页上有一系列图片
每个网页对应地创建了一个文件夹
以文件夹:图片url的形式 拿字典保存了下来
全部创建了子线程  6000多个
用BoundedSemaphore设置了最大线程数
问题在于
1  运行后所有图爬完了 主线程里print(end-start)也显示了运行时间 程序最后不知道为什么不退出
QQ图片20170917013824.png

2 如果最后改为for t in threads: t.join() 不设置timeout参数的话 图片爬完 不会显示最后的print(end-start) 一样的会卡住

与上图一样 只是最后没有运行时间

3 还有个小问题就是途中偶尔会有几个线程 遇到[Errno 10054] 远程主机强迫关闭了一个现有的连接 是被网站认定为了爬虫吗 有什么好的伪装办法吗

4 有没有更好的多线程的写法

最后感谢各位大佬


代码:


  1. import re
  2. import urllib.request
  3. import os
  4. import urllib.parse
  5. import json
  6. import threading
  7. import time

  8. class downloader(threading.Thread):
  9.         def __init__(self,img_url,file_name,semlock):
  10.                 threading.Thread.__init__(self)
  11.                 self.img_url=img_url
  12.                 self.file_name=file_name
  13.                 self.semlock=semlock
  14.         def run(self):
  15.                 self.semlock.acquire()
  16.                 auto_down(self.img_url,self.file_name)               
  17.                 self.semlock.release()


  18. def auto_down(url,file_name):      #递归urlretrieve下载图片至指定文件夹
  19.         try:
  20.                 print('正在下载%s到%s'%(url,file_name))
  21.                 urllib.request.urlretrieve(url,file_name)
  22.         except urllib.error.ContentTooShortError:
  23.                 print('Network conditions is not good.Reloading.')
  24.                 auto_down(url,file_name)                       


  25. def get_img_url(html):            #根据每个网址获取图片url
  26.         html=html.decode('utf-8')
  27.         imglist_before=re.findall(r'<img[\s\S]*?src="([^"]+\.jpg|[^"]+\.gif)"',html)
  28.         imglist_final=[]
  29.         for each in imglist_before:
  30.                 if re.search(r'.*\d+',each.split('/')[2]):
  31.                         imglist_final.append(each)
  32.         return imglist_final
  33.        

  34.                                
  35.                        
  36. def get_file_name(html):              #根据每个网址获取标题
  37.         html=html.decode('utf-8')
  38.         title_name=re.findall(r'<title>(.+) - AcFun',html)
  39.         return title_name[0]

  40.        
  41. def make_dir(title_name):      #根据标题创建文件夹
  42.         file_name='C:\\Users\\sj\\Desktop\\123\\Cv\\'+title_name
  43.         if not os.path.exists(file_name):
  44.                 os.mkdir(file_name)
  45.                  
  46.                  
  47.                  
  48. def get_url(url,page):                  #获取所有页上的网址列表
  49.        
  50.         data={}
  51.         data['specialId']=5003739
  52.         data['pageNo']=page
  53.         data['pageSize']=18
  54.         data=urllib.parse.urlencode(data).encode("utf-8")
  55.        
  56.         req=urllib.request.Request(url,data)
  57.         req.add_header('User-Agent','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36')
  58.         response=urllib.request.urlopen(req)
  59.         html=response.read()
  60.         html=html.decode('utf-8')
  61.         target=json.loads(html)
  62.         url_page=[]
  63.         for i in target['specialContents']:
  64.                 url_page.append("http://www.acfun.cn/a/ac"+i['id']+"#album=5003739,0,"+i['sort']+",64")
  65.         return url_page
  66.                

  67. def url_open(url):        
  68.         req=urllib.request.Request(url)
  69.         req.add_header('User-Agent','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36')
  70.         response=urllib.request.urlopen(req)
  71.         html=response.read()
  72.        
  73.         return html
  74.                        
  75. if __name__=="__main__":
  76.         start = time.clock()
  77.        
  78.         if not os.path.exists("C:\\Users\\sj\\Desktop\\123\\Cv"):
  79.                 os.mkdir('C:\\Users\\sj\\Desktop\\123\\Cv')
  80.         os.chdir('C:\\Users\\sj\\Desktop\\123\\Cv')
  81.        
  82.         url="http://www.acfun.cn/member/special/getSpecialContentPageBySpecial.aspx"
  83.         url_page=[]
  84.         threads=[]
  85.         dict={}
  86.         maxconnections = 30
  87.         semlock = threading.BoundedSemaphore(maxconnections)
  88.        
  89.         for page in range(1,6):
  90.                 url_page=get_url(url,page)                       
  91.                 for i in url_page:                       
  92.                         html=(url_open(i))
  93.                         title_name=get_file_name(html)
  94.                         make_dir(title_name)
  95.                         dict[title_name]=get_img_url(html)      #字典:{某文件夹:对应图片url}
  96.                                
  97.         for file_name,img_url in dict.items():
  98.                 for each in img_url:
  99.                        
  100.                         path='C:\\Users\\sj\\Desktop\\123\\Cv\\'+file_name+'\\'+each.split('/')[-1]
  101.                        
  102.                         t=downloader(each,path,semlock)
  103.                         threads.append(t)
  104.                        
  105.         for t in threads:
  106.                 t.start()
  107.                
  108.        
  109.         for t in threads:
  110.                 t.join(20)
  111.                
  112.         end = time.clock()
  113.         print (end-start)
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2017-9-17 10:25:15 | 显示全部楼层
本帖最后由 wei_Y 于 2017-9-17 10:29 编辑

建议用线程池或异步方式改进。
线程池可简单使用concurrent库里面的ThreadPoolExecutor。
  1. with ThreadPoolExecutor(max_work=30) as t:
  2.     t.submit(requests.get, 'http://www.fishc.com')
复制代码



异步方式第三方可使用aiohttp,gevent等异步库实现。
如果是py3.5以上也可以自己封装一个异步http请求类。

异步例:

  1. import asyncio

  2. import requests

  3. class Requests(object):
  4.     def __init__(self):
  5.         self.headers = headers.copy()

  6.     @requestsExceptionFilter
  7.     def get(self, url, **kwargs):
  8.         if not kwargs.get('headers'):
  9.             kwargs['headers'] = self.headers
  10.         return requests.get(url, **kwargs)
  11.    
  12.     @requestsExceptionFilter
  13.     def post(self, url, **kwargs):
  14.         if not kwargs.get('headers'):
  15.             kwargs['headers'] = self.headers

  16.         return requests.post(url, **kwargs)  

  17. class ARequests(Requests):
  18.     """
  19.     一个异步请求类,
  20.     """
  21.     def __init__(self, callback):
  22.         super().__init__()
  23.         self.callback = callback

  24.     def __enter__(self):
  25.         return self

  26.     def __exit__(self, except_type, value, tb):
  27.         
  28.         return True

  29.     def _httpRequest(self, method, url, kwargs):
  30.         method = method.upper()
  31.         if method == 'GET':
  32.             data = super().get(url, **kwargs)
  33.         elif method == 'POST':
  34.             data = super().post(url, **kwargs)

  35.         return data

  36.     @asyncio.coroutine
  37.     def _get(self, url, **kwargs):
  38.         eventLoop = asyncio.get_event_loop()
  39.         future = eventLoop.run_in_executor(None, self._httpRequest, 'GET', url, kwargs)

  40.         data = yield from future

  41.         return data

  42.     @asyncio.coroutine
  43.     def _post(self, url, **kwargs):
  44.         eventLoop = asyncio.get_event_loop()
  45.         future = eventLoop.run_in_executor(None, self._httpRequest, 'POST', url, kwargs)

  46.         data = yield from future

  47.         return data

  48.     def get(self, url, **kwargs):
  49.         eventLoop = asyncio.get_event_loop()
  50.         future = eventLoop.create_task(self._get(url, **kwargs))
  51.         future.add_done_callback(self.callback)

  52.     def post(self, url, **kwargs):
  53.         eventLoop = asyncio.get_event_loop()
  54.         future = eventLoop.create_task(self._post(url, **kwargs))
  55.         future.add_done_callback(self.callback)


  56. if __name__ == '__main__':
  57.     import sys
  58.     eventLoop = asyncio.get_event_loop()
  59.    
  60.     urls = ['http://www.fishc.com']*5

  61.     def printData(future):
  62.         print(future.result())
  63.         # 回调函数,下面用的是run_forever,这里需要退出。
  64.         urls.pop()
  65.         if not urls:
  66.             sys.exit()

  67.     http = ARequests(printData)
  68.    
  69.     for i in range(5):
  70.         http.get(urls[i])

  71.     eventLoop.run_forever()
复制代码

  1. <Response [200]>
  2. <Response [200]>
  3. <Response [200]>
  4. <Response [200]>
  5. <Response [200]>
  6. [Finished in 0.6s]
复制代码


Request可以自己用urllib封装下。
异步原理:
http://python.jobbole.com/88291/
装饰器:
http://bbs.fishc.com/thread-77552-1-1.html
多线程:
http://bbs.fishc.com/thread-77760-1-1.html
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-12-23 23:37

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表