python 2.7,xml,beautifulsoup4:只返回匹配的父标签

问题描述:

我想解析一些XML,但遇到问题时强制它只选择请求标记,如果它是父标记。例如,我的XML的部分是:python 2.7,xml,beautifulsoup4:只返回匹配的父标签

<Messages> 
    <Message ChainCode="LI" HotelCode="5501" ConfirmationID="5501"> 
     <MessageContent> 
     <OTA_HotelResNotifRQ TimeStamp="2014-01-24T21:02:43.9318703Z" Version="4" ResStatus="Book"> 
      <HotelReservations> 
      <HotelReservation> 
       <RoomStays> 
       <RoomStay MarketCode="CC" SourceOfBusiness="CRS"> 
        <RoomRates> 
        <RoomRate EffectiveDate="2014-02-04" ExpireDate="2014-02-06" RoomTypeCode="12112" NumberOfUnits="1" RatePlanCode="RAC"> 
         <Rates> 
         <Rate EffectiveDate="2014-02-04" ExpireDate="2014-02-06" RateTimeUnit="Day" UnitMultiplier="3"> 
          <Base AmountBeforeTax="749.25" CurrencyCode="USD" /> 
          <Total AmountBeforeTax="749.25" CurrencyCode="USD" /> 
         </Rate> 
         </Rates> 
        </RoomRate> 
        </RoomRates> 
        <Total AmountBeforeTax="2247.75" CurrencyCode="USD"> 
        <Taxes Amount="0.00" /> 
        </Total> 
       </RoomStay> 
       </RoomStays> 
      </HotelReservation> 
      </HotelReservations> 
     </OTA_HotelResNotifRQ> 
     </MessageContent> 
    </Message> 
    </Messages> 

我已经得到了整个事情解析我是如何将其与“总”标签的除外。

我想要得到总标签:

<Total AmountBeforeTax="2247.75" CurrencyCode="USD"> 
    <Taxes Amount="0.00" /> 
</Total> 

发生了什么事,是它的中返回的“总”标签是RoomRates的孩子\ RoomRate \价格\率。我试图弄清楚如何指定它来返回RoomStays \ RoomStay \ Total标记。我目前拥有的是:

soup = bs(response, "xml") 

messages = soup.find_all('Message') 

for message in messages: 
    hotel_code = message.get('HotelCode') 

    reservations = message.find_all('HotelReservation') 
    for reservation in reservations: 
     uniqueid_id = reservation.UniqueID.get('ID') 
     uniqueid_idcontext = reservation.UniqueID.get('ID_Context') 

     roomstays = reservation.find_all('RoomStay') 
     for roomstay in roomstays: 

      total = roomstay.Total 

任何想法如何指定确切的标签我试图拉?如果有人想知道for循环,那是因为通常会有多个“Message”,“Hotel Reservation”,“Room Stay”等标签,但我已经将它们移除以仅显示一个。有时候可能会有多个Rate \ Rates标签,所以我不能要求它给我第二个“Total”标签。

希望我已经解释了这一点。

有时候也可能有多个Rate \ Rates标签,所以我不能要求它给我第二个“Total”标签。

为什么不只是遍历所有Total标签并跳过那些没有Taxes孩子的标签?

reservations = message.find_all('HotelReservation') 
for reservation in reservations: 
    totals = reservation.find_all('Total') 
    for total in totals: 
     if total.find('Taxes'): 
      # do stuff 
     else: 
      # these aren't the totals you're looking for 

如果你更一般要消除那些没有子节点,你可以做以下任一:

if next(total.children, None): 
    # it's a parent of something 

if total.contents: 
    # it's a parent of something 

,或者你可以use a function instead of a string as your filter

total = reservation.find(lambda node: node.name == 'Total' and node.contents) 

或者你可以看看其他方式来找到这个标签:它是一个直接的孩子RoomStay,而不仅仅是一个后代;它不是Rate的后代;它是RoomStay下的最后一个Taxes后代;所有这些都可以轻松完成。


话虽这么说,这似乎是对的XPath一个完美的工作,这BeautifulSoup不支持,但ElementTreelxml做...

+0

忘了提的是,“税”孩子不会永远存在,所以不幸的是,我不认为这两种解决方案都可以工作:/但我会尝试查看ElementTree和LXML。 – crookedleaf

+0

@crookedleaf:那么你想要什么规则呢?不管它是什么,你都可以写 - 我展示了如何写一个备选方案,并列出了其他三种可能的备选方案,这些备选方案同样简单。但是在你编码之前你必须知道你想要哪一个。 – abarnert

+0

@abarnet其实,没有注意到你的第二个解决方案有两个不同的解决方案,第三个解决方案不在我最初看的时候。基本上,我试图把$ 2247.75作为一个变量。我最初希望的是做一些类似“total = roomstay.Total”的东西。get('AmountBeforeTaxes')“,如果不是之前的”Total“标签,它会给我想要达到的内容,我会尝试其他两种解决方案。 – crookedleaf