javascript - 可靠地抓取股价表

标签 javascript python r selenium web-scraping

问题:

我的目标是从该网站自动抓取包含货币价格的表格 stock prices .由于股票经纪人不提供 API,我不得不寻找变通办法。

为了避免重新发明轮子和浪费时间/金钱,我已经搜索过用于此目的的应用程序,但不幸的是,我没有找到一个可以与该网站一起使用的应用程序。

我尝试过的:

  1. Rrvest

R 以其简单和直接的使用而闻名。让我们看一下代码,它基本上是来自 texbook 的复制粘贴示例:

library("rvest")
url <- "https://iqoption.com/en/historical-financial-quotes?active_id=1&tz_offset=120&date=2016-12-19-19-0"
population <- url %>%
  read_html() %>%
  html_nodes(xpath='//*[@id="mCSB_3_container"]/table') %>%
  html_table()
population
population <- population[[1]]

head(population)

得到一个空表。

  1. JavaScriptcasperJS

这个选项是迄今为止最好的,我实际上能够提取数据,但它非常慢并且最终因“内存耗尽”错误而崩溃:

var casper = require('casper').create({
  logLevel:'debug',
  verbose:true,
  loadImages: false,
  loadPlugins: false,
  webSecurityEnabled: false,
  userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11"
});

var url = 'https://eu.iqoption.com/en/historical-financial-quotes?active_id=1&tz_offset=60&date=2016-12-19-21-0';
var length;
var fs = require('fs');
var sep = ';';
//var count = 0;
casper.start(url);

//date
var today = new Date();
var dd = today.getDate();
var mm = today.getMonth()+1; //January is 0!
var hh = today.getHours();  
var fff = today.getMilliseconds();  
var MM = today.getMinutes();

var yyyy = today.getFullYear();
if(dd<10){
    dd='0'+dd;
} 
if(mm<10){
    mm='0'+mm;
} 


var today = yyyy +'_'+mm + '_' +dd + '_'+ hh +'_'+ MM +'_'+ fff;
casper.echo(today);

function getCellContent(row, cell) {
    cellText = casper.evaluate(function(row, cell) {
        return document.querySelectorAll('table tbody tr')[row].childNodes[cell].innerText.trim();
    }, row, cell);
    return cellText;
}

function moveNext()
{
    var rows = casper.evaluate(function() {
        return document.querySelectorAll('table tbody tr');
    });
    length = rows.length;
    this.echo("table length: " + length);
};

//get 3 tables
for (var mins = 0; mins < 3; mins++)
{ 

    url = 'https://eu.iqoption.com/en/historical-financial-quotes?active_id=1&tz_offset=60&date=2016-12-19-21-' + mins;

    casper.echo(url);
    casper.thenOpen(url);
     casper.then(function() {
        this.waitForSelector('#mCSB_3_container table tbody tr');
    }); 

    casper.then(moveNext);

    casper.then(function() {
    for (var i = 0; i < length; i++) 
    {
        //this.echo("Date: " + getCellContent(i, 0));
        //this.echo("Bid: " + getCellContent(i, 1));
        //this.echo("Ask: " + getCellContent(i, 2));
        //this.echo("Quotes: " + getCellContent(i, 4));

        fs.write('prices_'+today+'.csv', getCellContent(i, 0) + sep + getCellContent(i, 1) + sep + getCellContent(i, 2) + sep + getCellContent(i, 4) + "\n", "a");
    }
    });  


}

casper.run(); 
this.echo("finished with processing");
  1. JavaSciptPhantomJS

使用这个选项我只得到一个表:

var webPage = require('webpage');
var page = webPage.create();

page.open('https://iqoption.com/en/historical-financial-quotes?active_id=1&tz_offset=120&date=2016-12-19-19-0', function(status) {

  var title = page.evaluate(function() {
    return document.querySelectorAll('table tbody tr');

  });
});
  1. PythonBeautifulSoup

结果得到一个空表:

from bs4 import BeautifulSoup
from urllib2 import urlopen


url = "https://iqoption.com/en/historical-financial-quotes?active_id=1&tz_offset=120&date=2016-12-19-19-0"
soup = BeautifulSoup(urlopen(url), "lxml")

table = soup.findAll('table', attrs={ "class" : "quotes-table-result"})
print("table length is: "+ str(len(table)))
  1. Scrapy

