2017-05-22 63 views
0

我有一個像下面這樣的蜘蛛,它從API獲取信息,除此之外,我想下載base64格式的產品圖像,但爬行過程變得如此緩慢,怎麼可能我以另一種方式做到這一點,例如,使用線程?Scrapy:在另一個線程下載base64圖像

class ExampleMobilePhoneSpider(Spider): 
    name = "example" 
    allowed_domains = ["www.example.com", "example.com"] 
    start_urls = (
     'https://search.example.com/api/search/?category=c11&pageno=0', 
    ) 
custom_settings = { 
    "ITEM_PIPELINES": { 
     'crawler_bot.pipelines.ExampleElectronicDevicePipeline': 100, 
    } 
} 

def parse_item(self, response): 
    js = json.loads(response.body.decode('utf-8')) 
    hits = js['hits']['hits'] 
    for counter, hit in enumerate(hits): 
     l = ItemLoader(item=ProductDetail(), response=response) 
     m = hits[counter]['_source'] 

     # print(json.dumps(m, indent=4, sort_keys=True)) 
     l.add_value('enTitle', m['EnTitle']) 
     l.add_value('faTitle', m['FaTitle']) 
     l.add_value('minPrice', {"value": m['MinPrice'], "updateDate": datetime.datetime.now()}) 
     l.add_value('price', {"value": m['MinPriceList'], "updateDate": datetime.datetime.now()}) 
     l.add_value('maxPrice', {"value": m['MaxPrice'], "updateDate": datetime.datetime.now()}) 
     l.add_value('isActive', m['IsActive']) 
     l.add_value('isEspecialOffer', m['IsSpecialOffer']) 
     l.add_value('productCategories', m['ProductCategories'].split()) 
     l.add_value('imagePath', m['ImagePath']) 
     l.add_value('hasVideo', m['HasVideo']) 
     l.add_value('productColorList', m['ProductColorList']) 
     l.add_value('localID', m['Id']) 

     l.add_value('url', response.url) 
     l.add_value('project', "example") 
     l.add_value('subject', ["electronic_device", "mobile_phone", "mobile"]) 
     l.add_value('spider', self.name) 
     l.add_value('server', socket.gethostname()) 
     l.add_value('date', datetime.datetime.now()) 
     l.add_value('collection', "electronic_device") 

     file_path = "https://file.example.com/example/" 
     l.add_value('images', image2base64.get_as_base64(file_path + m['ImagePath'])) 

     yield l.load_item() 

def parse(self, response): 
    base_url_mobile = 'https://search.example.com/api/search/?category=c11&pageno=' 
    urls = [base_url_mobile + str(n) for n in range(2)] 
    for url in urls: 
     yield Request(urljoin(response.url, url), callback=self.parse_item) 
+0

您是否嘗試過使用下載器管道? – Kruser

+0

隨着管道我只是在MongoDB和Elasticsearch存儲數據 – altruistic

回答

0

幾種方法,下面有一個隊列和一堆工作線程的例子來做請求/響應處理和圖像下載。

from threading import Thread 
from Queue import Queue 


class ExampleMobilePhoneSpider(Spider): 

    def __init__(self, num_workers=10, q_size=100): 
     self.in_q = Queue(q_size) 
     self.worker_threads = [Thread(
            target=self.worker, args=(self.in_q,)) 
           for _ in range(num_workers)] 
     for _thread in self.worker_threads: 
      _thread.daemon = True 
      _thread.start() 

    def worker(self): 
     while True: 
      _url = self.in_q.get() 
      Request(_url, callback=self.parse_item) 

    def parse(self, response): 
     base_url_mobile = 'https://search.example.com/api/search/?category=c11&pageno=' 
     urls = [base_url_mobile + str(n) for n in range(2)] 
     for url in urls: 
      self.in_q.add(urljoin(response.url, url)) 
+0

你測試你的代碼?除了將線程與scrapy混合,這不是一個好主意,你的代碼看起來像它會啓動一些線程來創建大量永遠不會執行的scrapy Request對象... – rrschmidt

+0

OP代碼示例不完整,縮進如此否,不可能基於它測試任何代碼。你有什麼特別的問題嗎?你試過什麼了?有很多方法可以做到這一點,可以很容易地使線程目標函數成爲圖像下載,而不是在其中進行任何分析/ scrapy的使用。這取決於你想要的。無論哪種方式,你都需要展示一些工作代碼並根據這些問題提出問題。 – danny

1

與scrapy混合自己的線程通常是一個壞主意。此外,它並不是必需的,Scrapy使用Twisted的Reactor Pattern實現併發性,並且如果硬件的大小足夠大,可以同時處理數千個請求......並且settings.py中的設置允許它。

因爲我看不到你的圖像流水線implmentation,也沒有你的settings.py這是一個有點猜測,但你應該嘗試這兩個東西:

  1. 增加CONCURRENT_REQUESTS_PER_DOMAIN和CONCURRENT_REQUESTS設置,否則您的蜘蛛將只能同時下載有限數量的頁面和文件

  2. 檢查您的映像管道是否存在任何CPU密集型處理或阻塞行爲。