python - 使用 Scrapy 串行爬取域

标签 python web-crawler scrapy

我在数据库中有网站,我想连续抓取,即:

  1. 将域添加到允许的域列表(应该为空)。
  2. 将此域添加到请求中(添加 http:// 可以完美地工作)。
  3. 抓取有关允许域的请求。
  4. 前进到下一个域,只将它添加到允许的域列表中。它将是那里唯一的一个,因此不会发生交叉。注:criss-cross question没有帮助我,但也许我遗漏了什么......
  5. 抓取此请求。
  6. 依次完成我拥有的所有域。

到目前为止我所取得的成就是抓取域,它做得很好,它完美地抓取了请求。 我遇到的唯一问题是 allowed_domains 似乎没有更新,它会抓取各种网站。

我设置了 DEPTH_LIMIT=1 所以它不会是无限爬行而且我还添加了 DF 爬行而不是 BF 爬行:

DEPTH_LIMIT= 1
SCHEDULER_DISK_QUEUE = 'scrapy.squeues.PickleFifoDiskQueue'
SCHEDULER_MEMORY_QUEUE = 'scrapy.squeues.FifoMemoryQueue'

这是我的蜘蛛的代码(只是开始;因为你并不真正关心我的项目处理......):

from __future__ import with_statement
import scrapy
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor
from scrapy.http.request import Request
from company.items import CompanyItem, UrlItem
from urlparse import urlparse
import MySQLdb
import json


class RecipeSpider(CrawlSpider):
    name = "company"

    allowed_domains = []
    start_urls = []
    rules = (Rule(LinkExtractor(allow=(), deny=(), tags=('a', 'link', 'li' 'area')), callback='extract_raw_recipe', follow=True),)

    def start_requests(self):
        # print "before sources"
        sources = self.get_allowed_domains()
        dummy_tuple = (long(len(sources) + 1), u'dummy', None, None, None, None, None, 0L, long(len(sources) + 1),)
        final_sources = sources + (dummy_tuple,)

        for source in final_sources:
            self.allowed_domains = [str(source[1])]
            url_string = str("http://" + source[1])
            self.start_urls = [url_string]
            self.update_running_source(source)
            yield Request(url_string, self.extract_raw_recipe)
    ### start_requests()

    def extract_raw_recipe(self, response):
        pass

extract_raw_recipe 的代码没有请求或解析下一个 url,它可以正常工作,因此无需编辑。但是如果我需要在那里添加一些东西,请告诉我,因为也许这是缺失的链接。当前代码将字典添加到项目中,然后将此项目放入数据库中。

总而言之:我需要添加什么才能让它在每次抓取请求时过滤域?

如果需要提供更多代码,请告诉我。

最佳答案

我的建议是采用完全不同的方法。在需要爬网的域数据库中创建一个队列(基于您想要的任何标准),然后选择一个域进行爬网,并使用 allowed_domains 列表中的相关域初始化爬虫。蜘蛛完成后,重新开始下一个域的另一个爬网。重复直到队列中的所有域都完成。

这将使您能够更好地控制整个过程(例如,重新排队失败的爬网、取消有问题的爬网并继续前进而不丢失进度、一次爬网多个域而不会出现“串扰”等)如果您计划扩展它,它还允许您执行自定义设置(例如 USER_AGENT、DUPEFILTER、DOWNLOAD_DELAY)或基于每个域的自定义规则等操作,从而显着扩展蜘蛛的可用性。


如果这不是一个选项,您可以重置您的 allowed_domains 列表。这样做有一些问题,但首先是 Scrapy 的异地过滤的一些背景知识。

OffSiteMiddleware 负责编译基于allowed_domains 的允许域列表。它为此使用了一个正则表达式,它只在蜘蛛启动时编译一次(使用 spider_opened 信号)。更新变量 allowed_domains 不会对蜘蛛产生任何影响,除非您还强制 OffSideMiddleWare 重新编译其正则表达式。

以下方法(放置在您的蜘蛛中)应该可用于用新列表替换您的 allowed_domains:

from scrapy.spidermiddlewares.offsite import OffsiteMiddleware

def change_allowed_domains(self, allowed_domains):
    self.allowed_domains = allowed_domains

    for middleware in self.crawler.engine.scraper.spidermw.middlewares:
        if isinstance(middleware, OffsiteMiddleware):
            middleware.spider_opened(self)

这将重置 OffsiteMiddleware 使用的 domains_seen set(),所以如果您将它用于其他任何东西。

因此,花点时间了解所有这些,一个问题开始浮出水面:当您在 start_requests() 中对每个域进行排队时,您当前更改 allowed_domains 的方法将不起作用,因为 Spider 类仅跟踪一个 allowed_domains 正则表达式(根本不与请求/响应对象相关联)。如果在蜘蛛开始爬行之前将二十个请求排队到不同的域(每次都更改 allowed_domains 列表),它将使用最近编译的正则表达式(即 allowed_domains 对于最后排队的域)。

为了克服这个问题,您需要抓取一个域的所有请求,编写您自己的带有插槽的 OffsiteMiddleware,并让它处理所有过滤。或者您可以创建一个不同的域,仅在队列中的所有请求都完成并且下载器中的所有插槽都为空后才将下一个域添加到列表中(可能通过检查 self.crawler.engine.slot.inprogress 和`self.crawler.engine.slot.scheduler)。

祝你好运!

关于python - 使用 Scrapy 串行爬取域,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40377964/

相关文章:

python - Scrapy - 如何启动同一个蜘蛛进程的多个实例?

python feedparser ImportError : No module named feedparser

python - 如何在 python 中编写生成器、列表理解

python - 这两个正则表达式之间的区别

elasticsearch - 使用StormCrawler(和Elasticsearch)将字段添加到已爬网内容

python - 无法从网页中抓取类别标题

python - 我无法抓取网站的 div 参数(scrapy)

Python:查找包含列表的两个字典之间的区别

perl - 递归网络爬虫 perl

python - 如何在爬行期间为我的 Scrapy Spider 添加新请求