为什么lxml不返回与此html元素关联的文本?

时间:2015-08-03 01:27:45

标签: python html xpath web-scraping lxml

我是使用python解析html的新手,我遇到了麻烦。我正在尝试编写一个简短的脚本,每天从网站上获取一个数字。但是,代码不是返回我想要的数字,而是返回None

以下网页显示每天穿越特定区域的自行车数量:http://eco-public.com/public2/?id=100023038。我正在尝试编写一个简短的python脚本来记录乘坐“昨天”的自行车数量(这是每天不同的数字,通常介于500和1,500之间)。当我检查网页的那个元素时,我发现它的xpath是://*[@id="region-lastDay"]/div/p[2]/text()。但是,当我尝试使用以下代码返回数字时,它会返回值None

import lxml.html as lh
import urllib2
doc = lh.parse(urlopen('http://eco-public.com/public2/?id=100023038'))
daily = doc.xpath('//*[@id="region-lastDay"]')
for i in daily:
    print i.text

我做错了什么?

3 个答案:

答案 0 :(得分:0)

如果您查看浏览器对该网址的原始请求(例如,使用Chrome或Firefox中的开发者工具,或只打印出urlopen(...).read()的值),您会看到region-lastDay元素看起来像这样:

    <!-- Key Numbers -->
    <div class="row" id="keyrow">
        <div id="region-total"  class="col-xs-12 col-sm-6 col-md-4"></div>
        <div id="region-lastDay" class="col-xs-12 col-sm-6 col-md-4" ></div>
    </div>

它确实是空的,因为在初始页面加载后使用返回JSON的AJAX调用(通过Chrome的开发人员工具 - &gt;网络选项卡观察)填充元素:

Chrome developer tools showing ajax call

因此,您需要使用lxml来解析来自用于AJAX调用的URL的响应,而不是使用json

从JSON中抓取最终元素,如下所示:

>>> import urllib.request
>>> import json
>>> response = urllib.request.urlopen("http://www.eco-public.com/api/h7q239dd/data/periode/100023038?begin=20150623&end=20150801&step=4")
>>> data = json.loads(response.read().decode('utf8'))
>>> data[-1]
{'comptage': 695, 'timestamp': 1438380000000, 'date': '2015-08-01 00:00:00.0'}

您还可能需要调整网址中的startend参数来获取其他日期的数据。

答案 1 :(得分:0)

html代码中div为空。

<div id="region-lastDay" class="col-xs-12 col-sm-6 col-md-4" ></div>

它充满了javascript。 Javascript部分无法由lxml处理。你需要能够处理javascript部分的包(例如selenium)。

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait


driver = webdriver.Firefox()  # OR  Chrome() / PhantomJS() / ...
driver.get('http://eco-public.com/public2/?id=100023038')
xpath = '//*[@id="region-lastDay"]/div'
WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, xpath)))
daily = driver.find_elements_by_xpath(xpath)
for i in daily:
    print i.text
# driver.quit()

答案 2 :(得分:0)

我知道这是一个老问题;但是,我在加载Javascript生成的页面时遇到了问题,因为lxml在JS加载之前抓取数据并且我找到了解决方案。

此解决方案将要求您使用这些导入:

import sys  
from PyQt4.QtGui import *  
from PyQt4.QtCore import *  
from PyQt4.QtWebKit import *  
from lxml import html 

和此代码等待页面呈现

#Take this class for granted.Just use result of rendering.
class Render(QWebPage):  
  def __init__(self, url):  
    self.app = QApplication(sys.argv)  
    QWebPage.__init__(self)  
    self.loadFinished.connect(self._loadFinished)  
    self.mainFrame().load(QUrl(url))  
    self.app.exec_()  

  def _loadFinished(self, result):  
    self.frame = self.mainFrame()  
    self.app.quit() 

现在,一旦你有了这个,你就可以这样开始拼抢:

url = 'http://pycoders.com/archive/'  
r = Render(url)  
result = r.frame.toHtml()
#This step is important.Converting QString to Ascii for lxml to process
archive_links = html.fromstring(str(result.toAscii()))
print archive_links

使用Render类,您可以加载页面并将其转换为Ascii for lxml。 我有“print archive_links”,所以你可以看到它返回的结构,你不需要这个。

现在要收集您收集的信息,您可以使用lxml。

#Now using correct Xpath we are fetching URL of archives
archive_links = tree.xpath('//divass="campaign"]/a/@href')
print archive_links

所有这些代码从上到下组合在一起,将为您提供一个包含JS生成元素的加载页面,并允许您使用lxml抓取它们。

内容来源:https://impythonist.wordpress.com/2015/01/06/ultimate-guide-for-scraping-javascript-rendered-web-pages/