我尝试抓取来自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请求完成。但它没有用。有人能告诉我为什么我丢失了数据吗?是否有一种简单的方法可以使用Scrapy抓取ajax页面。
答案 0 :(得分: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
下载链接,这是一种更健壮,更简便的数据获取方式。