2017-10-02 4 views
0

J'essaie de ramper les données de la table de http://www.sse.com.cn/assortment/stock/list/share/ qui est AJAX code pages.My suit:les données d'exploration à l'aide Scrapy + Sélénium + PhantopJS données perdues

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 

Mais quand il a terminé, je vérifie la JSON fichier et a constaté qu'il a perdu des données, c'est-à-dire, j'ai besoin de toutes les données de 54 pages, mais il sauve parfois 53 pages de données, parfois 52 pages de données ou même beaucoup moins. Mais j'ajoute la ligne

time.sleep(3) 

à la fin de la boucle while de la fonction Parse, cela fonctionne. Mais je ne sais pas pourquoi ça marche. Je suppose que cela pourrait être la requête ajax n'a pas fini sans délai, ce qui conduit à la perte de données. Donc, j'ajoute la ligne suivante à tester

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

Cette ligne est utilisée pour l'attente de la requête ajax terminée. Mais ça n'a pas marché. Quelqu'un peut-il me dire pourquoi j'ai perdu des données? Et existe-t-il un moyen simple d'explorer les pages ajax avec Scrapy.

Répondre

1

jQuery.active est le nombre de demandes AJAX en cours. Ainsi, le pilote attendra que les requêtes ajax se terminent. Mais il faudra un certain temps pour analyser la réponse et rendre les données.

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

Si le pilote essaie d'obtenir la source avant la fin du rendu, il perdra certaines données. Je choisirais une condition pour vérifier la valeur de l'élément. Ici, je maintiens un id stock actuel max et puisque toutes les données sont dans l'ordre croissant, les nouvelles données doivent être supérieures à ce:

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

Une autre raison possible pour la perte de données est que driver.implicitly_wait(15) peut ne pas fonctionner ici comme décrit par la documentation :

une attente implicite dit WebDriver au sondage DOM pour une certaine quantité de temps en essayant de trouver un élément (ou des éléments) ne sont pas immédiatement disponibles . Le paramètre par défaut est 0. Une fois défini, l'attente implicite est définie pour la durée de vie de l'objet WebDriver.

Ici vous nourrissez driver.page_source dans BeautifulSoup au lieu de driver.find_xxx, de sorte que le driver.implicitly_wait(15) ne sera pas déclenchée et il peut sauter la page 1. Ici, je voudrais utiliser une autre condition pour vérifier:

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

Code d'essai :

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: si vous avez besoin des données lui-même, il y a un lien de téléchargement xls dans la page qui est un plus r obust et moyen facile d'obtenir les données.