在 Scrapy 中嵌套项目数据

Posted

技术标签:

【中文标题】在 Scrapy 中嵌套项目数据【英文标题】:Nesting item data in Scrapy 【发布时间】:2017-01-29 08:41:29 【问题描述】:

我对 Python 和 Scrapy 还很陌生,并且对于如何在 Scrapy 的帮助下创建嵌套 JSON 感到困惑。

在 XPath Helper 和一些谷歌搜索的帮助下,从 html 中选择我想要的元素并不是问题。然而,我不太确定我应该如何获得我想要的 JSON 结构。

我想要的 JSON 结构如下所示:

"menu": 
    "Monday": 
        "alt1": "Item 1",
        "alt2": "Item 2",
        "alt3": "Item 3"
    ,
    "Tuesday": 
        "alt1": "Item 1",
        "alt2": "Item 2",
        "alt3": "Item 3"
    

HTML 看起来像:

<ul>
    <li class="title"><h2>Monday</h2></li>
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
</ul>
<ul>
    <li class="title"><h2>Tuesday</h2></li>
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
</ul>

我确实找到了https://***.com/a/25096896/6856987,但是我无法调整它来满足我的需要。我将非常感谢您在正确的方向上推动我如何实现这一目标。

编辑:在 Padraic 提供的推动下,我设法离我想要完成的目标更近了一步。我想出了以下内容,这比我以前的情况略有改善。 JSON 仍然不是我想要的。

爬虫:

import scrapy
from dmoz.items import DmozItem

class DmozSpider(scrapy.Spider):
    name = "dmoz"
    start_urls = ['http://urlto.com']

    def parse(self, response):
        uls = response.xpath('//ul[position() >= 1 and position() < 6]')
        item = DmozItem()
        item['menu'] = 
        item['menu'] = "restaurant": "name"
        for ul in uls:
                item['menu']['restaurant']['dayOfWeek'] = ul.xpath("li/h2/text()").extract()
                item['menu']['restaurant']['menuItem'] = ul.xpath("li/text()").extract()
                yield item

生成的 JSON:

[  
      
        "menu":  
            "dayOfWeek":[  
                "Monday"
            ],
            "menuItem":[  
                "Item 1",
                "Item 2",
                "Item 3"
            ]
        
    ,
      
        "menu":  
            "dayOfWeek":[  
                "Tuesday"
            ],
            "menuItem":[  
                "Item 1",
                "Item 2",
                "Item 3"
            ]
        
    
]

确实感觉我做错了一千零一件事,希望比我更聪明的人能指出正确的方法。

【问题讨论】:

我能看到dmoz.items.DmozItem吗? 【参考方案1】:

你只需要找到所有的ul,然后提取lis进行分组,下面是一个使用lxml的例子:

from lxml import html

h = """<ul>
    <li class="title"><h2>Monday</h2></li>
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
</ul>
<ul>
    <li class="title"><h2>Tuesday</h2></li>
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
</ul>"""

tree = html.fromstring(h)

uls = tree.xpath("//ul")

data = 
# iterate over all uls
for ul in uls:
    # extract the ul's li's
    lis = ul.xpath("li")
    # use the h2 text as the key and all the text from the remaining as values
    # with enumerate to add the alt logic
    data[lis[0].xpath("h2")[0].text] =  "alt".format(i): node.text for i, node in enumerate(lis[1:], 1)

print(data)

这会给你:

'Monday': 'alt1': 'Item 1', 'alt2': 'Item 2', 'alt3': 'Item 3',
 'Tuesday': 'alt1': 'Item 1', 'alt2': 'Item 2', 'alt3': 'Item 3'

如果你想把它放在一个单一的理解中:

data = lis[0].xpath("h2")[0].text:
               "alt".format(i): node.text for i, node in enumerate(lis[1:], 1)
                    for lis in (ul.xpath("li") for ul in tree.xpath("//ul"))

在您的问题中使用您编辑的代码并遵循相同的要求输出:

def parse(self, response):
    uls = response.xpath('//ul[position() >= 1 and position() < 6]')
    item = DmozItem()
    # just create an empty dict
    item['menu'] = 
    for ul in uls:
        # for each ul, add a key value pair day: alti: each li_text skipping the first
        item['menu'][ul.xpath("li/h2/text()").extract_first()]\
            = "alt".format(i): node.text for i, node in enumerate(ul.xpath("li[postition() > 1]/text()").extract(), 1)
    # yield outside the loop 
    yield item

这将为您提供一个字典中的数据,例如:

In [15]: d = "menu":'Monday': 'alt1': 'Item 1', 'alt2': 'Item 2', 'alt3': 'Item 3',
                  'Tuesday': 'alt1': 'Item 1', 'alt2': 'Item 2', 'alt3': 'Item 3'

In [16]: d["menu"]["Tuesday"]
Out[16]: 'alt1': 'Item 1', 'alt2': 'Item 2', 'alt3': 'Item 3'

In [17]: d["menu"]["Monday"]
Out[17]: 'alt1': 'Item 1', 'alt2': 'Item 2', 'alt3': 'Item 3'

In [18]: d["menu"]["Monday"]["alt1"]
Out[18]: 'Item 1'

这比你的新问题更符合你的原始问题预期输出,但我认为你在添加"dayOfWeek" 等的新逻辑中所做的事情没有任何优势。

【讨论】:

感谢您的推动,帕德莱克。它让我更接近了一点,但我很难将它翻译成与 Scrapy 一起工作。我用更多信息更新了我原来的问题。 Padraic,再次感谢您的出色帮助。我不得不将node.text 更改为node,否则会引发错误(AttributeError: 'unicode' object has no attribute 'text')。有趣的是,它没有以正确的顺序返回项目(或子项目),在实际抓取时,我按以下顺序获取它们:星期五、星期二、星期四、星期三、星期一。但我确信我可以解决这个问题,或者解决它。再次感谢! @Kristoffer,不用担心,dicts 是无序的,如果你想订购,你应该使用 collections.OrderedDict 代替常规 dict

以上是关于在 Scrapy 中嵌套项目数据的主要内容,如果未能解决你的问题,请参考以下文章

scrapy突然创建多个项目

Django项目中运行Scrapy项目

Python1+x证书培训笔记-附(大数据项目实战)-可做为简单课设

Python1+x证书培训笔记-附(大数据项目实战)-可做为简单课设

Python Scrapy项目创建(基础普及篇)

在 Scrapy 中连接 Xpath 嵌套文本