【问题标题】:Parsing with lxml xpath使用 lxml xpath 解析
【发布时间】:2013-07-30 17:31:41
【问题描述】:

我试图实现一个lxml, xpath 代码来解析来自链接的html:https://www.theice.com/productguide/ProductSpec.shtml?specId=251 具体来说,我试图解析页面末尾附近的<tr class="last"> 表。

我想获取该子表中的文本,例如:“纽约”及其旁边列出的时间(对伦敦和新加坡也是如此)。

我有以下代码(不能正常工作):

doc = lxml.html.fromstring(page)
tds = doc.xpath('//table[@class="last"]//table[@id"tradingHours"]/tbody/tr/td/text()')

使用 BeautifulSoup:

table = soup.find('table', attrs={'id':'tradingHours'})
for td in table.findChildren('td'):
    print td.text

实现这一目标的最佳方法是什么?我想使用lxml 而不是beautifulSoup(只是为了看看区别)。

【问题讨论】:

    标签: python parsing lxml


    【解决方案1】:

    您的lxml 代码非常接近工作。主要问题是table 标签不是具有class="last" 属性的标签。相反,它是具有该属性的tr 标记:

        </tr><tr class="last"><td>TRADING HOURS</td>&#13;
    

    因此,

    //table[@class="last"]
    

    没有匹配项。还有一个小语法错误:@id"tradingHours" 应该是@id="tradingHours"

    您也可以完全省略//table[@class="last"],因为table[@id="tradingHours"] 已经足够具体了。


    与您的 BeautifulSoup 代码最接近的模拟是:

    import urllib2
    import lxml.html as LH
    
    url = 'https://www.theice.com/productguide/ProductSpec.shtml?specId=251'
    doc = LH.parse(urllib2.urlopen(url))
    for td in doc.xpath('//table[@id="tradingHours"]//td/text()'):
        print(td.strip())
    

    grouper recipezip(*[iterable]*n) 在解析表时通常非常有用。它将iterable 中的项目收集到n 项目组中。我们可以像这样在这里使用它:

    texts = iter(doc.xpath('//table[@id="tradingHours"]//td/text()'))
    for group in zip(*[texts]*5):
        row = [item.strip() for item in group]
        print('\n'.join(row))
        print('-'*80)
    

    我不太擅长解释石斑鱼食谱的工作原理,但我做了一个attempt here


    此页面正在使用 JavaScript 重新格式化日期。要在 JavaScript 更改内容之后 抓取页面,您可以使用 selenium:

    import urllib2
    import lxml.html as LH
    import contextlib
    import selenium.webdriver as webdriver
    
    url = 'https://www.theice.com/productguide/ProductSpec.shtml?specId=251'
    with contextlib.closing(webdriver.PhantomJS('phantomjs')) as driver:
        driver.get(url)
        content = driver.page_source
        doc = LH.fromstring(content)
        texts = iter(doc.xpath('//table[@id="tradingHours"]//td/text()'))
        for group in zip(*[texts]*5):
            row = [item.strip() for item in group]
            print('\n'.join(row))
            print('-'*80)
    

    产量

    NEW YORK
    8:00 PM-2:15 PM *
    20:00-14:15
    7:30 PM
    19:30
    --------------------------------------------------------------------------------
    LONDON
    1:00 AM-7:15 PM
    01:00-19:15
    12:30 AM
    00:30
    --------------------------------------------------------------------------------
    SINGAPORE
    8:00 AM-2:15 AM *
    08:00-02:15
    7:30 AM
    07:30
    --------------------------------------------------------------------------------
    

    请注意,在这种特殊情况下,如果您不想使用 selenium,可以使用 pytz 自行解析和转换时间:

    import dateutil.parser as parser
    import pytz
    
    text = 'Tue Jul 30 20:00:00 EDT 2013'
    date = parser.parse(text)
    date = date.replace(tzinfo=None)
    print(date.strftime('%I:%M %p'))
    # 08:00 PM
    
    ny = pytz.timezone('America/New_York')
    london = pytz.timezone('Europe/London')
    london_date = ny.localize(date).astimezone(london)
    print(london_date.strftime('%I:%M %p'))
    # 01:00 AM
    

    【讨论】:

    • 谢谢,我一直在寻找这样的答案。是否可以使用xpath 区分“纽约”等城市和时代。例如,当前的for 循环正在打印所有内容,但我想将结果存储在站点中:一个有时间的城市。
    • 感谢以上内容,但有些地方不对劲。我得到的结果是:Tue Jul 30 20:00:00 EDT 2013-Tue Jul 30 14:15:00 EDT 2013 * Tue Jul 30 19:30:00 EDT 2013。这是相同的时间,重复了3次以上。此外,它还添加了额外的日期功能,我不确定它来自哪里。严格来说应该是网站上的时间。你知道这里发生了什么吗?就算我用BS,也是一样的结果……
    • 该页面正在使用 JavaScript 更改 HTML。 urllib2.urlopen 正在下载没有任何 JavaScript 处理的 HTML。浏览器会显示 JavaScript 处理后的结果。要在 JavaScript 处理后抓取页面,您可以使用 selenium(见上文)。
    • 所以和我上一个问题是一样的问题,再次感谢。
    • 如果你不介意,你怎么知道哪些部分/脚本正在被 JavaScript 处理?
    【解决方案2】:

    我喜欢 css 选择器比 xpaths 更适应页面变化:

    import urllib
    from lxml import html
    
    url = 'https://www.theice.com/productguide/ProductSpec.shtml?specId=251'
    
    response = urllib.urlopen(url).read()
    
    h = html.document_fromstring(response)
    for tr in h.cssselect('#tradingHours tbody tr'):
        td = tr.cssselect('td')
        print td[0].text_content(), td[1].text_content()
    

    【讨论】:

      【解决方案3】:

      如果站点是正确的 html,则 id 属性是唯一的,您可以在 doc.get_element_by_id('tradingHours') 找到该表。

      import urllib
      from lxml import html
      
      url = 'https://www.theice.com/productguide/ProductSpec.shtml?specId=251'
      
      response = urllib.urlopen(url).read()
      
      h = html.document_fromstring(response)
      
      print "BY ID"
      tradingHours = h.get_element_by_id('tradingHours')
      
      for tr in tradingHours.xpath('tbody/tr'):
          tds = tr.xpath('td')
          print tds[0].text.strip()
          for td in tds[1:]:
              print ' ', td.text.strip()
      

      结果

      BY ID
      NEW YORK
        Tue Jul 30 20:00:00 EDT 2013-Tue Jul 30 14:15:00 EDT 2013 *
        Tue Jul 30 19:30:00 EDT 2013
      LONDON
        Tue Jul 30 20:00:00 EDT 2013-Tue Jul 30 14:15:00 EDT 2013
        Tue Jul 30 19:30:00 EDT 2013
      SINGAPORE
        Tue Jul 30 20:00:00 EDT 2013-Tue Jul 30 14:15:00 EDT 2013 *
        Tue Jul 30 19:30:00 EDT 2013
      

      【讨论】:

        猜你喜欢
        • 2012-08-23
        • 2012-07-12
        • 2016-02-20
        • 1970-01-01
        • 1970-01-01
        • 2013-02-19
        • 2012-07-29
        • 2010-12-07
        • 1970-01-01
        相关资源
        最近更新 更多