JavaScript Snippet - eBay - Fetch Accurate "Sold" Amount And Update The Main Page

Posted at

Sold amount, shown in red, bold font-style is very useful,
but often inaccurate. I use this data in a script to sort the items by the amount sold (item with the largest amount is on the top of the list).
Often, the amount written is, also, inaccurate (I don't know why... :|)
anyway..



Running some quick JavaScript will help to put-things-into-order.

This script will fetch the target page (using native, quick Ajax),
read the accurate "sold" amount from it, and will update the accurate amount into the list,
this version only fetch/update the items with NO AMOUNT mentioned at all
(it will also updates the HTML structure in-case it is missing... :| )


NodeList.prototype.filter = Array.prototype.filter;
NodeList.prototype.forEach = Array.prototype.forEach;

function get(url, callback){
var xhr;

xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(ev){
var xhr = ev.target || ev.path[0];

console.log("onreadystatechange", xhr);

var response, responseURL;
if(XMLHttpRequest.DONE !== xhr.readyState){ console.log("not ready yet..["+xhr.readyState+"]"); return;} //not completed yet
if(200 !== xhr.status && 302 !== xhr.status){ console.log("SOFT ERROR: not 200/302..["+xhr.status+"]"); return;} //failed (cleanup) return false will stop the event.

setTimeout(function(){
console.log("calling callback with result.");
callback.apply(undefined,[xhr.response, xhr.getAllResponseHeaders(), xhr.responseURL]);
}, 20);
console.log("fetch success.");
};
xhr.open('POST', url, true);
xhr.setRequestHeader("X-Hello", "World!");
xhr.responseType = "document";
xhr.timeout = 30000;
//xhr.withCredentials = true;
xhr.overrideMimeType("text/html;charset=UTF-8;lang=en-US;language=English");
xhr.send();
}

var elements = document.querySelectorAll('[id^="ResultSetItems"] > ul[id^="ListViewInner"] > li[id^="item"]')
elements.forEach(function(element){ //unified missing HTML-structure.
var tmp = element.querySelector('.lvextras');
if(null === tmp.querySelector("div.hotness.bold div.hotness-signal.red"))
tmp.innerHTML = '<div class="hotness bold"><div class="hotness-signal red"></div></div>';
});

elements = elements.filter(function(element){ //leave only those with no "*** sold" data.
var tmp = element.querySelector(".lvextras div.hotness.bold div.hotness-signal.red").innerText.match(/sold/im);
console.log("no sold:",element);
return null === tmp;
});

elements.forEach(function(element){ //fetch page, update data (if exist)
var url = element.querySelector('.lvtitle a[href*="/itm/"]').href;
var target = element.querySelector(".lvextras div.hotness.bold div.hotness-signal.red");

console.log("fetching page for:",element);

get(url, function(response){
console.log("fetch success for:",element);

var tmp = response.querySelector('span.qtyTxt');
tmp = null === tmp ? "0" : tmp.innerText;
tmp = tmp.match(/(\\d+) sold/i);
tmp = null === tmp ? "0" : "undefined" === typeof tmp[1] ? "0" : tmp[1];

if("0" === tmp) console.log("could not find sold data, using 0 instead for",element);

target.innerHTML = tmp + " sold";

console.log("updated sold:", tmp);
});

});


I am using POST instead of GET to fetch the page, since amount of the arguments in the URL is huge there is no carry-data with this POST, just plain request..




This variation is probably a more advisable, it will fetch every page (not that bad decision as it sounds!)
and will update the amount in the search page.


NodeList.prototype.filter = Array.prototype.filter;
NodeList.prototype.forEach = Array.prototype.forEach;

function get(url, callback){
var xhr;

xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(ev){
var xhr = ev.target || ev.path[0];

console.log("onreadystatechange", xhr);

var response, responseURL;
if(XMLHttpRequest.DONE !== xhr.readyState){ console.log("not ready yet..["+xhr.readyState+"]"); return;} //not completed yet
if(200 !== xhr.status && 302 !== xhr.status){ console.log("SOFT ERROR: not 200/302..["+xhr.status+"]"); return;} //failed (cleanup) return false will stop the event.

setTimeout(function(){
console.log("calling callback with result.");
callback.apply(undefined,[xhr.response, xhr.getAllResponseHeaders(), xhr.responseURL]);
}, 20);
console.log("fetch success.");
};
xhr.open('POST', url, true);
xhr.setRequestHeader("X-Hello", "World!");
xhr.responseType = "document";
xhr.timeout = 30000;
//xhr.withCredentials = true;
xhr.overrideMimeType("text/html;charset=UTF-8;lang=en-US;language=English");
xhr.send();
}

var elements = document.querySelectorAll('[id^="ResultSetItems"] > ul[id^="ListViewInner"] > li[id^="item"]')
elements.forEach(function(element){ //unified missing HTML-structure.
var tmp = element.querySelector('.lvextras');
if(null === tmp.querySelector("div.hotness.bold div.hotness-signal.red"))
tmp.innerHTML = '<div class="hotness bold"><div class="hotness-signal red"></div></div>';
});

/*
elements = elements.filter(function(element){ //leave only those with no "*** sold" data.
  var tmp = element.querySelector(".lvextras div.hotness.bold div.hotness-signal.red").innerText.match(/sold/im);
  console.log("no sold:",element);
  return null === tmp;
});
*/

elements.forEach(function(element){ //fetch page, update data (if exist)
var url = element.querySelector('.lvtitle a[href*="/itm/"]').href;
var target = element.querySelector(".lvextras div.hotness.bold div.hotness-signal.red");

console.log("fetching page for:",element);

get(url, function(response){
console.log("fetch success for:",element);

var tmp = response.querySelector('span.qtyTxt');
tmp = null === tmp ? "0" : tmp.innerText;
tmp = tmp.match(/(\\d+) sold/i);
tmp = null === tmp ? "0" : "undefined" === typeof tmp[1] ? "0" : tmp[1];

if("0" === tmp) console.log("could not find sold data, using 0 instead for",element);

target.innerHTML = tmp + " sold";

console.log("updated sold:", tmp);
});

});


You can now use the following scripts at:
icompile- JavaScript Ninja – Crowd Wisdom eBay Enhancements
- and even skip the one that removed "unsold" items,
since sorting by "sold amount" will keep the most sold items on top and the least-sold items (zero is also an amount..) will kept at the bottom of the list, advisable to use the "fetch next page items script" first, this way your item-list will have more items to view/filter/organised at one time.





Here is an improved version,
where the sold amount is scraped from couple of places on the page,
the existing information (the item description) is then updated with an improved method,
code is also a bit refactored.


NodeList.prototype.filter = Array.prototype.filter;
NodeList.prototype.forEach = Array.prototype.forEach;

function get(url, callback){
var xhr;

xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(ev){
var xhr = ev.target || ev.path[0];

console.log("onreadystatechange", xhr);

var response, responseURL;
if(XMLHttpRequest.DONE !== xhr.readyState){ console.log("not ready yet..["+xhr.readyState+"]"); return;} //not completed yet
if(200 !== xhr.status && 302 !== xhr.status){ console.log("SOFT ERROR: not 200/302..["+xhr.status+"]"); return;} //failed (cleanup) return false will stop the event.

setTimeout(function(){
console.log("calling callback with result.");
callback.apply(undefined,[xhr.response, xhr.getAllResponseHeaders(), xhr.responseURL]);
}, 20);
console.log("fetch success.");
};
xhr.open('POST', url, true);
xhr.setRequestHeader("X-Hello", "World!");
xhr.responseType = "document";
xhr.timeout = 30000;
//xhr.withCredentials = true;
xhr.overrideMimeType("text/html;charset=UTF-8;lang=en-US;language=English");
xhr.send();
}

var elements = document.querySelectorAll('[id^="ResultSetItems"] > ul[id^="ListViewInner"] > li[id^="item"]')

elements.forEach(function(element){
var url = element.querySelector('.lvtitle a[href*="/itm/"]').href
,target = element.querySelector(".lvextras")
;

//if(null !== target.innerText.match(/ sold/i)) return; //commented: fetch every item in the list, uncommented: only items with no "** sold".
target.innerHTML = target.innerHTML.replace(/\\d+[\\s\\+]*sold/ig, "0 sold");

get(url, function(response){
var tmp = "0";

try{tmp = response.querySelector('#why2buy').innerText.match(/(\\d+)[\\s\\+]*sold/mi)[1]; }catch(err){}
if("0" === tmp){
try{tmp = response.querySelector('span.qtyTxt').innerText.match(/(\\d+)[\\s\\+]*sold/i)[1]; }catch(err){}
}

target.innerHTML = target.innerHTML.replace(/\\d+[\\s\\+]*sold/ig, "");
target.innerHTML +='<div style="color:#329900" class="hotness bold">' + tmp + ' sold</div>';

//console.log("updated sold:", tmp, "for:", element);
})
});


Enjoy!