我正在为我的SPA应用程序编写一个爬虫。因为它是SPA,所以我不能使用wget/curl或任何其他基于非浏览器的解决方案来爬行,因为我需要一个浏览器才能在我的SPA中运行javascript。
我使用python和selenium编写了这个代码。它将从主页开始,扫描所有href元素,将它们保存在set中,丢弃我已经访问过的元素(visited和opened with selenium and collected all the href elements),然后从集合中获取下一个URL并访问它。然后它会一遍又一遍地重复这个过程,直到它访问了所有的链接。
代码如下所示:
def main():
    ...
    # Here we will be saving all the links that we can find in the DOM of
    # each visited URL
    collected = set()
    collected.add(crawler.start_url)
    # Here we will be saving all the URLs that we have already visited
    visited = set()
    base_netloc = urlparse(crawler.start_url).netloc
    while len(collected):
        url = collected.pop()
        urls = self.collect_urls(url)
        urls = [x for x in urls if x not in visited and urlparse(x).netloc == base_netloc]
        collected = collected.union(urls)
        visited.add(url)
    crawler.links = list(visited)
    crawler.save()
def collect_urls(self, url):
    browser = Browser()
    browser.fetch(url)
    urls = set()
    elements = browser.get_xpath_elements("//a[@href]")
    for element in elements:
        link = browser.get_element_attribute(element, "href")
        if link != url:
            urls.add(link)
    browser.stop()
    return urls我希望每个调用collect_urls都是一个芹菜任务,这样如果它失败了,它就可以重试一次,并且使整个过程更快(使用几个工作人员)。问题是,collect_urls是从while内部调用的,这取决于collected集,该集合由collect_urls的结果填充。
我知道我可以用delay()调用芹菜任务,然后用get()等待结果,所以我的代码如下所示:
    while len(collected):
        url = collected.pop()
        task = self.collect_urls.delay(url)
        urls = task.get(timeout=30)这将把我对collect_urls的调用转换为芹菜任务,如果某些事情失败了,我可以重新尝试,但是我仍然不能使用多个工作人员,因为我需要等待delay()的结果。
如何重构代码,使其允许我为collect_urls使用多个工作人员
发布于 2019-01-03 06:57:36
简单的回答,如果你想要为了速度的目的而分发,你必须使已经访问的网站集成为一个跨进程的安全结构。例如,您可以将其作为一个集合存储在redis或数据库表中。完成此操作后,您可以更新代码以执行以下操作:
# kick off initial set of tasks:
result_id = uuid.uuid4()
for x in collected:
    task = self.collect_urls.delay(x, result_id)
return result_id您可以使用该result_id定期检查访问的urls集。一旦该集合对于n调用数具有相同的长度,您就会认为它已经完成。
在collect_urls函数中,您实际上是这样做的:
def collect_urls(self, url, result_id):
    # for example, you can use redis smember to check if the 
    # set at result_id contains url
    if url has been visited:
        return
    # you can do this in redis using sadd
    add url to the set of visited
    # collect urls as before
    ...
    # but instead of returning the urls, you kick off new tasks
    for x in urls:
        collect_urls.delay(x, result_id)如果使用redis,所有收集/访问的urls都将包含在result_id标识的redis密钥中。您不必使用redis,您可以很容易地在数据库中的行中使用result_id作为一个列,而在另一个列中使用url。
https://stackoverflow.com/questions/54015327
复制相似问题