package hk.reality.stock.service.fetcher; import hk.reality.stock.model.StockDetail; import hk.reality.stock.service.exception.DownloadException; import hk.reality.stock.service.exception.ParseException; import java.io.IOException; import java.math.BigDecimal; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang.StringUtils; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.HttpGet; import org.apache.http.util.EntityUtils; import org.htmlcleaner.TagNode; import org.htmlcleaner.XPatherException; public class EtnetQuoteFetcher extends BaseQuoteFetcher { private static final String BASE_URL = "http://www.etnet.com.hk/www/tc/stocks/quote.php?code=%s"; private static final String XPATH_UPDATE = "//div/div[1]/table[1]/tbody/tr/td[2]/span"; private static final String XPATH_BASE = "//div/div/table[3]/tbody/tr[2]/td/table"; private static final String XPATH_NAME = "//table/tbody/tr/td[1]/table/tbody/tr/td[2]"; private static final String XPATH_PRICE = "//table/tbody/tr[2]/td[1]"; private static final String XPATH_PRICE_CHANGE = "//table/tbody/tr[2]/td[2]"; private static final String REGEXP_PRICE_CHANGE = "(-?[0-9\\.]+).+\\((-?[0-9\\.]+)%\\)"; private static final Pattern PATTERN_PRICE_CHANGE = Pattern.compile(REGEXP_PRICE_CHANGE); private static final String XPATH_DAY_HIGH = "//tr[1]/td[2]/table/tbody/tr[2]/td/span"; private static final String XPATH_DAY_LOW = "//tr[2]/td[1]/table/tbody/tr[2]/td/span"; private static final String XPATH_DAY_VOLUME = "//tr[1]/td[3]/table/tbody/tr[2]/td/span"; private static final String DATE_FORMAT = "dd/MM/yyyy HH:mm"; @Override public StockDetail fetch(String quote) { StockDetail detail = new StockDetail(); String url = getUrl(quote); HttpGet req = new HttpGet(url); try { detail.setQuote(quote); detail.setSourceUrl(url); // download html HttpResponse resp = getClient().execute(req); String html = EntityUtils.toString(resp.getEntity()); // optimization to reduce html size int start = html.indexOf("<!-- Content -->"); int end = html.indexOf("top:-1000px;\">"); html = StringUtils.substring(html, start, end); TagNode document = getCleaner().clean(html); resp = null; // set updatedAt SimpleDateFormat formatter = new SimpleDateFormat(DATE_FORMAT); String updatedAtStr = getFirstMatchedElementContent(document, XPATH_UPDATE); Date updatedDate = formatter.parse(updatedAtStr); Calendar updatedAt = Calendar.getInstance(); updatedAt.setTime(updatedDate); detail.setUpdatedAt(updatedAt); TagNode table = getFirstMatchedElement(document, XPATH_BASE); // set price String pricesStr = getFirstMatchedElementContent(table, XPATH_PRICE); BigDecimal price = new BigDecimal(pricesStr); detail.setPrice(price); // set price change and change % String priceChangesStr = getFirstMatchedElementContent(table, XPATH_PRICE_CHANGE); Matcher priceChangeMatcher = PATTERN_PRICE_CHANGE.matcher(priceChangesStr); if (priceChangeMatcher.find()) { String priceChangeNumStr = priceChangeMatcher.group(1); BigDecimal priceChangeNum = new BigDecimal(priceChangeNumStr); detail.setChangePrice(priceChangeNum); String priceChangePercentStr = priceChangeMatcher.group(2); BigDecimal priceChangePercent = new BigDecimal(priceChangePercentStr); detail.setChangePricePercent(priceChangePercent); } String dayHighStr = getFirstMatchedElementContent(table, XPATH_DAY_HIGH); BigDecimal dayHigh = new BigDecimal(dayHighStr); detail.setDayHigh(dayHigh); String dayLowStr = getFirstMatchedElementContent(table, XPATH_DAY_LOW); BigDecimal dayLow = new BigDecimal(dayLowStr); detail.setDayLow(dayLow); String volume = getFirstMatchedElementContent(table, XPATH_DAY_VOLUME); detail.setVolume(volume); } catch (ClientProtocolException e) { throw new DownloadException("error fetching stock", e); } catch (IOException e) { throw new DownloadException("error fetching stock", e); } catch (XPatherException e) { throw new ParseException("unexpected result while fetch stock", e); } catch (java.text.ParseException e) { throw new ParseException("date format unparsable", e); } return detail; } @Override public String getUrl(String quote) { return String.format(BASE_URL, quote); } }