2012-08-23 43 views
2

我用Scrapy了幾個星期,最近,我發現HtmlXPathSelector不能正確解析某些HTML文件。Scrapy無法解析某些HTML文件正確

在網頁http://detail.zol.com.cn/series/268/10227_1.html,這裏只有一個名爲標籤

`div id='param-more' class='mod_param '`. 

當我使用的XPath 「// DIV [@ ID = 'PARAM-更多']」選擇標記,它返回[]

我試過scrapy殼牌並且得到了同樣的結果。

當使用wget的檢索網頁,我也可以找到標籤「DIV ID =‘PARAM-更’類=‘MOD_PARAM’」 HTML源文件中,我認爲它不是由造成通過觸發動作顯示標籤的原因。

請給我一些關於如何解決這個問題的提示。

以下是關於該問題的代碼。當處理上述的URL,LEN(nodes_product)總是

def parse_series(self, response): 
    hxs = HtmlXPathSelector(response) 

    xpath_product = "//div[@id='param-normal']/table//td[@class='name']/a | "\ 
        "//div[@id='param-more']/table//td[@class='name']/a" 
    nodes_product = hxs.select(xpath_product) 
    if len(nodes_product) == 0: 
     # there's only the title, no other products in the series 
     ....... 
    else: 
     ....... 
+0

請你告訴你具體的代碼使用,返回不正確的結果。 –

+0

剛剛添加了代碼片段:) – flyer

回答

3

這似乎與XPathSelectors的錯誤。我創建了一個快速測試蜘蛛並遇到了同樣的問題。我相信這與頁面上的非標準字符有關。

我不相信問題是'param-more'div與任何javascript事件或CSS隱藏相關聯。我禁用了JavaScript,並更改了我的用戶代理(和位置),以查看這是否影響了頁面上的數據。它沒有。

我,但是,能夠解析「PARAM-更」 DIV使用beautifulsoup:

from scrapy.selector import HtmlXPathSelector 
from scrapy.spider import BaseSpider 
from bs4 import BeautifulSoup 

class TestSpider(BaseSpider): 
    name = "Test" 

    start_urls = [ 
     "http://detail.zol.com.cn/series/268/10227_1.html" 
       ] 

    def parse(self, response): 
     hxs = HtmlXPathSelector(response) 

     #data = hxs.select("//div[@id='param-more']").extract() 

     data = response.body 
     soup = BeautifulSoup(data) 
     print soup.find(id='param-more') 

別人可能知道更多關於XPathSelect問題,但暫時,你可以保存HTML通過beautifulsoup發現一件物品並將其傳入管道。

這裏是鏈接到最新版本beautifulsoup:http://www.crummy.com/software/BeautifulSoup/#Download

UPDATE

我相信我發現的具體問題。正在討論的網頁在meta標籤中指定它使用GB 2312 charset。從GB 2312到unicode的轉換是有問題的,因爲有些字符沒有unicode equivalent。除了UnicodeDammit,beautifulsoup的編碼檢測模塊實際確定編碼爲ISO 8859-2這一事實之外,這不是一個問題。問題在於lxml通過查看charset specified in the meta tag of the header來確定文檔的編碼。因此,lxml和scrapy感知的編碼類型不匹配。

下面的代碼演示了上述問題,並提供了一個替代不必依靠BS4庫:

from scrapy.selector import HtmlXPathSelector 
from scrapy.spider import BaseSpider 
from bs4 import BeautifulSoup 
import chardet 

class TestSpider(BaseSpider): 
    name = "Test" 

    start_urls = [ 
     "http://detail.zol.com.cn/series/268/10227_1.html" 
       ] 

    def parse(self, response): 

     encoding = chardet.detect(response.body)['encoding'] 
     if encoding != 'utf-8': 
      response.body = response.body.decode(encoding, 'replace').encode('utf-8') 

     hxs = HtmlXPathSelector(response) 
     data = hxs.select("//div[@id='param-more']").extract() 
     #print encoding 
     print data 

在這裏,你看到的是通過強制LXML使用UTF-8編碼,它不嘗試從它認爲是GB 2312-> utf-8的地圖進行映射。

在scrapy中,在scrapy/select/lxmlsel.py模塊中設置了HTMLXPathSelectors編碼。該模塊使用response.encoding屬性將響應主體傳遞給lxml解析器,該屬性最終在scrapy/http/response/test.py模塊中設置。

@property 
def encoding(self): 
    return self._get_encoding(infer=True) 

def _get_encoding(self, infer=False): 
    enc = self._declared_encoding() 
    if enc and not encoding_exists(enc): 
     enc = None 
    if not enc and infer: 
     enc = self._body_inferred_encoding() 
    if not enc: 
     enc = self._DEFAULT_ENCODING 
    return resolve_encoding(enc) 

def _declared_encoding(self): 
    return self._encoding or self._headers_encoding() \ 
     or self._body_declared_encoding() 

這裏要注意的重要一點是,_headers_encoding和_encoding都將最終體現在報頭中的meta標籤聲明的編碼:

,處理設置response.encoding屬性的代碼如下實際上使用諸如UnicodeDammit或chardet來確定文檔編碼。因此,如果文檔包含指定的編碼中包含無效字符的情況,我相信Scrapy會忽略它,最終導致我們今天看到的問題。

+0

** HtmlXPathSelector **默認使用** lxml **作爲後端,可能它的** lxml **無法處理的html源文件有問題。出現問題時,我將使用** BeautifulSoup **處理此類網頁。非常感謝:D – flyer

+0

*發現問題具體。我提供了一種替代以前解決問題的方法。 –

+0

哇,它的工作原理!但是,我抓取了許多類似[http://detail.zol.com.cn/series/268/10227_1.html]這樣的類似頁面,只有這個頁面出現了問題。例如,[http://detail.zol.com.cn/series/16/14042_1.html]有一個simliar標籤「div id ='param-normal''」,我可以使用HtmlXPathSelector獲取標籤。這些頁面具有相同的「字符集」。在處理html源文件之前檢查字符集是一個好習慣。再次感謝:D – flyer

0
'mod_param ' != 'mod_param' 

類不等於「MOD_PARAM」但它確實包含「MOD_PARAM」,注意,是在結束一個空格:

[email protected]:~$ scrapy shell http://detail.zol.com.cn/series/268/10227_1.html 
2012-08-23 09:17:28-0500 [scrapy] INFO: Scrapy 0.15.1 started (bot: scrapybot) 
Python 2.7.3 (default, Aug 1 2012, 05:14:39) 
IPython 0.12.1 -- An enhanced Interactive Python. 

In [1]: hxs.select("//div[@class='mod_param']") 
Out[1]: [] 

In [2]: hxs.select("//div[contains(@class,'mod_param')]") 
Out[2]: [<HtmlXPathSelector xpath="//div[contains(@class,'mod_param')]" data=u'<div id="param-more" class="mod_param "'>] 

In [3]: len(hxs.select("//div[contains(@class,'mod_param')]").extract()) 
Out[3]: 1 

In [4]: len(hxs.select("//div[contains(@class,'mod_param')]").extract()[0]) 
Out[4]: 5372 
+0

謝謝你在scrapy-user maillist中的幫助:)但是當我嘗試你的方法時,它仍然得到相同的結果** [] ** – flyer