2017-03-28 16 views
1

私はScrapyで新しいです。私は数千のURL、xpathタプル、値をデータベースに持っています。 これらのURLは異なるドメインからのものです(必ずしも同じドメインから100個のURLが存在する可能性があります)。広いクロール - 異なるxpaths - Scrapy

x.com/a //h1 
y.com/a //div[@class='1'] 
z.com/a //div[@href='...'] 
x.com/b //h1 
x.com/c //h1 
... 

これらの値は、できるだけ早く2時間ごとに取得したいが、これらの値はどれもオーバーロードしないようにしたい。

これを行う方法を理解できません。

私の考え:

私はすべての異なるドメインに1つのスパイダーを作成することができますが、それはルールを解析し、一度にそれらを実行しています設定しました。

いいですか?

EDIT: 並行処理によってデータをデータベースに出力する方法がわかりません。

EDIT2:

私はこのような何かを行うことができます - すべてのドメインに対して新しいクモがあります。しかし、これは数千の異なるURLを持つことは不可能であり、それはxpathsです。

class WikiScraper(scrapy.Spider): 
    name = "wiki_headers" 

    def start_requests(self): 
     urls = [ 
      'https://en.wikipedia.org/wiki/Spider', 
      'https://en.wikipedia.org/wiki/Data_scraping', 
     ] 
     for url in urls: 
      yield scrapy.Request(url=url, callback=self.parse) 

    def parse(self, response): 
     hxs = HtmlXPathSelector(response) 
     header = hxs.select('//h1/text()').extract() 
     print header 
     filename = 'result.txt' 
     with open(filename, 'a') as f: 
      f.write(header[0]) 
     self.log('Saved file %s' % filename) 

class CraigslistScraper(scrapy.Spider): 
    name = "craigslist_headers" 

    def start_requests(self): 
     urls = [ 
      'https://columbusga.craigslist.org/act/6062657418.html', 
      'https://columbusga.craigslist.org/acc/6060297390.html', 
     ] 
     for url in urls: 
      yield scrapy.Request(url=url, callback=self.parse) 

    def parse(self, response): 
     hxs = HtmlXPathSelector(response) 
     header = hxs.select('//span[@id="titletextonly"]/text()').extract() 
     filename = 'result.txt' 
     with open(filename, 'a') as f: 
      f.write(header[0]) 
     self.log('Saved file %s' % filename) 

答えて

0

あなたはそれがオプションであることを覚えている、なぜならallowed_domainsパラメータの程度scrapyは一度に複数のドメインを扱うことができない考えている場合。

スパイダーにallowed_domainsパラメータが設定されていない場合は、取得するすべてのドメインで動作します。

+0

はい、しかし、異なるドメインを持つすべてのURLは、データを取得するために異なるxpathを持っています。 –

+0

mmmmドメインごとに構成可能なxpathを使用することに問題はないので、あなたが考えているスパイダーコードを共有する方が良いと思います。 – eLRuLL

+0

質問の最後にコードを追加しました。私はScrapyで非常に新しいです。実際、私は今はマルチスレッドとlxmlを使用していますが、スロットルやその他の利点のために、Scrapyを使用したいと考えています。 –

-1

私は正しくドメインのマップをxpath値に持っていて、クロールするドメインに応じてxpathを引っ張りたいのですか?
のような何か試してみてください:あなたはEDIT2に投稿された例から

DOMAIN_DATA = [('domain.com', '//div')] 
def get_domain(url): 
    for domain, xpath in DOMAIN_DATA: 
     if domain in url: 
      return xp 


def parse(self, response): 
    xpath = get_domain(response.url) 
    if not xpath: 
     logging.error('no xpath for url: {}; unknown domain'.format(response.url)) 
     return 
    item = dict() 
    item['some_field'] = repsonse.xpath(xpath).extract() 
    yield item 
1

をすべてのクラスが1つの以上のレベルで簡単に抽出可能ですのように、それが見えます。これはどう:?

from urllib.parse import urlparse 

