郑州汉狮做网站的大公司,海尔网站建设,学seo建网站,共和网站建设公司最近有小伙伴问我能不能抓取同花顺的数据#xff0c;最近股票行情还不错#xff0c;想把数据抓下来自己分析分析。我大A股#xff0c;大家都知道的#xff0c;一个概念火了#xff0c;相应的股票就都大涨。
如果能及时获取股票涨跌信息#xff0c;那就能在刚开始火起来的…最近有小伙伴问我能不能抓取同花顺的数据最近股票行情还不错想把数据抓下来自己分析分析。我大A股大家都知道的一个概念火了相应的股票就都大涨。
如果能及时获取股票涨跌信息那就能在刚开始火起来的时候杀进去小赚一笔。但是股票那么多小伙伴也盯不过来于是就微信问我能不能抓取同花顺的板块下的股票信息存到数据库里他就能根据数据库里的数据制定一些策略。俗话说哪里有痛点哪里就有编程不就是个同花顺嘛办他
调研背景
于是我点开了同花顺的板块页面http://q.10jqka.com.cn/gn/ 发现有好268个概念分析概念板块的网页HTML发现268个概念的URL就在HTML中打开其中的“阿里巴巴概念”发现网页又有分页分页的数据是根据接口实时获取的接口中注入了一些Cooki信息和其他标识同花顺的反爬虫策略一直比较强使用模拟接口的方式可能难度会比较大所以使用selenium模拟浏览器操作这种方式比较完美。
设计方案
技术方向有了再简单整理一下思路根据http://q.10jqka.com.cn/gn/获取板块网页的源码HTML用Jsoup解析HTML获取每个概念的url信息放到List中
遍历List根据概念的url获取概念网页源码HTML解析股票信息
再递归点击执行“下一页”操作获取每一页的股票数据直至尾页
把股票信息存储到数据库
配置环境
先介绍下工程所需要的环境编码工具idea 语言java 依赖jdk1.8、maven、chrome、ChromeDriver
我们使用的方案是模拟浏览器的操作所以我们需要在电脑安装chrome浏览器和chromedriver驱动。chrome的安装这里就不说了百度下载个浏览器就行。
关键是安装 ChromeDriver 需要安装和当前chrome版本一致的驱动才写。
查看chrome版本chrome浏览器输入Chrome://version在根据版本下载对于的驱动版本最好要一致比如我的是79.0.3945.117 (正式版本) 64 位我下载的就是 79.0.3945.36。
ChromeDriver各版本的下载地址
下面这一步可做可不做不做也能启动工程只是需要修改代码中的一个配置即可。配置方式
将下载好的ChromeDriver文件放到/usr/local/bin/目录下
shell cp chromedriver /usr/local/bin/
检测是否安装成功
shell chromedriver --version
如果不配置只需要记得修改ChromeDriver在代码中配置的路径你只需要将路径改为你自己的ChromeDriver路径即可比如我的是
System.setProperty(
webdriver.chrome.driver,
/Users/admin/Documents/selenium/chrome/79.0.3945.36/chromedriver
);
记得修改代码里ChromeDriver的路径。 记得修改代码里ChromeDriver的路径。 记得修改代码里ChromeDriver的路径。
验证方案
首先完成设计方案中的三步
package com.ths.controller;
import com.ths.service.ThsGnCrawlService;
import com.ths.service.ThsGnDetailCrawlService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.List;
Controller
public class CrawlController {
Autowired
private ThsGnCrawlService thsGnCrawlService;
Autowired
private ThsGnDetailCrawlService thsGnDetailCrawlService;
RequestMapping(/test)
ResponseBody
public void test() {
// 抓取所有概念板块的url List list thsGnCrawlService.ThsGnCrawlListUrl();
// 放入阻塞队列 thsGnDetailCrawlService.putAllArrayBlockingQueue(list);
// 根据url多线程抓取 thsGnDetailCrawlService.ConsumeCrawlerGnDetailData(1);
}
}
先看看thsGnCrawlService.ThsGnCrawlListUrl();方法如何抓取所有概念板块的url
package com.ths.service.impl;
import com.ths.parse.service.ThsParseHtmlService;
import com.ths.service.ThsGnCrawlService;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
Service
public class ThsGnCrawlServiceImpl implements ThsGnCrawlService {
private final static Logger LOGGER LoggerFactory.getLogger(ThsGnCrawlServiceImpl.class);
/*** 同花顺全部概念板块url*/
private final static String GN_URL http://q.10jqka.com.cn/gn/;
Autowired
private ThsParseHtmlService thsParseHtmlService;
Override
public List ThsGnCrawlListUrl() {
System.setProperty(webdriver.chrome.driver, /Users/admin/Documents/selenium/chrome/79.0.3945.36/chromedriver);
ChromeOptions options new ChromeOptions();
//是否启用浏览器界面的参数 //无界面参数// options.addArguments(headless); //禁用沙盒 就是被这个参数搞了一天// options.addArguments(no-sandbox); WebDriver webDriver new ChromeDriver(options);
try {
// 根据网速设置网速慢可以调低点 webDriver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
webDriver.get(GN_URL);
Thread.sleep(1000L);
String gnWindow webDriver.getWindowHandle();
// 获取同花顺概念页面的HTML String thsGnHtml webDriver.getPageSource();
LOGGER.info(获取同花顺url:[{}]的html为:/n{}, GN_URL, thsGnHtml);
return thsParseHtmlService.parseGnHtmlReturnGnUrlList(thsGnHtml);
} catch (Exception e) {
LOGGER.error(获取同花顺概念页面的HTML出现异常:, e);
} finally {
webDriver.close();
webDriver.quit();
}
return null;
}
}
这里使用了上文说的ChromeDriver我们需要根据自己的配置修改对应的地址重复第四遍。 根据代码可以看到String thsGnHtml webDriver.getPageSource();方法获取页面的HTML再解析HTML就能获取各大概念板块的url。
解析HTML我使用的是Jsoup简单易上手api也很简单解析HTML获取各大板块的url的代码如下
package com.ths.parse.service.impl;
import com.ths.parse.service.ThsParseHtmlService;
import org.jsoup.Jsoup;
import org.jsoup.helper.StringUtil;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
Service
public class ThsParseHtmlServiceImpl implements ThsParseHtmlService {
/*** 解析同花顺概念板块的Html页面http://q.10jqka.com.cn/gn/* 返回所有概念板块的url地址*/
public List parseGnHtmlReturnGnUrlList(String html) {
if (StringUtil.isBlank(html)) {
return null;
}
List list new ArrayList();
Document document Jsoup.parse(html);
Elements cateItemsFromClass document.getElementsByClass(cate_items);
for (Element element : cateItemsFromClass) {
Elements as element.getElementsByTag(a);
for (Element a : as) {
String gnUrl a.attr(href);
String name a.text();
HashMap map new HashMap();
map.put(url, gnUrl);
map.put(gnName, name);
list.add(map);
}
}
return list;
}
}
可以看到只要在html中有的数据定位到标签就能获取对应的数据。
然后放到阻塞队列
/*** 阻塞队列*/
private ArrayBlockingQueue arrayBlockingQueue new ArrayBlockingQueue(1000);
Override
public void putAllArrayBlockingQueue(List list) {
if (!CollectionUtils.isEmpty(list)) {
arrayBlockingQueue.addAll(list);
}
}
再开启多个线程从阻塞队列里获取url分别抓取概念板块的股票数据如果页面有分页就循环点击下一页再获取数据直到尾页代码如下
package com.ths.service.impl;
import com.ths.dao.StockThsGnInfoDao;
import com.ths.domain.StockThsGnInfo;
import com.ths.service.ThsGnDetailCrawlService;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
Service
public class ThsGnDetailCrawlServiceImpl implements ThsGnDetailCrawlService {
private final static Logger LOGGER LoggerFactory.getLogger(ThsGnDetailCrawlServiceImpl.class);
/*** 阻塞队列*/
private ArrayBlockingQueue arrayBlockingQueue new ArrayBlockingQueue(1000);
Autowired
private StockThsGnInfoDao stockThsGnInfoDao;
Override
public void putAllArrayBlockingQueue(List list) {
if (!CollectionUtils.isEmpty(list)) {
arrayBlockingQueue.addAll(list);
}
}
Override
public void ConsumeCrawlerGnDetailData(int threadNumber) {
for (int i 0; i threadNumber; i) {
LOGGER.info(开启线程第[{}]个消费, i);
new Thread(new crawlerGnDataThread()).start();
}
LOGGER.info(一共开启线程[{}]个消费, threadNumber);
}
class crawlerGnDataThread implements Runnable {
Override
public void run() {
try {
while (true) {
Map map arrayBlockingQueue.take();
String url map.get(url);
String gnName map.get(gnName);
String crawlerDateStr new SimpleDateFormat(yyyy-MM-dd HH:00:00).format(new Date());
//chromederiver存放位置 System.setProperty(webdriver.chrome.driver, /Users/admin/Documents/selenium/chrome/79.0.3945.36/chromedriver);
ChromeOptions options new ChromeOptions();
//无界面参数 // options.addArguments(headless); //禁用沙盒 就是被这个参数搞了一天 // options.addArguments(no-sandbox); WebDriver webDriver new ChromeDriver(options);
try {
webDriver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
webDriver.get(url);
Thread.sleep(1000L);
String oneGnHtml webDriver.getPageSource();
LOGGER.info(当前概念[{}],html数据为[{}], gnName, oneGnHtml);
LOGGER.info(oneGnHtml);
// TODO 解析并存储数据 parseHtmlAndInsertData(oneGnHtml, gnName, crawlerDateStr);
clicktoOneGnNextPage(webDriver, oneGnHtml, gnName, crawlerDateStr);
} catch (Exception e) {
LOGGER.error(用chromerDriver抓取数据出现异常url为[{}],异常为[{}], url, e);
} finally {
webDriver.close();
webDriver.quit();
}
}
} catch (Exception e) {
LOGGER.error(阻塞队列出现循环出现异常:, e);
}
}
}
public void parseHtmlAndInsertData(String html, String gnName, String crawlerDateStr) {
Document document Jsoup.parse(html);
// Element boardElement document.getElementsByClass(board-hq).get(0);// String gnCode boardElement.getElementsByTag(h3).get(0).getElementsByTag(span).get(0).text();
Element table document.getElementsByClass(m-pager-table).get(0);
Element tBody table.getElementsByTag(tbody).get(0);
Elements trs tBody.getElementsByTag(tr);
for (Element tr : trs) {
try {
Elements tds tr.getElementsByTag(td);
String stockCode tds.get(1).text();
String stockName tds.get(2).text();
BigDecimal stockPrice parseValueToBigDecimal(tds.get(3).text());
BigDecimal stockChange parseValueToBigDecimal(tds.get(4).text());
BigDecimal stockChangePrice parseValueToBigDecimal(tds.get(5).text());
BigDecimal stockChangeSpeed parseValueToBigDecimal(tds.get(6).text());
BigDecimal stockHandoverScale parseValueToBigDecimal(tds.get(7).text());
BigDecimal stockLiangBi parseValueToBigDecimal(tds.get(8).text());
BigDecimal stockAmplitude parseValueToBigDecimal(tds.get(9).text());
BigDecimal stockDealAmount parseValueToBigDecimal(tds.get(10).text());
BigDecimal stockFlowStockNumber parseValueToBigDecimal(tds.get(11).text());
BigDecimal stockFlowMakertValue parseValueToBigDecimal(tds.get(12).text());
BigDecimal stockMarketTtm parseValueToBigDecimal(tds.get(13).text());
// 存储数据 StockThsGnInfo stockThsGnInfo new StockThsGnInfo();
stockThsGnInfo.setGnName(gnName);
stockThsGnInfo.setGnCode(null);
stockThsGnInfo.setStockCode(stockCode);
stockThsGnInfo.setStockName(stockName);
stockThsGnInfo.setStockPrice(stockPrice);
stockThsGnInfo.setStockChange(stockChange);
stockThsGnInfo.setStockChangePrice(stockChangePrice);
stockThsGnInfo.setStockChangeSpeed(stockChangeSpeed);
stockThsGnInfo.setStockHandoverScale(stockHandoverScale);
stockThsGnInfo.setStockLiangBi(stockLiangBi);
stockThsGnInfo.setStockAmplitude(stockAmplitude);
stockThsGnInfo.setStockDealAmount(stockDealAmount);
stockThsGnInfo.setStockFlowStockNumber(stockFlowStockNumber);
stockThsGnInfo.setStockFlowMakertValue(stockFlowMakertValue);
stockThsGnInfo.setStockMarketTtm(stockMarketTtm);
stockThsGnInfo.setCrawlerTime(crawlerDateStr);
stockThsGnInfo.setCrawlerVersion(同花顺概念板块# crawlerDateStr);
stockThsGnInfo.setCreateTime(new Date());
stockThsGnInfo.setUpdateTime(new Date());
stockThsGnInfoDao.insert(stockThsGnInfo);
} catch (Exception e) {
LOGGER.error(插入同花顺概念板块数据出现异常:, e);
}
}
}
public BigDecimal parseValueToBigDecimal(String value) {
if (StringUtils.isEmpty(value)) {
return BigDecimal.ZERO;
} else if (--.equals(value)) {
return BigDecimal.ZERO;
} else if (value.endsWith(亿)) {
return new BigDecimal(value.substring(0, value.length() - 1)).multiply(BigDecimal.ONE);
}
return new BigDecimal(value);
}
public boolean clicktoOneGnNextPage(WebDriver webDriver, String oneGnHtml, String key, String crawlerDateStr) throws InterruptedException {
// 是否包含下一页 String pageNumber includeNextPage(oneGnHtml);
if (!StringUtils.isEmpty(pageNumber)) {
WebElement nextPageElement webDriver.findElement(By.linkText(下一页));
webDriver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
nextPageElement.click();
Thread.sleep(700);
String nextPageHtml webDriver.getPageSource();
LOGGER.info(下一页);
LOGGER.info(nextPageHtml);
// TODO 解析并存储数据 parseHtmlAndInsertData(nextPageHtml, key, crawlerDateStr);
clicktoOneGnNextPage(webDriver, nextPageHtml, key, crawlerDateStr);
}
return true;
}
public String includeNextPage(String html) {
Document document Jsoup.parse(html);
List list document.getElementsByTag(a);
for (Element element : list) {
String a element.text();
if (下一页.equals(a)) {
String pageNumber element.attr(page);
return pageNumber;
}
}
return null;
}
}
最后对概念板块的页面数据进行解析入库。
数据展示如果遇到问题可以关注我的公众号java之旅或扫描下方二维码回复【加群】加我个人微信询问我