【问题标题】:Scrapy crawl saved links out of csv or arrayScrapy从csv或数组中抓取保存的链接
【发布时间】:2020-07-28 07:31:06
【问题描述】:
import scrapy

class LinkSpider(scrapy.Spider):
    name = "articlelink"
    allow_domains = ['topart-online.com']
    start_urls = ['https://www.topart-online.com/de/Blattzweige-Blatt-und-Bluetenzweige/l-KAT282?seg=1']
    
    BASE_URL = 'https://www.topart-online.com/de/'
#scraping cards of specific category
    def parse(self, response):
        card = response.xpath('//a[@class="clearfix productlink"]')
        for a in card:
            yield{
                'links': a.xpath('@href').get()
            }

        next_page_url = response.xpath('//a[@class="page-link"]/@href').extract_first()
        if next_page_url:
            next_page_url = response.urljoin(next_page_url)
            yield scrapy.Request(url=next_page_url, callback=self.parse)

这是我的蜘蛛,当我在我的服务器上运行“scrapy crawl articlelink -o filename.csv”时,它会抓取该类别的所有页面并将所有产品链接保存到一个 csv 文件中。 现在我必须抓取我的 csv 文件中的所有链接以获取特定信息,这些链接不包含在 productlink clearfix 的卡中 我怎么看?