class GenericScraper(scrapy.Spider): 
    def __init__(self, urls, xpath): 
     super().__init__() 
     self.name = self._create_scraper_name_from_url(urls[0]) 
     self.urls = urls 
     self.xpath = xpath 

    def _create_scraper_name_from_url(url): 
     '''Generate scraper name from url 
      www.example.com/foobar/bar -> www_example_com''' 
     netloc = urlparse(url).netloc 
     return netloc.replace('.','_') 

    def start_requests(self): 
     for url in self.urls: 
      yield scrapy.Request(url=url, callback=self.parse) 

    def parse(self, response): 
     hxs = HtmlXPathSelector(response) 
     header = hxs.select(self.xpath).extract() 
     filename = 'result.txt' 
     with open(filename, 'a') as f: 
      f.write(header[0]) 
     self.log('Saved file %s' % filename) 

次に、あなたはグループのXPath

for urls, xpath in grouped_data: 
    scraper = GenericScraper(urls, xpath) 
    # do whatever you need with scraper 

ADの並行性により、データベースからのデータができます 関連:concurentを処理する必要があり、データベースはので、私は

編集が問題が表示されていない書き込みタイムアウトに:私はボンネットの下でどのように動作しているのか分かりません。つまり、何らかのパラレル化を使用していて、バックグラウンドで非同期に実行されているかどうかはわかりません。しかし、あなたが書いたことから、私はそう思っています。そして、あなたが1kスクレーパーを発射したときに、ハードウェアがそれほど多くのトラフィックを処理できない時に、複数の要求を発射したとき(免責事項、これは単なる推測です!

これを行うには、ネイティブな方法があるかもしれませんが、可能な回避策は+ multiprocessingを使用するキューです:

from multiprocessing import JoinableQueue, Process 
NUMBER_OF_CPU = 4 # change this to your number. 
SENTINEL = None 


class Worker(Process): 
    def __init__(self, queue): 
     super().__init__() 
     self.queue = queue 
    def run(self): 
     # blocking wait !You have to use sentinels if you use blocking waits! 
     item = self.queue.get(): 
     if item is SENTINEL: 
      # we got sentinel, there are no more scrapers to process 
      self.queue.task_done() 
      return 
     else: 
      # item is scraper, run it 
      item.run_spider() # or however you run your scrapers 
      # This assumes that each scraper is **not** running in background! 

      # Tell the JoinableQueue we have processed one more item 
      # In the main thread the queue.join() waits untill for 
      # each item taken from queue a queue.task_done() is called 
      self.queue.task_done() 


def run(): 
    queue = JoinableQueue() 
    # if putting that many things in the queue gets slow (I imagine 
    # it can) You can fire up a separate Thread/Process to fill the 
    # queue in the background while workers are already consuming it. 
    for urls, xpath in grouped_data: 
     scraper = GenericScraper(urls, xpath) 
     queue.put(scraper) 
    for sentinel in range(NUMBER_OF_CPU): 
     # None or sentinel of your choice to tell the workers there are 
     # no more scrapers to process 
     queue.put(SENTINEL) 
    workers = [] 
    for _ in range(NUMBER_OF_CPU): 
     worker = Worker(queue) 
     workers.append(worker) 
     worker.start() 

    # We have to wait until the queue is processed 
    queue.join() 

しかし、これは完全にScrapy能力を無視してparalell実行のためのバニラアプローチがあることを心に留めてください。 。私はThis blogpostが同じことを達成するためにtwistedを使用していることがわかりました。しかし、私はツイストを使用していないので、私はそのことについてコメントすることはできません

+0

Pocin、この答えをありがとう、それはそうです問題は1つあります。一度にすべてのスパイダーを実行すると、すべてのすべての応答がタイムアウトエラーです。たとえば、私がURLのために行う場合、xpathはgrouped_data [:50]:で正しく動作します。しかし、残念ながら、私は(50項目)の塊をかき集める方法を見つけませんでした。この質問の詳細:http://stackoverflow.com/questions/43156920/process-start-in-for-loop-scrapy-twisted –

+0

@MilanoSlesarikリンクが壊れています。私はいくつかの詳細を提供するために私の答えを編集しました –

関連する問題