簡單介紹下功能
1.每隔一段時間(例如1分鐘)在京東手機每日一秒殺頁面提取產品(手機)連結。
http://sale.360buy.com/act/8VTHFGr10CjMDyZ.html#01
2.根據提取到得產品連結給後台發送數據,以便獲取產品價格,描述,折扣,庫存(是否有貨)等資訊。
3.根據所得到的信息,進行判斷。
若符合條件自動呼叫瀏覽器(前提是chrome加入環境變數,或改程式碼將瀏覽器.exe路徑加入程式碼,修改程式)開啟產品訂購頁面。
4.其實也就解決了一個問題:不用自己頻繁的刷新網頁了,不用自己查看;
登陸,提交訂單還得瀏覽器解決(似乎這幾個功能點比較複雜,沒做處理)
程式做的不太完善:
運作前需要修改幾個地方:
1.環境變數:chrome 加入瀏覽器變量,以便呼叫。 。或自己修改原始碼用其它方式打開。
2.活動中每個產品的價格資訊需要設定。這個比較不好,必須修改原始碼。
修改的地方在filter()這個函數裡。
3.另外一個需要修改的地方是
hasStore(String skuidkey)
address="http://price.360buy.com/stocksoa/StockHandler.ashx?callback=getProvinceStockCallback&type=pcastock&skuid="+skuidkey+"&provinceid=1&cityid=2800&areaid=2850";
這個地方的cityid=2800&areaid=...地裡位置資訊。這個沒做處理。需要從手機產品頁自己搞。
其實也比較簡單。 chrome+F12 ,修改“城市”,區域等資訊後,會看到一個get請求發送到後台,這個連結裡麵包含了需要的資訊。 (http://price.360buy.com/stocksoa/StockHandler.ashx?callback=getProvinceStockCallback&type=pcastock&skuid=64EBD0F20F593D95C72C6EED59B64658&provinceid=158280280280280280802804)。
Util.java
複製代碼代碼如下:
package view.Util;
import java.util.ArrayList;
public class Util {
public static void print(Object o){
System.out.print(o);
}
public static void println(Object o){
if(null==o)
System.out.println();
else
System.out.println(o);
}
public static ArrayList<Integer> toArrayList(int[] ints){
if(ints.length==0)
return null;
ArrayList<Integer> al=new ArrayList<Integer>();
for(int i=0;i<ints.length;i++){
al.add(ints[i]);
}
return al;
}
}
Miaosha360buy.java
複製代碼代碼如下:
package jingdong;
public class Miaosha360buy {
java.util.concurrent.CountDownLatch t= new java.util.concurrent.CountDownLatch(1);
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName() + "開始");
Miaosha360buy ms360=new Miaosha360buy();
new ThreadOne360buy(ms360.t).start();
while(true){
try {
ms360.t.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
Thread.sleep(1000*60);//間隔1分鐘呼叫一次?
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ms360.t=new java.util.concurrent.CountDownLatch(1);
new ThreadOne360buy(ms360.t).start();
System.out.println("New Tread in while..");
}
}
}
Miaosha360buy.java
複製代碼代碼如下:
package jingdong;
public class Miaosha360buy {
java.util.concurrent.CountDownLatch t= new java.util.concurrent.CountDownLatch(1);
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName() + "開始");
Miaosha360buy ms360=new Miaosha360buy();
new ThreadOne360buy(ms360.t).start();
while(true){
try {
ms360.t.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
Thread.sleep(1000*60);//間隔1分鐘呼叫一次?
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ms360.t=new java.util.concurrent.CountDownLatch(1);
new ThreadOne360buy(ms360.t).start();
System.out.println("New Tread in while..");
}
}
}
ThreadOne360buy.java
複製代碼代碼如下:
package jingdong;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import view.Util.Util;
public class ThreadOne360buy extends Thread{
java.util.concurrent.CountDownLatch c;
ArrayList al;//記錄秒殺產品頁面
float price=0.0f;//商品價格
float discount=0.0f;//商品折扣
//用來保存執行緒訊息,在這個專案裡用處不大
private static List<Thread> runningThreads = new ArrayList<Thread>();
//這個是計數器(不太會用,線程這方面一直感覺是比較複雜的)
public ThreadOne360buy(java.util.concurrent.CountDownLatch c) {
this.c=c;
}
@Override
public void run() {
regist(this);// 線程開始時註冊
// 列印開始標記
System.out.println(Thread.currentThread().getName() + "開始...");
try {
//抓取京東手機秒殺頁面
this.getMessage("http://sale.360buy.com/act/8VTHFGr10CjMDyZ.html#01");
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
c.countDown();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
c.countDown();
}
c.countDown();
unRegist(this);// 線程結束時取消註冊
// 列印結束標記
System.out.println(Thread.currentThread().getName() + "結束.");
}
public void regist(Thread t) {
synchronized (runningThreads) {
runningThreads.add(t);
}
}
public void unRegist(Thread t) {
synchronized (runningThreads) {
runningThreads.remove(t);
}
}
public static boolean hasThreadRunning() {
// 透過判斷runningThreads是否為空就能知道是否還有執行緒未執行完
return (runningThreads.size() > 0);
}
/**
* 從手機秒殺頁面取得prodcut連結,product skuid,skuidkey,price,store訊息
* @param url:手機秒殺頁面
* @throws ClientProtocolException
* @throws IOException
*/
public void getMessage(String url) throws ClientProtocolException, IOException{
al=getMainUrl(down(url));
Util.println(al);
if(al.size()==0){
c.countDown();
System.exit(0);
return;
}
for(int i=0;i<al.size();i++){
StringBuffer sb=new StringBuffer();
StringBuffer openUrl = new StringBuffer();
openUrl.append("http://www.360buy.com/product/");
openUrl.append(al.get(i).toString().subSequence(al.get(i).toString().lastIndexOf('/')+1, al.get(i).toString().lastIndexOf( '.')));
openUrl.append(".html");
//557673
sb.append("http://d.360buy.com/fittingInfo/get?skuId=");
sb.append(al.get(i).toString().subSequence(al.get(i).toString().lastIndexOf('/')+1, al.get(i).toString().lastIndexOf( '.')));
sb.append("&callback=Recommend.cbRecoFittings");
Util.println(sb.toString());
//map中保存的是產品name,price,折扣訊息
Util.println("Al("+i+") down:"+sb.toString());
HashMap<String, String> hm=parseProduct(down(sb.toString()));
//用來匹配價格資訊。匹配庫存信息
filter(hm,openUrl.toString());//過濾價格,如果條件符合就開啟瀏覽器
}
}
/**
* 一個驗證方法
* @param hm 保存著價格訊息
* @param url 產品頁面
*/
public void filter(HashMap<String, String> hm,String url){//url既是產品頁面
//view.Util.oenCMD.openWinExe(null,url);
//是不是應該先檢查庫存?
String skuidkey=parseSkuidkey(url);
if(!hasStore(skuidkey)){
Util.println("-------------------------------------");
Util.println("沒有庫存了!");
Util.println("-------------------------------------");
//減掉計數,以便主執行緒判斷
c.countDown();
//應該結束子執行緒哦?
return;
}
if(hm.get("skuid").equals("201602")){//判斷//摩托羅拉skuid=201602
//這裡的價格是寫死了,運作前得改過來才行。
this.setPrice(499.0f);
//是不是應該開啟控制台?
if(Float.parseFloat(hm.get("price"))<=this.getPrice()){
view.Util.oenCMD.openWinExe(null,url);
}
}else if(hm.get("skuid").equals("675647")){//天語skuid=675647
////這裡的價格是寫死了,運作前得改過來才行。
//this.setPrice(699.0f);
////是不是應該打開控制台?
//if(Float.parseFloat(hm.get("price"))<=this.getPrice()){
//view.Util.oenCMD.openWinExe(null,url);
//}
}
}
/**
* 解析了產品頁面中name,skuid,price訊息
* @param doc
* @return
*/
public static HashMap<String, String> parseProduct(Document doc){
String text=doc.text();
String docc=text.substring(text.indexOf("master")+9,text.indexOf("fittings")-3).replaceAll("[//s]", "");
String[] ss=docc.split(",");
HashMap<String, String> hm=new HashMap<String, String>();
for(String it: ss){
String string=it.replaceAll("/"", "");
if(string.contains("//u"))
string=unicodeDecode(string);
String[] str=string.split(":");
hm.put(str[0], str[1]);
}
Util.println(hm);
return hm;
}
/**
* 處理unicode字符,轉換成顯示字符(漢字),不太通用
* @param it: /u6a5d
* @return
*/
public static String unicodeDecode(String it){//有個缺點,就是前面的字元無法移除
Util.println(it);
String regex="(////u[0-9a-f]{4})";
Pattern pt= Pattern.compile(regex);
Matcher mc;
StringBuffer sb;
StringBuffer sba=new StringBuffer();
mc=pt.matcher(it);
while(mc.find()){
sb=new StringBuffer();
mc.appendReplacement(sba,sb.append((char )Integer.parseInt((mc.group(1).substring(2)), 16)).toString());
}
return sba.toString();
}
/**
* 傳回文檔物件(下載內容)
* @param url 下載頁面
* @return
* @throws ClientProtocolException
* @throws IOException
*/
public static Document down(String url) throws ClientProtocolException, IOException{
Document doc = null;
DefaultHttpClient httpClient=new DefaultHttpClient();
Util.println("DownLoad:"+url);
HttpGet get=new HttpGet(url);
HttpResponse response;
response = httpClient.execute(get);
HttpEntity entity = response.getEntity();
doc = Jsoup.parse(entity.getContent(), "utf-8","");
//釋放資源
EntityUtils.consume(entity);
//關閉連線
httpClient.getConnectionManager().shutdown();
return doc;
}
/**
* 加入了編碼控制訊息
* @param url 待下載頁面
* @param code 編碼
* @return
* @throws ClientProtocolException
* @throws IOException
*/
public static Document down(String url,String code) throws ClientProtocolException, IOException{
Document doc = null;
DefaultHttpClient httpClient=new DefaultHttpClient();
Util.println("DownLoad:"+url);
HttpGet get=new HttpGet(url);
HttpResponse response;
response = httpClient.execute(get);
HttpEntity entity = response.getEntity();
doc = Jsoup.parse(entity.getContent(), code,"");
//釋放資源
EntityUtils.consume(entity);
//關閉連線
httpClient.getConnectionManager().shutdown();
return doc;
}
/**
* 用來解析秒殺頁面中的產品(收集)鏈接
* @param doc
* @return
*/
public static ArrayList<String> getMainUrl(Document doc){
if(doc.equals("")||doc==null)
return null;
try {
Thread.sleep(50);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
ArrayList<String> urls=new ArrayList<String>();
String rule="map[name=Map] >area[href~=product]";
/**
* 開始解析
*/
Elements elements=doc.select(rule);
for (Element e : elements) {
//Util.println(e.absUrl("abs:href"));
urls.add(e.absUrl("abs:href"));
}
return urls;
}
/**
* 取得skuidkey,用於查詢商品庫存信息
* @param url
* @return
*/
public static String parseSkuidkey(String url){
Document doc=null;
try {
doc=down(url,"gb2312");
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//Util.println(doc.select("script"));
String text = null;
for(Element e : doc.select("script")){
if(e.data().contains("skuidkey:")){
text=e.data();
break;
}
}
//skuidkey:'7D45919EA8242511DAA5CC7C6D7B351C'
text=text.substring(text.indexOf("skuidkey:")+10, text.indexOf("skuidkey:")+42);
Util.println("---------------------------------");
Util.println(text);
return text;
}
/**
* 查看庫存信息
* @param skuidkey
* @return
*/
public static boolean hasStore(String skuidkey){//這個地方沒有處理,直接提取瀏覽器中的信息
String address = null;
boolean hasStore=false;
if(skuidkey!=null && !"".equals(skuidkey))
address="http://price.360buy.com/stocksoa/StockHandler.ashx?callback=getProvinceStockCallback&type=pcastock&skuid="+skuidkey+"&provinceid=1&cityid=2800&areaid=2850";
else{
Util.println("解析skuidkey錯誤");
}
try {
if(parseStore(down(address))){
hasStore=true;
}
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return hasStore;
}
/* if(array[1]=="34"||array[1]=="18"){
changeCart(false);
djdarea.stockInfoDom.html("<strong class='store-over'>無貨</strong>");
}
else if(array[1]=="0"){
changeCart(false);
djdarea.stockInfoDom.html("<strong class='store-over'>無貨</strong>");
}
else if(array[2]=="0"&&array[4]!="2"){
changeCart(false);
djdarea.stockInfoDom.html("很抱歉,商品無法送至您選擇的區域");
}
else if(array[1]=="33"||array[1]=="5"){
changeCart(true);
djdarea.stockInfoDom.html("<strong>現貨</strong>"+(array[4]=="1"?",該區域"+(array[3]=="0"?"不":" ")+"支持貨到付款":"")+cashdesc);
}
else if(array[1]=="36"){
changeCart(true);
djdarea.stockInfoDom.html("<strong>預訂</strong>"+(array[4]=="1"?",該區域"+(array[3]=="0"?"不":" ")+"支持貨到付款":"")+cashdesc);
}
else if(array[1]=="39"){
changeCart(true);
djdarea.stockInfoDom.html("<strong>在途</strong>"+(array[4]=="1"?",該區域"+(array[3]=="0"?"不":" ")+"支持貨到付款":"")+cashdesc);
}
else if(array[1]=="40"){
changeCart(true);
djdarea.stockInfoDom.html("<strong>可配貨</strong>"+(array[4]=="1"?",該區域"+(array[3]=="0"?"不" :"")+"支持貨到付款":"")+cashdesc);
}
*/
/**
* 解析庫存訊息
* @param doc
* @return
*/
public static boolean parseStore(Document doc){
String text=doc.text();
String docc=text.substring(text.indexOf("-")-1,text.lastIndexOf(",")-1);
Util.println(docc);
String[] store=docc.split("-");
if(store[1].equals("34") || store[1].equals("18")){
//無貨
Util.println("此地無貨");
return false;
}else if(store[1].equals("33") || store[1].equals("5")){
//現貨
Util.println("此地現貨");
return true;
}
Util.println(store[1]);
return false;
}
//幾個bean方法
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public float getDiscount() {
return discount;
}
public void setDiscount(float discount) {
this.discount = discount;
}
}
oenCMD.java
複製代碼代碼如下:
package view.Util;
public class oenCMD {
//public static void main(String[] args) {
//// openWinExe(null);
// openExe(null,"http://www.baidu.com");
// }
//用Java 呼叫windows系統的exe文件,例如notepad,calc之類
public static void openWinExe(String command,String url) {
if(command==null ||command.equals("")){
command = "chrome "+url;
}
Runtime rn = Runtime.getRuntime();
Process p = null;
try {
p = rn.exec(command);
} catch (Exception e) {
System.out.println("Error win exec!");
}
}
//呼叫其他的可執行文件,例如:自己製作的exe,或是下載安裝的軟體.
public static void openExe(String pathAndName,String url) {
if(pathAndName==null || pathAndName.equals("")){
pathAndName="C://Users//Administrator//AppData//Local//Google//Chrome//Application//chrome.exe";
}
if(url!=null && !url.equals("")){
pathAndName+=" ";
pathAndName+=url;
}
Runtime rn = Runtime.getRuntime();
Process p = null;
try {
p = rn.exec(pathAndName);
} catch (Exception e) {
System.out.println("Error exec!");
}
}
}