尝试使用“Scrapy Shell”,但结果是一个空表。

  1. Pandas它是 read_html()

使用 pandas 我遇到了以下错误:

ValueError: No tables found matching pattern '.+'

代码:

import pandas as pd
import html5lib

f_states = pd.read_html("https://iqoption.com/en/historical-financial-quotes?active_id=1&tz_offset=120&date=2016-12-19-19-0")
print f_states

问题:

  1. 您能解释一下为什么我在尝试不同的网络抓取和 HTML 解析工具时得到空表吗?
  2. 对这个特定股票价格网站进行网络抓取的最可靠方法是什么?

注意:这可能是网站试图阻止网页抓取,我研究过robots.txt ,但看起来只有特定于浏览器支持和特定于 google-bot 的说明。

最佳答案

主要问题是这个特定站点非常动态 - 加载表格是与您的浏览器发出的额外 XHR 请求异步完成的。

除了使用实际浏览器(casperJSPhantomJS)之外的所有方法都会失败,因为它们只会下载初始 HTML 页面 没有所有动态部分。换句话说,rvesturllib2 不是浏览器,它们没有内置 JavaScript 引擎。

话虽如此,由于此资源没有可用的公共(public) API,您基本上有两个通用选项,我们称其为“低级”和“高级”:

  1. “低级”。使用浏览器开发人员工具,检查表格的加载方式并在您的代码中模拟相同的请求 - 例如,使用 requests .

  2. “高级”。实际上自动化一个真正的浏览器,例如,selenium .此选项类似于您的 casperJSphantomJS 方法,但您必须考虑某些事情,例如“等待加载元素” - 让浏览器有时间加载页面和表格。

让我们关注第二种方法。通过 pip 安装 selenium:

pip install selenium

让我们使用 Chrome(您也可以使用 FirefoxPhantomJS 或其他)。假设您安装了实际的浏览器,请下载最新的 chromedriver适用于 Windows。通过 Getting Started页面并确保你有它的工作。

然后,让我们加载您的网页,等待加载表格(等待是通过 WebDriverWait 和一组预期条件完成的)。然后,我们将获取页面源并将其传递给 pandas 以进行进一步的解析和数据提取(我们也可以通过 selenium 来完成 - 定位元素并获取它们文本,但这会很慢 - 记住你的 casperJS 方法):

import pandas as pd

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC


url = "https://iqoption.com/en/historical-financial-quotes?active_id=1&tz_offset=120&date=2016-12-19-19-0"

driver = webdriver.Chrome()
driver.maximize_window()
driver.get(url)

# wait for a table to load
wait = WebDriverWait(driver, 10)
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "#mCSB_3_container table tbody tr")))

# read the page source and pass it to "pandas"
dfs = pd.read_html(driver.page_source)

# close the browser - we don't need it anymore, it's job is done
driver.close()

print(dfs)  # dfs is a list of DataFrame instances

请注意,您不必使用 pandas 进行 HTML 解析和数据提取 - 一旦您在 driver.page_source 中拥有 HTML 源代码,您就完成了最复杂的部分。然后,您可以使用任何您喜欢的工具 - 流行的选项是 BeautifulSouplxml.html .从性能的 Angular 来看,后者将是一个不错的选择。


作为旁注,在进行网络抓取时,您应该始终努力成为一名优秀的网络抓取公民并站在法律的一边——遵守服务的“使用条款”,尊重“机器人”。 txt”规则,不要经常访问该站点和/或通过提供特定的“User-Agent” header 或联系资源所有者或维护者以了解获取所需数据的最佳方式来表明自己的身份。相关资源:

关于javascript - 可靠地抓取股价表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41344883/

相关文章:

python - 连接python列表中的单个元素

r - 按索引组合列表列表的元素

javascript - 如何调试旧版 javascript 代码(在 IE 中工作正常,但在 Google Chrome 中异常)

javascript - Node.js - 服务器关闭了连接?

python - Django 条目归档

Python Pandas : create new column based on category values from another dataframe

r - ggplot x Axis 刻度线标签

r - 为什么条件均值(条件模式)或随机效应的图看起来像这样?

javascript - 在单独的网站上使用 ajax 登录 Rails 3 应用程序(使用 Devise 进行身份验证)

javascript - 使用 Closure-Compiler 类型检查数组内容