【问题讨论】:

    标签: python-3.x web-scraping scrapy web-crawler


    【解决方案1】:

    所以我很高兴你去看看如何抓取下一页。

    现在关于产品编号,这是产生字典很麻烦的地方。在几乎大多数scrapy脚本中使用items字典会更好。它适用于能够从不同页面抓取正是想要做的元素。

    Scrapy 从 HTML 中提取数据,它建议这样做的机制是通过他们所谓的项目。现在,scrapy 接受了几种不同类型的方法来将数据放入某种形式的对象中。您可以使用 bog 标准字典,但对于需要修改的数据或除了来自网站的非常结构化的数据集之外几乎没有任何内容的数据,您至少应该使用项目。 Item 提供了一个类似对象的字典。

    要在我们的蜘蛛脚本中使用项目机制,我们必须实例化项目类来创建项目的对象。然后,我们用我们想要的数据填充该项目字典,然后在您的特殊情况下,我们跨函数共享此项目字典以继续从不同页面添加数据。

    除此之外,我们必须将项目字段名称声明为被​​调用。但它们是项目字典的键。我们通过位于项目文件夹中的 items.py 来执行此操作。

    代码示例

    项目.py

    import scrapy
    
    class TopartItem(scrapy.Item):
        
        title = scrapy.Field()
        links = scrapy.Field()
        ItemSKU = scrapy.Field()
        Delivery_Status = scrapy.Field()
        ItemEAN = scrapy.Field()
    

    蜘蛛脚本

    import scrapy
    from ..items import TopartItem
    
    class LinkSpider(scrapy.Spider):
        name = "link"
        allow_domains = ['topart-online.com']
        start_urls = ['https://www.topart-online.com/de/Blattzweige-Blatt-und-Bluetenzweige/l-KAT282?seg=1']
        custom_settings = {'FEED_EXPORT_FIELDS': ['title','links','ItemSKU','ItemEAN','Delivery_Status'] } 
        def parse(self, response):
            card = response.xpath('//a[@class="clearfix productlink"]')
            
            for a in card:
                items = TopartItem()
                link = a.xpath('@href')
                items['title'] = a.xpath('.//div[@class="sn_p01_desc h4 col-12 pl-0 pl-sm-3 pull-left"]/text()').get().strip()
                items['links'] = link.get()
                items['ItemSKU'] = a.xpath('.//span[@class="sn_p01_pno"]/text()').get().strip()
                items['Delivery_Status'] = a.xpath('.//div[@class="availabilitydeliverytime"]/text()').get().strip().replace('/','')
                yield response.follow(url=link.get(),callback=self.parse_item, meta={'items':items})
    
            last_pagination_link = response.xpath('//a[@class="page-link"]/@href')[-1].get()
            last_page_number = int(last_pagination_link.split('=')[-1])
            for i in range(2,last_page_number+1):
                url = f'https://www.topart-online.com/de/Blattzweige-Blatt-und-Bluetenzweige/l-KAT282?seg={i}'
                yield response.follow(url=url, callback=self.parse)
        
    
        def parse_item(self,response):
            items = response.meta['items']
            items['ItemEAN'] = response.xpath('//div[@class="productean"]/text()').get().strip()
            yield items
    

    解释

    首先在 items.py 中,我们创建了一个名为 TopArtItem 的类,因为我们继承自 scrapy.Item,我们可以为我们的项目对象实例化一个字段对象创建。我们要创建的任何字段都给它一个名称并通过scrapy.Field()创建字段对象

    在蜘蛛脚本中,我们必须将这个类TopArtItem 导入到我们的蜘蛛脚本中。 from ..items 是一个相对导入,这意味着我们要从蜘蛛脚本的父目录中从 items.py 中获取一些东西。

    现在这里的代码看起来有点熟悉。首先在我们使用 items = TopArtItem() 的 for 循环中创建名为 items 的特定项目的字典。

    添加到items字典的方式和其他python字典类似,items字典中的key就是我们在items.py中创建的字段。

    变量link 是指向特定页面的链接。然后我们获取您之前看到的我们想要的数据。 因此,当我们填充我们的项目字典时,我们需要从各个页面中获取产品编号。我们通过点击链接来做到这一点,回调是我们想要将 HTML 从单个页面发送到的函数。

    meta = {'items',items} 是我们将项目字典传输到该函数parse_item 的方式。我们创建一个元字典,键名为 items,值是我们刚刚创建的 items 字典。

    然后我们创建了函数parse_item。要访问该项目字典,我们必须通过response.meta 访问它,该字典包含我们在上一个函数中发出请求时创建的元字典。 response.meta['items'] 是我们访问项目字典的方式,然后我们像以前一样调用它items

    现在我们可以从前面的函数中填充已经包含数据的项目字典,并将产品编号添加到其中。然后,我们最终生成项目字典,告诉 scrapy 我们已经完成了从这张特定卡片中提取数据的添加。

    总结一下 parse 函数中的工作流程,我们有一个循环,对于每个循环迭代,我们都跟踪一个链接,我们首先提取四个数据,然后让 scrapy 跟踪特定链接并添加第 5 个数据在移动到原始 html 文档中的下一张卡片之前。

    附加信息

    1. 注意,如果您只想抓取一条数据,请尝试使用get(),如果您想抓取多条数据,请尝试使用getall()。这不是extract_first()extract()。如果你查看scrapy docs,他们推荐这个。 get() 更加简洁,当使用extract() 时,您并不总是确定是否会获得字符串或列表作为提取的数据。 getall 总会给你一个列表。

    2. 建议您在其他 scrapy 脚本中查找其他项目示例。通过搜索github或其他网站。我建议您在了解工作流程后仔细阅读文档here 上的项目页面。这很清楚,但不友好。我认为当您多次创建带有项目的脚本时,它会更容易理解。

    更新了下一页链接

    我已将您用于 next_page 的代码替换为更强大的获取所有数据的方法。

    last_pagination_link = response.xpath('//a[@class="page-link"]/@href')[-1].get()
    last_page_number = int(last_pagination_link.split('=')[-1])
    for i in range(2,last_page_number+1):
        url = f'https://www.topart-online.com/de/Blattzweige-Blatt-und-Bluetenzweige/l-KAT282?seg={i}'
        yield response.follow(url=url, callback=self.parse)
        
    

    在这里,我们为最后一页创建一个变量并从中获取数字。我这样做是因为如果您的页面超过 3 页。

    我们正在执行一个 for 循环并为每次迭代创建 url。这个 url 里面有一个叫做 f-string 的东西。 f'' 这允许我们在字符串中植入一个变量,并使用 for 循环在该 url 中添加一个数字或其他任何内容。因此,我们首先将数字 2 植入 url,它为我们提供了到第二页的链接。然后我们使用最后一页的变量 + 1,因为 range 函数只会通过选择 lastpage+1 来处理最后一页。创建第三页网址。然后我们也关注第 3 页的 url。

    【讨论】:

    • 一如既往地感谢好心的先生!我会按照您的指示阅读您链接的文档。但我还有一个问题:我运行代码时出现了这个错误:AttributeError: "LinkSpider" object has no attribute 'parse_item' 你能解释一下第 19 行的错误吗?
    • 检查 parse_items 函数的缩进。它不在课堂上。在 SO 中没有正确缩进是我的错。这不是我喜欢在输入代码时继续做的事情!更新了代码,但只是将 parse_items 行缩进了四个空格。这就是为什么它抱怨没有 parse_item。
    • 也只是检查了数据,当它应该是 51 项时,您只抓取了 40 项。看起来您的代码仅从第 1 页和第 2 页抓取。因为您仅从变量下一页中提取第一项。事实上,next_page 的 XPATH 选择器有两个值。
    • 现在可以了。我有一个新项目在工作,我可以在其中重建一个蜘蛛并自己检查它。你帮了我很多难以置信。我现在得到了我的 csv 文件,但我发现只有 40 个项目,所以有 20 个项目丢失。当我从 items.py 文件中导入某些内容时,我是否必须始终将其导入为“来自 ..items”?积分有什么用? //编辑:我看到你也写了,我只达到 40 项
    • 我已经用另一种方式更新了示例中的代码以获取下一页。它允许超过 3 个页面并抓取所需的 51 个对象。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多