Scrapy 提供无序结果

问题描述 投票:0回答:1

我正在构建一个 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
解决了这个问题,但这(显然)让它变得更慢。有什么办法可以解决这个问题,同时又不限制蜘蛛一次只能发出1个请求吗?比如为它抓取的每个 URL 生成一个单独的
dict
,或者跟踪
dict
中每个元素的来源?我知道我可以通过为字典中的每个条目创建一个基于 URL 的键,然后将具有相同键的项目聚集在一起来解决这个问题。但同样,这会增加与排序相关的额外时间并降低效率。

请记住,我对这些东西如何工作的技术方面了解不多。我从概念上理解异步请求或

yield
命令如何工作,但我不知道其背后的实际机制。我什至不知道 Scrapy 本身是如何工作的(创建所有这些文件,从 shell 处理它们等),因为我习惯于编写单个文件程序来执行它们内部的操作,而无需在它们背后有一个完整的运行过程。因此,过于技术性的答案可能没有帮助。

为了参考我的理解水平,我尝试在

for url in url_list:
之外创建一个
class autovitSpider
循环,该循环遍历 url 列表并为每个单独的 url 重新运行蜘蛛。它不起作用,并且仅生成第一个网址的数据。还尝试在
class autovitSpider
内循环并为
start_urls
提供单独的 url。与第一种情况相同的结果。

编辑:当使用多个并发请求时,也会出现某种数据丢失:

INFO: Stored json feed (41 items) in: a.json

INFO: Stored json feed (31 items) in: a.json

连续运行,没有更改代码。

python web-scraping scrapy
1个回答
0
投票

现在,我通过设置 CONCURRENT_REQUESTS = 1 解决了这个问题

我知道我可以通过为字典中的每个条目创建一个基于 URL 的键,然后将具有相同键的项目聚集在一起来解决这个问题。但同样,这会增加与排序相关的额外时间并降低效率。

您尝试过对此进行基准测试吗?

我认为将并发请求设置为 1 比在字典中对数据进行排序会损失更多的处理时间。将并发请求设置为 1 基本上可以防止您从并行化的效果中受益,同时增加与并行化相关的所有问题。另一方面,字典的内存很大,除非你在烤面包机上运行或者你正在废弃一些非常大的东西,否则应该没问题。最坏的情况是,您可以将结果转储到数据库中。

但是,由于您正在生成异步对象,如果您没有在某种键上匹配它们,您将无法对它们进行正确排序。这就是异步操作的本质:你不知道它什么时候结束。

因此,简而言之:使用字典,您不会损失太多时间或内存,但会使用更多并发请求。

当使用多个并发请求时,也会出现某种数据丢失

大多数服务器都针对大量异步请求提供保护。

我不太了解你的问题,因为我缺少诸如你的用户代理或你正在废弃的系统之类的信息,但 scrapy 的文档说“Scrapy/VERSION (+https://scrapy.org)”是默认值用户代理,因此服务器会将您识别为机器人,并可能在某个时候限制请求。

如果您的请求最终失败(因为您受到限制),那么您可能不会获得所有结果。我在你的函数中没有看到 try- except ,所以基本上如果你失败了,你应该收到一个错误,但由于你的操作都是并行的,它会继续执行其他操作。

假设你有操作A-B-C,A=成功=产生你的东西,B=失败=没有产生,C=成功=再次产生。因此,如果您第一次受到限制,一切都成功,因此 json 有 3 个条目,下次您遇到错误(因为服务器限制了您),因此 json 有 2 个条目。

您应该尝试人为地减慢请求速度以满足服务器的要求。如果您正在将网站视为文档,请使用它来限制自己。否则就是反复试验。

我希望这会有所帮助。

© www.soinside.com 2019 - 2024. All rights reserved.