Web用nodejs刮擦Google Finance主页
#node #webscraping #serpapi

将被刮擦

what

完整代码

如果您不需要解释,请看一下the full code example in the online IDE

const cheerio = require("cheerio");
const axios = require("axios");

const AXIOS_OPTIONS = {
  headers: {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36",
  }, // adding the User-Agent header as one way to prevent the request from being blocked
  params: {
    hl: "en", // parameter defines the language to use for the Google search
  },
};

function getGoogleFinanceResults() {
  return axios.get("https://www.google.com/finance/markets/indexes", AXIOS_OPTIONS).then(function ({ data }) {
    let $ = cheerio.load(data);

    const scripts = Array.from($("script"))
      .map((el) => $(el).prop("outerHTML"))
      .filter((el) => el.includes("key: 'ds:"))
      .join("");

    const allScriptsPattern = /key: '(?<key>[^']+)'.+?<\/script>/gm; //https://regex101.com/r/YUTocp/1
    const scriptObjects = [...scripts.matchAll(allScriptsPattern)].map((el) => {
      let script = `{${el[0].slice(0, -11)}`;
      eval(`script = ${script}`);
      script = JSON.stringify(script);
      return script;
    });

    let markets = scriptObjects.find((el) => el.includes('"key":"ds:6"'));

    const marketsRegionPattern =
      /\[null,\[\["\/m\/.+?],"(?<name>[^"]+)".+?\[(?<price>[^,]+),(?<value>[^,]+),(?<percentage>[^,]+).+?\]."(?<region>[^\/]+).+?null,"(?<stock>[^"]+)/gm; //https://regex101.com/r/jM9hbY/2

    const marketsRegions = [...markets.matchAll(marketsRegionPattern)].reduce((result, { groups }) => {
      const { name, price, value, percentage, region, stock } = groups;
      const index = {
        stock,
        link: `https://www.google.com/finance/quote/${stock}`,
        name,
        price,
        priceMovement: {
          percentage,
          value,
          movement: parseFloat(value) === 0 ? undefined : value[0] === "-" ? "Down" : "Up",
        },
      };
      if (result[region]) result[region].push(index);
      else result[`${region}`] = [index];
      return result;
    }, {});

    markets.match(marketsRegionPattern).forEach((el) => (markets = markets.replace(el, "")));

    const marketsCurrenciesAndCryptoPattern =
      /\[null,\[\["\/g\/.+?null,"(?<name>[^":]+)",\d.+?\[(?<price>\d[^,]+),(?<value>[^,]+),(?<percentage>[^,]+).+?null,"(?<stock>[^"]+)/gm; //https://regex101.com/r/vu6yI2/1

    const marketsCurrenciesAndCrypto = [...markets.matchAll(marketsCurrenciesAndCryptoPattern)].reduce((result, { groups }) => {
      const { name, price, value, percentage, stock } = groups;
      const index = {
        stock,
        link: `https://www.google.com/finance/quote/${stock}`,
        name,
        price,
        priceMovement: {
          percentage,
          value,
          movement: parseFloat(value) === 0 ? undefined : value[0] === "-" ? "Down" : "Up",
        },
      };
      if (name.includes("(")) {
        if (result.crypto) result.crypto.push(index);
        else result.crypto = [index];
      } else {
        if (result.currencies) result.currencies.push(index);
        else result.currencies = [index];
      }
      return result;
    }, {});

    markets.match(marketsCurrenciesAndCryptoPattern).forEach((el) => (markets = markets.replace(el, "")));

    const marketsFuturesPattern =
      /".+?",\d.+?\[(?<price>\d[^,]+),(?<value>[^,]+),(?<percentage>[^,]+).+?",null,"(?<stock>[^"]+)"\]\],"(?<name>[^"]+)/gm; //hhttps://regex101.com/r/50gPod/1

    const marketsFutures = [...markets.matchAll(marketsFuturesPattern)].reduce((result, { groups }) => {
      const { name, price, value, percentage, stock } = groups;
      const index = {
        stock,
        link: `https://www.google.com/finance/quote/${stock}`,
        name,
        price,
        priceMovement: {
          percentage,
          value,
          movement: parseFloat(value) === 0 ? undefined : value[0] === "-" ? "Down" : "Up",
        },
      };
      if (result.futures) result.futures.push(index);
      else result.futures = [index];
      return result;
    }, {});

    const marketsResults = { ...marketsRegions, ...marketsCurrenciesAndCrypto, ...marketsFutures };

    const marketTrends = Array.from($(".Sy70mc")).map((el) => ({
      title: $(el)
        .find(".r6XbWb")
        .contents()
        .filter((i, el) => el.nodeType === 3)
        .text(),
      link: `https://www.google.com/finance${$(el).find(".r6XbWb a").attr("href").slice(1)}`,
      results: Array.from($(el).find(".sbnBtf li")).map((el) => ({
        stock: $(el).find("a").attr("href").slice(8),
        link: `https://www.google.com/finance${$(el).find("a").attr("href").slice(1)}`,
        name: $(el).find(".ZvmM7").text(),
        price: $(el).find(".YMlKec").text(),
        priceMovement: {
          percentage: $(el).find(".JwB6zf").text(),
          value: $(el).find(".P2Luy").text(),
          movement: $(el).find(".NydbP").attr("aria-label").split(" ")[0],
        },
      })),
    }));

    const news = Array.from($(".yY3Lee")).map((el) => ({
      source: $(el).find(".sfyJob").text(),
      link: $(el).find(".z4rs2b a").attr("href"),
      date: $(el).find(".Adak").text(),
      snippet: $(el).find(".mRjSYb").text(),
      thumbnail: $(el).find(".a9REI").attr("src"),
      stocks: Array.from($(el).find(".wxtCmb")).map((el) => ({
        stock: $(el).attr("href").slice(8),
        link: `https://www.google.com/finance${$(el).attr("href").slice(1)}`,
        name: $(el).find(".X18JZ").text(),
        priceMovement: {
          percentage: $(el).find(".JwB6zf").text(),
          movement: $(el).find(".NydbP").attr("aria-label").split(" ")[0],
        },
      })),
    }));

    const discoverMore = Array.from($(".NBZP0e > div[role='listitem']")).map((el) => ({
      stock: $(el).find(".tOzDHb").attr("href").slice(8),
      link: `https://www.google.com/finance${$(el).find(".tOzDHb").attr("href").slice(1)}`,
      name: $(el).find(".RwFyvf").text(),
      price: $(el).find(".YMlKec").text(),
      priceMovement: {
        percentage: $(el).find(".JwB6zf").text(),
        movement: $(el).find(".NydbP").attr("aria-label").split(" ")[0],
      },
    }));
    return { markets: marketsResults, marketTrends, news, discoverMore };
  });
}

getGoogleFinanceResults().then((result) => console.dir(result, { depth: null }));

准备

首先,我们需要创建一个node.js* project,然后添加koude0软件包koude1koude1koude2koude2添加到网站上。

为此,在我们项目的目录中,打开命令行并输入:

$ npm init -y

,然后:

$ npm i cheerio axios

*如果您没有安装node.js,则可以download it from nodejs.org并遵循安装documentation

Process

首先,我们需要从HTML元素中提取数据。通过SelectorGadget Chrome extension,获得合适的CSS选择器的过程非常容易,通过单击浏览器中的所需元素,我们能够获取CSS选择器。但是,它并不总是完美地工作,尤其是当JavaScript大量使用该网站时。

如果您想了解更多有关它们的信息,我们在Serpapi上有专门的web Scraping with CSS Selectors博客文章。

下面的GIF说明了选择结果不同部分的方法。

how

代码说明

koude1koude2库中声明常数:

const cheerio = require("cheerio");
const axios = require("axios");

接下来,我们使用koude6编写请求选项:koude5,用于用作“真实”用户访问,以及提出请求的必要参数:

const AXIOS_OPTIONS = {
  headers: {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36",
  }, // adding the User-Agent header as one way to prevent the request from being blocked
  params: {
    hl: "en", // parameter defines the language to use for the Google search
  },
};

ð注意:Default koude2 request user-agent is koude8因此,网站了解这是一个发送请求并可能阻止其的脚本。 Check what's your user-agent

接下来,我们编写一个函数,该功能使请求并从页面返回接收到的数据。我们收到了koude2请求的响应,该请求具有我们destructureddata键,并用koude1对其进行解析:

function getGoogleFinanceResults() {
  return axios
    .get("https://www.google.com/search", AXIOS_OPTIONS)
    .then(function ({ data }) {
        let $ = cheerio.load(data);
        ...
  })
}

接下来,我们从页面上的所有脚本标签(使用koude13方法)中制作了一个新数组(koude12方法),并找到其中哪个包含"key: 'ds:6"字符串:

const script = Array.from($("script"))
  .map((el) => $(el).prop("outerHTML"))
  .find((el) => el.includes("key: 'ds:6"));

然后,我们需要从script字符串(koude16koude17方法)的开始和末端中删除不必要的部分,并执行它(使用koude18方法)从字符串中获取对象并制造有效的JSON字符串(我们需要这样做,因为启动器script字符串具有编码的符号(例如'\ u0026'),当我们执行它时,字符串已解码):

let markets;
const scriptObject = script.slice(script.indexOf("AF_initDataCallback") + 20, -11);
eval(`markets = JSON.stringify(${scriptObject})`);

然后,我们需要从markets字符串中获取区域,货币,加密和期货市场数据,因为它们在页面上只显示一个,并且在浏览器中通过JavaScript更改。

首先,我们定义了koude21,然后使用koude22我们从koude23方法中收到的iterable iterator制作数组。

接下来,我们取得匹配结果,并使用koude24方法制作新对象:

//https://regex101.com/r/jM9hbY/2
const marketsRegionPattern =
    /\[null,\[\["\/m\/.+?],"(?<name>[^"]+)".+?\[(?<price>[^,]+),(?<value>[^,]+),(?<percentage>[^,]+).+?\]."(?<region>[^\/]+).+?null,"(?<stock>[^"]+)/gm;

const marketsRegions = [...markets.matchAll(marketsRegionPattern)].reduce((result, { groups }) => {
    ...
}, {});

接下来,在reduce方法的每次迭代中,我们destructure groups对象,检查results对象中是否存在带有当前region的数组,我们将index对象我们在此数组中,否则我们使用index元素创建一个新数组对象:

const { name, price, value, percentage, region, stock } = groups;
const index = {
  stock,
  link: `https://www.google.com/finance/quote/${stock}`,
  name,
  price,
  priceMovement: {
    percentage,
    value,
    movement: parseFloat(value) === 0 ? undefined : value[0] === "-" ? "Down" : "Up",
  },
};
if (result[region]) result[region].push(index);
else result[`${region}`] = [index];
return result;

接下来,我们从markets字符串中删除解析部分:

markets.match(marketsRegionPattern).forEach((el) => (markets = markets.replace(el, "")));

然后,我们重复采用不同的货币,加密和期货市场的正则义务模式的解析:

//https://regex101.com/r/vu6yI2/1
const marketsCurrenciesAndCryptoPattern =
  /\[null,\[\["\/g\/.+?null,"(?<name>[^":]+)",\d.+?\[(?<price>\d[^,]+),(?<value>[^,]+),(?<percentage>[^,]+).+?null,"(?<stock>[^"]+)/gm;

const marketsCurrenciesAndCrypto = [...markets.matchAll(marketsCurrenciesAndCryptoPattern)].reduce((result, { groups }) => {
  const { name, price, value, percentage, stock } = groups;
  const index = {
    stock,
    link: `https://www.google.com/finance/quote/${stock}`,
    name,
    price,
    priceMovement: {
      percentage,
      value,
      movement: parseFloat(value) === 0 ? undefined : value[0] === "-" ? "Down" : "Up",
    },
  };
  if (name.includes("(")) {
    if (result.crypto) result.crypto.push(index);
    else result.crypto = [index];
  } else {
    if (result.currencies) result.currencies.push(index);
    else result.currencies = [index];
  }
  return result;
}, {});

markets.match(marketsCurrenciesAndCryptoPattern).forEach((el) => (markets = markets.replace(el, "")));

//https://regex101.com/r/50gPod/1
const marketsFuturesPattern = /".+?",\d.+?\[(?<price>\d[^,]+),(?<value>[^,]+),(?<percentage>[^,]+).+?",null,"(?<stock>[^"]+)"\]\],"(?<name>[^"]+)/gm;

const marketsFutures = [...markets.matchAll(marketsFuturesPattern)].reduce((result, { groups }) => {
  const { name, price, value, percentage, stock } = groups;
  const index = {
    stock,
    link: `https://www.google.com/finance/quote/${stock}`,
    name,
    price,
    priceMovement: {
      percentage,
      value,
      movement: parseFloat(value) === 0 ? undefined : value[0] === "-" ? "Down" : "Up",
    },
  };
  if (result.futures) result.futures.push(index);
  else result.futures = [index];
  return result;
}, {});

之后,我们从不同市场制作marketsResults对象:

const marketsResults = { ...marketsRegions, ...marketsCurrenciesAndCrypto, ...marketsFutures };

接下来,要获取marketTrendsnewsdiscoverMore结果,我们需要使用下一个方法来获取页面的不同部分:

const marketTrends = Array.from($(".Sy70mc")).map((el) => ({
  title: $(el)
    .find(".r6XbWb")
    .contents()
    .filter((i, el) => el.nodeType === 3)
    .text(),
  link: `https://www.google.com/finance${$(el).find(".r6XbWb a").attr("href").slice(1)}`,
  results: Array.from($(el).find(".sbnBtf li")).map((el) => ({
    stock: $(el).find("a").attr("href").slice(8),
    link: `https://www.google.com/finance${$(el).find("a").attr("href").slice(1)}`,
    name: $(el).find(".ZvmM7").text(),
    price: $(el).find(".YMlKec").text(),
    priceMovement: {
      percentage: $(el).find(".JwB6zf").text(),
      value: $(el).find(".P2Luy").text(),
      movement: $(el).find(".NydbP").attr("aria-label").split(" ")[0],
    },
  })),
}));

const news = Array.from($(".yY3Lee")).map((el) => ({
  source: $(el).find(".sfyJob").text(),
  link: $(el).find(".z4rs2b a").attr("href"),
  date: $(el).find(".Adak").text(),
  snippet: $(el).find(".mRjSYb").text(),
  thumbnail: $(el).find(".a9REI").attr("src"),
  stocks: Array.from($(el).find(".wxtCmb")).map((el) => ({
    stock: $(el).attr("href").slice(8),
    link: `https://www.google.com/finance${$(el).attr("href").slice(1)}`,
    name: $(el).find(".X18JZ").text(),
    priceMovement: {
      percentage: $(el).find(".JwB6zf").text(),
      movement: $(el).find(".NydbP").attr("aria-label").split(" ")[0],
    },
  })),
}));

const discoverMore = Array.from($(".NBZP0e > div[role='listitem']")).map((el) => ({
  stock: $(el).find(".tOzDHb").attr("href").slice(8),
  link: `https://www.google.com/finance${$(el).find(".tOzDHb").attr("href").slice(1)}`,
  name: $(el).find(".RwFyvf").text(),
  price: $(el).find(".YMlKec").text(),
  priceMovement: {
    percentage: $(el).find(".JwB6zf").text(),
    movement: $(el).find(".NydbP").attr("aria-label").split(" ")[0],
  },
}));

最后,我们返回一个带有结果的新对象:

return { markets: marketsResults, marketTrends, news, discoverMore };

现在我们可以启动我们的解析器:

$ node YOUR_FILE_NAME # YOUR_FILE_NAME is the name of your .js file

输出

{
   "markets":{
      "America":[
         {
            "stock":".DJI:INDEXDJX",
            "link":"https://www.google.com/finance/quote/.DJI:INDEXDJX",
            "name":"Dow Jones Industrial Average",
            "price":"33745.69",
            "priceMovement":{
               "percentage":"0.5943158",
               "value":"199.3711",
               "movement":"Up"
            }
         },
        ...and other stocks
      ],
      "Europe":[
         {
            "stock":"DAX:INDEXDB",
            "link":"https://www.google.com/finance/quote/DAX:INDEXDB",
            "name":"DAX PERFORMANCE-INDEX",
            "price":"14431.86",
            "priceMovement":{
               "percentage":"1.1599331",
               "value":"165.48047",
               "movement":"Up"
            }
         },
        ...and other stocks
      ],
      "Asia":[
         {
            "stock":"NI225:INDEXNIKKEI",
            "link":"https://www.google.com/finance/quote/NI225:INDEXNIKKEI",
            "name":"Nikkei 225",
            "price":"27899.77",
            "priceMovement":{
               "percentage":"-0.11027624",
               "value":"-30.800781",
               "movement":"Down"
            }
         },
        ...and other stocks
      ],
      "currencies":[
         {
            "stock":"EUR-USD",
            "link":"https://www.google.com/finance/quote/EUR-USD",
            "name":"EUR / USD",
            "price":"1.0345499999999999",
            "priceMovement":{
               "percentage":"0",
               "value":"0",
               "movement":"undefined"
            }
         },
        ...and other stocks
      ],
      "futures":[
         {
            "stock":"YMW00:CBOT",
            "link":"https://www.google.com/finance/quote/YMW00:CBOT",
            "name":"Dow Futures",
            "price":"33766",
            "priceMovement":{
               "percentage":"0.5509068",
               "value":"185",
               "movement":"Up"
            }
         },
        ...and other stocks
      ]
   },
   "marketTrends":[
      {
         "title":"Americas",
         "link":"https://www.google.com/finance/markets/indexes/americas",
         "results":[
            {
               "stock":".INX:INDEXSP",
               "link":"https://www.google.com/finance/quote/.INX:INDEXSP",
               "name":"S&P 500",
               "price":"3,965.34",
               "priceMovement":{
                  "percentage":"0.48%",
                  "value":"+18.78",
                  "movement":"Up"
               }
            },
           ...and other stocks
         ]
      },
      ...and other regions
   ],
   "news":[
      {
         "source":"Investor's Business Daily",
         "link":"https://www.investors.com/market-trend/stock-market-today/dow-jones-futures-market-rally-faces-key-resistance-time-to-buy-apple-stock-or-short/",
         "date":"1 hour ago",
         "snippet":"Dow Jones Futures: Market Rally Faces Key Resistance; Time To Buy Apple \n""+""Stock — Or Short? | Investor's Business ...",
         "thumbnail":"https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcRVfTCLlCLoj0slVCJA8XImDiE6s4Tqf-nMDcrzDyvN6CePx9R0d0hDkE1gHNY",
         "stocks":[
            {
               "stock":".DJI:INDEXDJX",
               "link":"https://www.google.com/finance/quote/.DJI:INDEXDJX",
               "name":".DJI",
               "priceMovement":{
                  "percentage":"0.59%",
                  "movement":"Up"
               }
            },
            ...and other stocks
         ]
      },
    ...and other news
   ],
   "discoverMore":[
      {
         "stock":".INX:INDEXSP",
         "link":"https://www.google.com/finance/quote/.INX:INDEXSP",
         "name":"S&P 500",
         "price":"3,965.34",
         "priceMovement":{
            "percentage":"0.48%",
            "movement":"Up"
         }
      },
    ... and other stocks
   ]
}

usuingaoqian36从serpapi

本节是为了显示DIY解决方案与我们的解决方案之间的比较。

最大的区别是您不需要从头开始创建解析器并维护它。

也有可能在Google的某个时候阻止请求,我们在后端处理它,因此无需弄清楚如何自己做或弄清楚要使用哪个验证码,代理提供商。

首先,我们需要安装koude42

npm i google-search-results-nodejs

这是full code example,如果您不需要说明:

const SerpApi = require("google-search-results-nodejs");
const search = new SerpApi.GoogleSearch(process.env.API_KEY); //your API key from serpapi.com

const params = {
  engine: "google_finance_markets", // search engine
  trend: "indexes", // parameter is used for retrieving different market trends
  hl: "en", // parameter defines the language to use for the Google search
};

const getJson = () => {
  return new Promise((resolve) => {
    search.json(params, resolve);
  });
};

exports.getResults = async () => {
  const json = await getJson();
  delete json.search_metadata;
  delete json.search_parameters;
  return json;
};

getResults().then((result) => console.dir(result, { depth: null }));

代码说明

首先,我们需要从koude42库中声明SerpApi,并使用SerpApi的API键定义新的search实例:

const SerpApi = require("google-search-results-nodejs");
const search = new SerpApi.GoogleSearch(API_KEY);

接下来,我们为提出请求的必要参数编写:

const params = {
  engine: "google_finance_markets", // search engine
  trend: "indexes", // parameter is used for retrieving different market trends
  hl: "en", // parameter defines the language to use for the Google search
};

接下来,我们从Serpapi库中包装搜索方法,以便进一步处理搜索结果:

const getJson = () => {
  return new Promise((resolve) => {
    search.json(params, resolve);
  });
};

最后,我们声明了从每个页面获取数据并返回的函数getResult

const getResults = async () => {
  ...
};

在此功能中,我们获得了带有结果的json,删除search_metadatasearch_parameters,然后返回此json

const json = await getJson();
delete json.search_metadata;
delete json.search_parameters;
return json;

之后,我们运行getResults函数并使用koude52方法在控制台中打印所有接收的信息,该方法允许您使用带有必要参数的对象来更改默认输出选项:

getResults().then((result) => console.dir(result, { depth: null }));

输出

{
   "markets":{
      "us":[
         {
            "stock":".DJI:INDEXDJX",
            "link":"https://www.google.com/finance/quote/.DJI:INDEXDJX",
            "name":"Dow Jones",
            "price":33745.69,
            "price_movement":{
               "percentage":0.5943158,
               "value":199.3711,
               "movement":"Up"
            }
         },
         {
            "stock":".INX:INDEXSP",
            "link":"https://www.google.com/finance/quote/.INX:INDEXSP",
            "name":"S&P 500",
            "price":3965.34,
            "price_movement":{
               "percentage":0.47585818,
               "value":18.78003,
               "movement":"Up"
            }
         },
         {
            "stock":".IXIC:INDEXNASDAQ",
            "link":"https://www.google.com/finance/quote/.IXIC:INDEXNASDAQ",
            "name":"Nasdaq",
            "price":11146.0625,
            "price_movement":{
               "percentage":0.009910241,
               "value":1.1044922,
               "movement":"Up"
            }
         },
         {
            "stock":"RUT:INDEXRUSSELL",
            "link":"https://www.google.com/finance/quote/RUT:INDEXRUSSELL",
            "name":"Russell",
            "price":1849.7319,
            "price_movement":{
               "percentage":0.5768315,
               "value":10.608643,
               "movement":"Up"
            }
         },
         {
            "stock":"VIX:INDEXCBOE",
            "link":"https://www.google.com/finance/quote/VIX:INDEXCBOE",
            "name":"VIX",
            "price":23.12,
            "price_movement":{
               "percentage":3.3848703,
               "value":0.80999947,
               "movement":"Down"
            }
         }
      ],
      "europe":[
         {
            "stock":"DAX:INDEXDB",
            "link":"https://www.google.com/finance/quote/DAX:INDEXDB",
            "name":"DAX",
            "price":14431.86,
            "price_movement":{
               "percentage":1.1599331,
               "value":165.48047,
               "movement":"Up"
            }
         },
         {
            "stock":"UKX:INDEXFTSE",
            "link":"https://www.google.com/finance/quote/UKX:INDEXFTSE",
            "name":"FTSE 100",
            "price":7385.52,
            "price_movement":{
               "percentage":0.53058964,
               "value":38.97998,
               "movement":"Up"
            }
         },
         {
            "stock":"PX1:INDEXEURO",
            "link":"https://www.google.com/finance/quote/PX1:INDEXEURO",
            "name":"CAC 40",
            "price":6644.46,
            "price_movement":{
               "percentage":1.0392122,
               "value":68.33984,
               "movement":"Up"
            }
         },
         {
            "stock":"SX5E:INDEXSTOXX",
            "link":"https://www.google.com/finance/quote/SX5E:INDEXSTOXX",
            "name":"STOXX 50",
            "price":3924.84,
            "price_movement":{
               "percentage":1.1968834,
               "value":46.420166,
               "movement":"Up"
            }
         }
      ],
      "asia":[
         {
            "stock":"NI225:INDEXNIKKEI",
            "link":"https://www.google.com/finance/quote/NI225:INDEXNIKKEI",
            "name":"Nikkei 225",
            "price":27899.77,
            "price_movement":{
               "percentage":0.11027624,
               "value":30.800781,
               "movement":"Down"
            }
         },
         {
            "stock":"000001:SHA",
            "link":"https://www.google.com/finance/quote/000001:SHA",
            "name":"SSE",
            "price":3097.2432,
            "price_movement":{
               "percentage":0.5839201,
               "value":18.19165,
               "movement":"Down"
            }
         },
         {
            "stock":"HSI:INDEXHANGSENG",
            "link":"https://www.google.com/finance/quote/HSI:INDEXHANGSENG",
            "name":"HSI",
            "price":17992.54,
            "price_movement":{
               "percentage":0.29437047,
               "value":53.121094,
               "movement":"Down"
            }
         },
         {
            "stock":"SENSEX:INDEXBOM",
            "link":"https://www.google.com/finance/quote/SENSEX:INDEXBOM",
            "name":"SENSEX",
            "price":61663.48,
            "price_movement":{
               "percentage":0.14108542,
               "value":87.12109,
               "movement":"Down"
            }
         },
         {
            "stock":"NIFTY_50:INDEXNSE",
            "link":"https://www.google.com/finance/quote/NIFTY_50:INDEXNSE",
            "name":"NIFTY 50",
            "price":18307.65,
            "price_movement":{
               "percentage":0.19761337,
               "value":36.25,
               "movement":"Down"
            }
         }
      ],
      "currencies":[
         {
            "stock":"EUR-USD",
            "link":"https://www.google.com/finance/quote/EUR-USD",
            "name":"EUR / USD",
            "price":1.0345499999999999,
            "price_movement":{
               "percentage":0,
               "value":0
            }
         },
        ...and other stocks
      ],
      "crypto":[
         {
            "stock":"BTC-USD",
            "link":"https://www.google.com/finance/quote/BTC-USD",
            "name":"Bitcoin",
            "price":16561.8,
            "price_movement":{
               "percentage":0.7264880417191196,
               "value":121.20000000000073,
               "movement":"Down"
            }
         },
        ...and other stocks
      ],
      "futures":[
         {
            "stock":"YMW00:CBOT",
            "link":"https://www.google.com/finance/quote/YMW00:CBOT",
            "name":"Dow Futures",
            "price":33766,
            "currency":"USD",
            "price_movement":{
               "percentage":0.5509068,
               "value":185,
               "movement":"Up"
            }
         },
        ...and other stocks
      ]
   },
   "market_trends":[
      {
         "title":"Americas",
         "link":"https://www.google.com/finance/markets/indexes/americas",
         "serpapi_link":"https://serpapi.com/search.json?engine=google_finance_markets&hl=en&index_market=americas&trend=indexes",
         "results":[
            {
               "stock":".INX:INDEXSP",
               "link":"https://www.google.com/finance/quote/.INX:INDEXSP",
               "name":"S&P 500",
               "price":"3,965.34",
               "extracted_price":3965.34,
               "price_movement":{
                  "percentage":0.48,
                  "value":18.78,
                  "movement":"Up"
               }
            },
        ...and other stocks
         ]
      },
      ...and other regions
   ],
   "news_results":[
      {
         "source":"Investor's Business Daily",
         "link":"https://www.investors.com/market-trend/stock-market-today/dow-jones-futures-market-rally-faces-key-resistance-time-to-buy-apple-stock-or-short/",
         "date":"1 hour ago",
         "snippet":"Dow Jones Futures: Market Rally Faces Key Resistance; Time To Buy Apple Stock — Or Short? | Investor's Business ...",
         "thumbnail":"https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcRVfTCLlCLoj0slVCJA8XImDiE6s4Tqf-nMDcrzDyvN6CePx9R0d0hDkE1gHNY",
         "stocks":[
            {
               "name":"Dow Jones Industrial Average",
               "stock":".DJI",
               "price_movement":{
                  "percentage":0.59,
                  "movement":"Up"
               }
            },
            ...and other stocks
         ]
      },
        ...and other news
   ],
   "discover_more":[
      {
         "stock":".INX:INDEXSP",
         "link":"https://www.google.com/finance/quote/.INX:INDEXSP",
         "name":"S&P 500",
         "price":"3,965.34",
         "extracted_price":3965.34,
         "price_movement":{
            "percentage":0.48,
            "movement":"Up"
         }
      },
    ...and other stocks
   ]
}

链接

如果您想查看一些用Serpapi制造的项目,write me a message


加入我们的Twitter | YouTube

添加一个Feature Requestð«或Bugð