Scrapy提供无序结果

aydmsdu9  于 2023-10-20  发布在  其他
关注(0)|答案(1)|浏览(156)

我正在构建一个python scraper,目前,需要多个URL并提取有关汽车广告的信息。代码看起来像这样:

import time
import csv
import scrapy
import json
from scrapy.loader import ItemLoader
from urllib.parse import urlencode, quote
 

class autovitSpider(scrapy.Spider):
    name = "autovit"
    start_urls = [
            url1,
            url2,
            url3,
            url4,
            url5,
        ]
        

    def parse(self, response):
        for list_ in response.css("div.offer-params"):
            for sublist in list_.css("ul.offer-params__list"):
                for elem in sublist.css("li.offer-params__item"):
                    if elem.css("span.offer-params__label::text").get() == "Anul" or elem.css("span.offer-params__label::text").get() == "Km" or elem.css("span.offer-params__label::text").get() == "Putere" or elem.css("span.offer-params__label::text").get() == "Capacitate cilindrica":
                        yield { 
                            "type":elem.css("span.offer-params__label::text").get(),
                            "value":elem.css("div.offer-params__value::text").get().replace("\n                ","").replace("         ","").replace("        ",""),
                        }
                    elif elem.css("span.offer-params__label::text").get() == "Marca" or elem.css("span.offer-params__label::text").get() == "Model" or elem.css("span.offer-params__label::text").get() == "Versiune" or elem.css("span.offer-params__label::text").get() == "Combustibil" or elem.css("span.offer-params__label::text").get() == "Culoare" or elem.css("span.offer-params__label::text").get() == "Cutie de viteze" or elem.css("span.offer-params__label::text").get() == "Numar de portiere":
                        yield { 
                            "type":elem.css("span.offer-params__label::text").get(),
                            "value":elem.css("a.offer-params__link::text").get().replace("\n                ","").replace("                ",""),
                        }
                    else:
                        pass

        yield {"url": response.request.url,
            "price": response.css("span.offer-price__number::text").get().replace("        ",""),
            
            }

我知道yield的是丑陋的,但这是我刮的网站。汽车的不同属性有不同的html div/a's/etc。
无论如何,我注意到以下问题:
对于多个链接,蜘蛛有时(一些运行以正确的顺序返回数据,另一些则不是)开始混淆它产生的值的顺序。我特别需要那个顺序,每个广告要有10行。有时它会将多个广告中的信息混在一起。我认为这是由异步请求引起的,因为蜘蛛加载多个URL,(几乎)同时开始抓取它们,并在发现感兴趣的内容时产生信息。现在,我通过设置CONCURRENT_REQUESTS = 1解决了这个问题,但这(显然)会使它变慢。有没有办法解决这个问题,而不限制蜘蛛在同一时间只有一个请求?比如为它抓取的每个URL生成一个单独的dict,或者跟踪dict中每个元素的来源?我知道我可以通过为字典中的每个条目创建一个基于URL的键,然后将具有相同键的条目聚集在一起来沿着它。但是,这再次增加了与排序相关的额外时间并降低了效率。
请记住,我对这些东西如何工作的技术方面没有太多的了解。我从概念上理解异步请求或yield命令是如何工作的,但我不知道它背后的实际机制。我甚至不知道Scrapy本身是如何工作的(创建所有这些文件,从shell处理它们,等等),因为我习惯于编写单个文件程序,这些程序在它们内部执行任务,而没有一个完整的运行过程。因此,一个过于技术性的答案可能没有帮助。
为了参考我的理解水平,我尝试在class autovitSpider之外创建一个for url in url_list:循环,它遍历一个url列表,并为每个url重新运行spider。它没有工作,并且只产生第一个URL的数据。还尝试在class autovitSpider内部循环并给予start_urls单独的URL。结果与第一种情况相同。
编辑:当使用多个并发请求时,也会有某种数据丢失:
INFO: Stored json feed (41 items) in: a.json
INFO: Stored json feed (31 items) in: a.json
concentive在不更改代码的情况下运行。

q7solyqu

q7solyqu1#

现在,我通过设置CONCURRENT_REQUESTS = 1解决了这个问题
我知道我可以通过为字典中的每个条目创建一个基于URL的键,然后将具有相同键的条目聚集在一起来沿着它。但是,这再次增加了与排序相关的额外时间并降低了效率。
你有没有试过这个基准?
我认为,将并发请求设置为1比在字典中对数据进行排序损失的处理时间要多得多。将并发请求设置为1基本上可以防止您从并行化的效果中受益,同时增加与并行化相关的所有问题。另一方面,字典的内存是那么大,除非你在烤面包机上运行,或者你正在报废一些非常大的东西,否则你应该没问题。最坏的情况是,你可以将结果转储到数据库中。
但是,由于您产生的是异步对象,如果您没有在某种键上匹配它们,那么您将无法正确地对它们进行排序。这就是异步操作的本质:你不知道什么时候会结束。
所以,简而言之:使用dict,你不会损失太多的时间和内存,但会使用更多的concurrent_requests。
当使用多个并发请求时,也会有某种数据丢失
大多数服务器都有针对大量PMAC请求的保护。
我不太了解你的问题,因为我缺少诸如你的用户代理或你正在报废的系统之类的信息,但scrappy的文档说“Scrapy/VERSION(+https:scrapy.org)”是默认的用户代理,因此服务器将识别你为机器人,并可能在某个时候限制请求。
如果您的请求最终失败(因为您被限制了),那么您可能不会得到所有结果。我在你的函数中没有看到try-except,所以基本上如果你失败了,你应该会收到一个错误,但是因为你的操作都是并行的,它会继续做其他的操作。
假设你有A-B-C操作,A=成功=产出你的东西,B=失败=没有产出,C=成功=再次产出。因此,如果你第一次被节流,一切都是成功的,那么json有3个条目,下次你有一个错误(因为服务器节流你),那么json有2个条目。
您应该尝试人为地减慢您的请求一点,以适应服务器的要求。如果你正在寻找的网站作为文档使用它来节流自己。否则就是反复试验。
希望这能帮上忙。

相关问题