Сканирование данных с использованием Scrapy + Selenium + PhantopJS потерянные данные

Я пытаюсь просканировать данные таблицы с http://www.sse.com.cn/assortment/stock/list/share/, который является страницами AJAX. Мой код следующий:

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. Но это не сработало. Может кто подскажет, почему я потерял данные? И есть ли простые способы сканирования страниц ajax с помощью Scrapy.


person XiaoJin    schedule 02.10.2017    source источник


Ответы (1)


jQuery.active - количество текущих запросов AJAX. Таким образом, драйвер будет ждать завершения запросов ajax. Но потребуется некоторое время, чтобы проанализировать ответ и отобразить данные.

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

Если драйвер попытается получить исходный код до завершения рендеринга, он потеряет некоторые данные. Я бы выбрал условие для проверки значения элемента. Здесь я поддерживаю текущий максимальный идентификатор запаса, и поскольку все данные находятся в порядке возрастания, новые данные должны быть больше, чем он:

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 ссылка для скачивания, которая является более надежным и простым способом получения данных.

person CtheSky    schedule 03.10.2017