【问题标题】:Crawl data using Scrapy+Selenium+PhantopJS lost data使用 Scrapy+Selenium+PhantopJS 抓取数据丢失数据
【发布时间】:2017-10-02 10:34:14
【问题描述】:

我尝试从 AJAX 页面的http://www.sse.com.cn/assortment/stock/list/share/ 抓取表格数据。我的代码如下:

import scrapy

class GovSpider(scrapy.Spider):
    name = 'gov'

    url = "http://www.sse.com.cn/assortment/stock/list/share/"

    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"
    }

    driver = webdriver.PhantomJS('/Users/luozhongjin/ScrapyDemo/ScrapyDemo/phantomjs')
    driver.implicitly_wait(15)

    def start_requests(self):
        yield scrapy.Request(url = self.url, headers = self.headers,callback = self.parse);

    def parse(self, response):
        self.driver.get(response.url)
        self.driver.set_window_size(1124, 850)
        i = 1
        while True:
            soup = BeautifulSoup(self.driver.page_source, 'lxml')
            trs = soup.findAll("tr")
            for tr in trs:
                try:
                    tds = tr.findAll("td")
                    print tds
                    item = GovSpiderItem()
                    item["name"] = tds[1].string
                    print ("ok")
                    yield item
                except:
                    pass
            try:
                next_page = self.driver.find_element_by_class_name("glyphicon-menu-right").click()
                i = i + 1
                if i >= 55:
                    break
            except:
                break

但是当它完成后,我检查了json文件,发现它丢失了数据,也就是说,我需要全部54页数据,但有时只保存53页数据,有时52页数据甚至更少.在我的不同测试中。但是我加了一行

time.sleep(3)

在解析函数的while循环结束时,它可以工作。但我不知道它为什么有效。我猜可能是ajax请求没有及时完成,导致数据丢失。所以我添加以下行来测试

WebDriverWait(self.driver, 10).until(lambda driver: self.driver.execute_script("return jQuery.active == 0"))

此行用于等待 ajax 请求完成。但它没有用。有人能告诉我为什么我丢失了数据吗?有没有简单的方法用 Scrapy 抓取 ajax 页面。

【问题讨论】:

    标签: python ajax scrapy web-crawler scrapy-spider


    【解决方案1】:

    jQuery.active 是当前 AJAX 请求的数量。因此驱动程序将等待 ajax 请求完成。但是解析响应和渲染数据需要一些时间。

    ajax complete -> render the data -> html source updated
    

    如果驱动程序在渲染完成之前尝试获取源代码,它将丢失一些数据。我会选择一个条件来检查元素值。这里我维护了一个当前的最大股票 id,由于所有数据都是按升序排列的,因此新数据必须大于它:

    return current_max_id < parseInt(document.getElementsByTagName("td")[0].children[0].text);
    

    数据丢失的另一个可能原因是 driver.implicitly_wait(15) 可能无法在此处工作,如文档所述:

    隐式等待告诉 WebDriver 轮询 DOM 一定数量 不立即尝试查找任何元素(或多个元素)的时间 可用的。默认设置为 0。一旦设置,隐式等待为 为 WebDriver 对象的生命周期设置。

    这里你将driver.page_source输入BeautifulSoup而不是driver.find_xxx,所以driver.implicitly_wait(15)不会被触发,它可能会跳过第1页。这里我会使用另一个条件来检查:

    return document.getElementsByTagName("td").length > 0;
    

    测试代码:

    import scrapy
    from bs4 import BeautifulSoup
    from selenium import webdriver
    from selenium.webdriver.support.ui import WebDriverWait
    
    
    class GovSpider(scrapy.Spider):
        name = 'gov'
    
        url = "http://www.sse.com.cn/assortment/stock/list/share/"
    
        driver = webdriver.Chrome()
        driver.set_window_size(1124, 850)
    
        def start_requests(self):
            yield scrapy.Request(url=self.url, callback=self.parse)
    
        def parse(self, response):
            i = 1
            current_max = 0
    
            self.driver.get(response.url)
            WebDriverWait(self.driver, 10).until(
                lambda driver: self.driver.execute_script('return document.getElementsByTagName("td").length > 0;'))
    
            while True:
                soup = BeautifulSoup(self.driver.page_source, 'lxml')
                trs = soup.findAll("tr")
                for tr in trs:
                    try:
                        tds = tr.findAll("td")
                        stock_id = int(tds[0].string)
                        current_max = max(current_max, stock_id)
                        yield {
                            'page num': i,
                            'stock id': tds[0].string
                        }
                    except:
                        pass
                try:
                    self.driver.find_element_by_class_name("glyphicon-menu-right").click()
    
                    js_condition_tpl = 'return {} < parseInt(document.getElementsByTagName("td")[0].children[0].text);'
                    WebDriverWait(self.driver, 10).until(
                        lambda driver: self.driver.execute_script(js_condition_tpl.format(current_max)))
    
                    i = i + 1
                    if i >= 55:
                        break
                except:
                    break  
    

    PS:如果您只需要数据本身,页面中有一个xls 下载链接,这是一种更可靠、更容易获取数据的方法。

    【讨论】:

      猜你喜欢
      • 2020-12-11
      • 2018-09-16
      • 2019-08-15
      • 2013-05-23
      • 2014-01-23
      • 1970-01-01
      • 2022-08-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多