VBA Scraping Tables in JS - javascript

i tried to gather every possible solution to get my code working, but unfortunately I am stuck.
I want to scrape the values from two different graphs, which are generated from JavaScript in the background.
https://us.soccerway.com/matches/2019/12/14/england/premier-league/liverpool-fc/watford-football-club/3029238/
The section I am interested is actually the two graphs in the middle of the page.
"The general game stats chart" and the chart pie to the right.
the html code is presented below:
<div class="block clearfix block_match_stats_plus_chart-wrapper" id="page_match_1_block_match_stats_plus_chart_15-wrapper">
<h2>General Game Stats Chart</h2>
<div class="content ">
<div class="block_match_stats_plus_chart real-content clearfix " id="page_match_1_block_match_stats_plus_chart_15">
<iframe src='/charts/statsplus/3029238/' style='width: 550px; height: 300px; overflow-y: hidden;' frameborder='no' allowtransparency="true" scrolling="no"> </iframe>
</div>
</div>
</div>
<script type="text/javascript" charset="utf-8">
(function() {
var block = new Block('page_match_1_block_match_stats_plus_chart_15', 'block_match_stats_plus_chart', null);
TimestampFormatter.format('page_match_1_block_match_stats_plus_chart_15');
})();

Using a MSXML request should work. Loading only the page with the chart is probably the fastest way to get that information. It looks like you just need what appears to be the game ID 3029238 in order to generate the chart at https://us.soccerway.com/charts/statsplus/
Option Explicit
Sub Scrape_Stats()
Dim Clip As Object: Set Clip = CreateObject("new:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
Dim ws As Worksheet: Set ws = ThisWorkbook.Worksheets("Sheet1")
Dim html As Object: Set html = CreateObject("htmlfile")
Dim Text As String
With CreateObject("MSXML2.XMLHTTP")
.Open "GET", "https://us.soccerway.com/charts/statsplus/3029238/", False
.Send
While .readyState <> 4: DoEvents: Wend
html.body.InnerHTML = .responseText
Text = html.body.getElementsByTagName("Table")(0).outerhtml
End With
Clip.SetText Text
Clip.PutInClipboard
ws.Range("A2:H1000").ClearContents
ws.Range("A2").Select
ws.PasteSpecial Format:="Unicode Text"
Set Clip = Nothing
End Sub

You are looking at the "Match" page while the charts actually loaded from an iFrame
This is the source you should be looking at: https://us.soccerway.com/charts/statsplus/3029238/
And this is the JavaScript function you should scrape the data from
<script type="text/javascript" charset="utf-8">
window.addEvent('domready', function() {
var chart = new PieChart('Possession');
chart.addSeries('Possession', [{"name":"Watford FC","y":39},{"name":"Liverpool FC","y":61,"sliced":true}]);
chart.highChartsOptions.plotOptions.pie.topmargin = '20';
chart.highChartsOptions.plotOptions.pie.size = "80%";
chart.highChartsOptions.plotOptions.pie.center = ['50%', '55%'];
chart.render('page_chart_1_chart_statsplus_1_chart_possession_1-wrapper');
});
</script>

Related

Captain.speak() method issue

I am currently learning about objects within class. I created an object with constructor notation in Javascript, and instantiated four different objects with distinct names. For some reason, when I try to run the Captain.speak() method in my code, it doesn't work. It should display the Captain.strPhase string that I created right before initiating the command for the function. When I check this in online compilers, there are no errors, but it doesn't output my string. Would anyone happen to know why?
$(document).ready(function() {
function Pirate(rank, phrase, id) {
output = "";
randNum = 1;
secretNum = 1;
this.strRank = rank;
this.intNum = favnum;
this.strPhrase = phrase;
this.elOutput = document.getElementById(id);
this.speak = function() {
this.elOutput.innerHTML += "<br>" + this.strPhrase;
}; //End speak
this.chooseRandNum = function() {
this.randNum = Math.floor(Math.random() * 10);
}; //End chooseRandNum
}; //End Pirate
var Captain = new Pirate("Captain", "", "captain");
var firstMate = new Pirate("First Mate", "I love guessing games!", "pirate1");
var Quartermaster = new Pirate("Quartermaster", "This game should be fun.", "pirate2");
var Gunner = new Pirate("Gunner", "Let's start playing!", "pirate3");
Captain.strPhrase = "Argh maties, ready to play a guessing game?";
Captain.speak();
}); // end of $(document).ready()
<html lang="en">
<head>
<meta charset="utf-8">
<!-- Begin every html page with everything up to this point (just use your own header block) -->
<!-- Also, feel free to remove all the instructional comments as you modify this file to make it yours. -->
<!-- This <title> displays in the page tab -->
<title>Randomness</title>
<!-- This will link to your CSS stylesheet for formatting as soon as you create the file. The page will work without it, though. -->
<link rel="stylesheet" href="css/myFancyStylesheet.css">
<!-- This links to the jQuery library so your js code will work
Always include this *before* your own js code (extremely important) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!-- This links to the js code specific for this page -->
<script src="Randomness.js"></script>
</head>
<body>
Captain's Guessing Game:
<br></br>
<div id="captain">
</div>
<br></br>
<div id="pirate1">
</div>
<br></br>
<div id="pirate2">
</div>
<br></br>
<div id="pirate3">
</div>
</body>
</html>
When I run your code, I get an error message:
Uncaught ReferenceError: favnum is not defined
If you comment out this line...
// this.intNum = favnum;
...everything should work just fine.

javascript how to make all strings italic after hyphen?

Good morning to all
I have a question related to my big commerce products title. here I need the first part of the product's title in bold and after the hyphen or dash the second part need in italic. But problem is that the products title comes with one global variable %%GLOBAL_ProductName%% which I cannot make separated with the span tag. so can you suggest me how I can achieve the rest of strings after hyphen show in Italics with the help of javascript?
For example, check this screenshot https://www.screencast.com/t/fKy0FhByzzl
and here is big commerce website http://rp-staging2.mybigcommerce.com/categories
<li class="%%GLOBAL_AlternateClass%%">
<div class="ProductImage" data-product="%%GLOBAL_ProductId%%">
%%GLOBAL_ProductThumb%%
</div>
<div class="OutOfStockMessage InfoMessage" style="%%GLOBAL_ItemSoldOut%%">
%%SNIPPET_SideAddItemSoldOut%%
</div>
<div class="ProductActionAdd" onclick="location.href='%%GLOBAL_ProductLink%%';">
<p>%%GLOBAL_ProductName%%
</p>
<p><em class="p-price">%%GLOBAL_ProductPrice%% USD</em>
</p>
%%GLOBAL_ProductAddText%%
</div>
</li>
%%GLOBAL_ProductName%%
this variable showing products name please check screenshot and website i have provided link
Using some of the cool es6 features (array destructuring and template literals)
$(".pname").each(function () {
[beforeDash, afterDash] = $(this).text().split(" - ");
$(this).html(`${beforeDash} - <i>${afterDash}</i>`);
});
Looks like:
And if you are using jQuery in your website, you can use something like this:
$( window ).on( "load", function(){
var text = $('.text');
var x = text.text().split('-');
text.html(`${x[0]} - <i>${x[1]}<i>`);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="text">
Hello - World
</div>
When ever possible do this kind of split at the server side. Because client side you will manipulate strings after loading the page. So it is not good to do at client side. But anyhow I have written jquery code to fulfill your requirement. I have written in a click event for demo purpose. Please do the logic on onload event.
$("#btn").click(function(){
$(".productName").each(function(){
var title = $(this).text();
var firstSentence = "<b>"+title.substr(0,title.indexOf('-'))+"</b>";
var secondSentence = "<i>"+title.substr(title.indexOf('-')+1)+"</i>";
var finalTitle = firstSentence+ "-" + secondSentence;
$(this).html(finalTitle);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<a class="productName"> Sample1 - Product Name1</a><br>
<a class="productName"> Sample2 - Product Name2</a><br>
<input id="btn" type="button" value="Change Format">
</body>
</html>
Check this if it helps...
https://jsfiddle.net/Lz8p11mc/1/
You need to split your product name with '-' and then add these isolated names in separate spans and then you can style these spans as you want. I have written code for simple test case , you can modify it as per your requirement.
<html>
<script>
var productName = 'ABC-XYZ';
var separatedNames = productName.split('-');
var firtsName = separatedNames[0];
var secondname = separatedNames[1];
window.onload = function() {
//when the document is finished loading, replace everything
//between the <a ...> </a> tags with the value of splitText
document.getElementById("myTag").innerHTML = '<span>'+firtsName+'</span>-<span class="secondnameCls">'+secondname+'</span>';
}
</script>
<body>
<li class="%%GLOBAL_AlternateClass%%">
<p><a id='myTag'></a></p>
</li>
</body>
</html>

Print whole HTML page including pdf file inside iframe

I've got an assignment to embed relatively small pdf file inside html page and print the entire html pade including the pdf file inside the iframe.
Here is the structure of my html page:
Here is my code:
#media print{
body * {display:block;}
.toPrint{display:block; border:0; width:100%; min-height:500px}
<body>
<button onclick="window.print()">Print</button>
<h3>MUST BE PRINTED</h3>
<p> MUST BE PRINTED</p>
<iframe class="toPrint" src="https://nett.umich.edu/sites/default/files/docs/pdf_files_scan_create_reducefilesize.pdf" style="width:100%; height:97vh;"></iframe>
<h3>MUST BE PRINTED</h3>
<p> MUST BE PRINTED</p>
</body>
Currently I'm printing the page using css #media query. But unfortunately this media query prints the pdf's first page only.
What can I do print the entire pdf file?
The reason it's not printing the whole PDF is because it's in an iframe and the height is fixed. In order to print the whole PDF you'll need the iframe height to match its content height (there should be no scrollbar on the iframe).
Another option is to print only the iframe. Add id to your iFrame:
<iframe id="toPrint" class="toPrint"></iframe>
Focus the iframe and print its content:
var pdfFrame = document.getElementById("toPrint").contentWindow;
pdfFrame.focus();
pdfFrame.print();
Try this, it includes some JS but thats always good.
HTML:
<body>
<h3>MUST BE PRINTED</h3>
<p> MUST BE PRINTED</p>
<div id="pdfRenderer"></div>
<h3>MUST BE PRINTED</h3>
<p> MUST BE PRINTED</p>
</body>
JS:
var pdf = new PDFObject({
url: "https://nett.umich.edu/sites/default/files/docs/pdf_files_scan_create_reducefilesize.pdf",
id: "pdfRendered",
pdfOpenParams: {
view: "FitH"
}
}).embed("pdfRenderer");
This should work. Let me now if i doesnt
Use https://github.com/itext/itextsharp itextsharp or https://github.com/MrRio/jsPDF jsPDF. It is easy to use by plugin
I used Mozilla's pdf.js to solve my problem.
It renders the pdf file on html5 canvas thus it allows to print the whole html page as required.
Here is the code (credit):
<html>
<body>
<script type="text/javascript" src="https://raw.github.com/mozilla/pdf.js/gh-pages/build/pdf.js"></script>
<script type="text/javascript">
function renderPDF(url, canvasContainer, options) {
var options = options || { scale: 1 };
function renderPage(page) {
var viewport = page.getViewport(options.scale);
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
canvas.height = viewport.height;
canvas.width = viewport.width;
canvasContainer.appendChild(canvas);
page.render(renderContext);
}
function renderPages(pdfDoc) {
for(var num = 1; num <= pdfDoc.numPages; num++)
pdfDoc.getPage(num).then(renderPage);
}
PDFJS.disableWorker = true;
PDFJS.getDocument(url).then(renderPages);
}
</script>
<h3>MUST BE PRINTED</h3>
<p> MUST BE PRINTED</p>
<div id="holder"></div>
<h3>MUST BE PRINTED</h3>
<p> MUST BE PRINTED</p>
<script type="text/javascript">
renderPDF('yourFile.pdf', document.getElementById('holder'));
</script>
</body>
</html>
Here is a simpler solution using javascript.
<body>
<h3 id='first_h3'>MUST BE PRINTED</h3>
<p id ='first_paragraph'> MUST BE PRINTED</p>
<div id="pdfRenderer"></div>
<h3 id='second_h3'>MUST BE PRINTED</h3>
<p id='second_paragraph'> MUST BE PRINTED</p>
<input type="submit" value="Print All"
onclick="javascript:printPage()"
/>
</body>
<script>
pages =[] // initiate an empty list here
function printPage() {
var first_h3 = document.getElementById('first_h3');
// get the first h3 tag and
// then push its innerHTML into the list
pages.push(first_h3.innerHTML);
var first_paragraph = document.getElementById('first_paragraph');
// get the first paragraph and
// then push its innerHTML into the list
pages.push(first_paragraph.innerHTML);
var pdfRenderer = document.getElementById('pdfRenderer');
// get the pdfRenderer and
// then push its innerHTML into the list
pages.push(pdfRenderer.contentWindow.document.body.innerHTML);
var second_h3 = document.getElementById('second_h3');
// get the second h3 tag and
// then push its innerHTML into the list
pages.push(second_h3.innerHTML);
var second_paragraph = document.getElementById('second_paragraph');
// get the second paragraph and
// then push its innerHTML into the list
pages.push(second_paragraph.innerHTML);
if (pages && pages.length) {
// this if statement, just checks to ensure our list is not empty before running the code.
// here is the magic, we now set the parent window to be equal to all the concatenated innerHTML
window.content.document.body.innerHTML = pages;
// then we print this new window that contains all the different parts in the exact order we pushed them into the list.
window.print();
}
else {
// do nothing
}
}
</script>
With this solution avoid the problem of the iframe being cut off if it exceeds one page. Secondly, you get only one print dialogue box even if you have multiple iframes.

Editable cshtml View Page save as PDF

I'm trying to create web application that consisting an
editable cshtml view page
and then
save that edited content as a PDF once I click the button
So this is the whole cshtml view page ,
#model IEnumerable<int>
#{
ViewBag.Title = "Create Template";
}
<!DOCTYPE html>
<html>
<head>
<title>Create a Template</title>
</head>
<body>
<div id='template'>
<h1 contentEditable='True'>Product Name</h1>
<div >
<h2 contentEditable='True'>Product Description</h2>
<p contentEditable='True'>London is the capital city of England.</p>
</div>
</div>
<button id="btn_sumbit" type="button" class="btn btn-danger submit">Save as PDF</button>
</body>
</html>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
#Scripts.Render("~/bundles/jqueryui")
<script type="text/javascript">
$('#btn_sumbit').on('click', function () {
var div_value = document.getElementById('template').innerHTML;
RazorPDF.PdfResult(div_value, "PDF");
});
</script>
}
I'm using RazorPDF to do this task , but once I click this button it doesn't saving to PDF
I can edit this cshtml view page , I want to save that finally edited content as pdf (which is dynamic view)
I think you're doing it wrong. The line "RazorPDF.PdfResult(...)" belongs in the Controller not in the View.
Watch this Video: http://nyveldt.com/blog/post/Introducing-RazorPDF
It should give you a clearer understanding of how things work in RazorPDF.
EDIT:
Just create a Controller method, that generates a PDF-View based on Parameters.
public ActionResult Pdf(string name, string description) {
var product = new Product();
product.Name = name;
product.Description = description;
var pdfResult = new PdfResult(product, "Pdf");
return pdfResult;
}
Of course you need to create a Product-Class that holds the information.
In your Javascript part you can write:
location.href = #Url.Action("Pdf", "Controllername") + "?name=" + name + "&description=" + description;
This will hopefully generate a link that you can follow in Javascript. name and description are Javascript variables that hold the information that was typed by the user.
Do you understand? My approach would be to generate the PDF content in a different view (like in that video) based on the information of your editable view.
Tell me if it worked. ;-)

I'm trying to dynamically move elements up and down on my webpage. How do I do that?

I have a webpage that has a list of iframe soundcloud music "boxes" that play music and I want to add buttons to the side of these iframes to move the boxes up and down based on people voting on them. Kinda like what reddit has. (I'm doing this for fun and have no intention of publishing this) I tried absolute positioning the iframes and then using javascript to move the up or down but realized that would be a disaster to try to code. What would be the best way of going about this? I'm relatively new to web development.
(I used bootstrap to style the webpage)
Here is the body of the html doc:
<body>
<div class="page-header" class="stayPut"><div class="stayPut" id="Header"><img src="logo.png" ALT="Hamiltron" WIDTH=300 HEIGHT=61/></div></div>
<div class="container">
<div class="jumbotron"><h1 style="text-align: center; color: white;">An Ultimate Music List.</h1></div>
<div class="jumbotron">
<iframe id="box1" width="100%" height="150" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/113414910&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&visual=true"></iframe>
<iframe id="box2" width="100%" height="150" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/180568985&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&visual=true"></iframe>
<iframe id="box3" width="100%" height="150" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/161660686&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&visual=true"></iframe>
</div>
</div>
<script>
document.getElementById("box1").style["position"]="absolute";
document.getElementById("box1").style["top"] = "20px";
document.getElementById("box1").style["width"] = "75%";
</script>
<script src="http://code.jquery.com/jquery.min.js"></script>
<script src="js/bootstrap.min.js"></script>
I'd love to help so I'll show you the best way I'd do it. You will need to enable voting and a database, even just a 1 table data base with values such as
ID | VoteCount | TRACKID
0 3 180568985
If I where you I'd work on this first and then assign buttons to increase the vote count in the database. I'd do this with Ajax to poll a PHP file to connect to the database so your page doesn't reload after each vote!
Then you will need to use Javascript or get the items from the database in order when you poll them. I've created an example that will display them for you:
Working Example of Following Code | JSFiddle.com
//This is a JSON Object that can be assessed like dataFromDatabaseExample[2].vote = 100
//NOTE THESE ARE NOT IN THE RIGHT ORDER ;)
var dataFromDatabaseExample = [{id: 0, vote: 43, songID: 113414910},
{id: 1, vote: 5, songID: 180568985},
{id: 2, vote: 100, songID: 161660686}];
//Creates an event listener to listen for if someone clicks the refresh button
$('#refresh').on('click',function(){
refreshData();
});
//Function that holds main data that can be run whenever
function refreshData(){
//Clears the bit of the page where the votes are going to be ie RESET
$('#soundCloudItems').html("");
//Sort the values to be in order using our custom sorting function compareVotes
var songs = dataFromDatabaseExample.sort( compareVotes );
//For every item we got
for(var i=0;i<songs.length;i++){
//Title it (Optional but shows vote count)
$('#soundCloudItems').append("Votes: "+songs[i].vote+"<br>");
//Display the soundcloud box
$('#soundCloudItems').append(getSongCode(songs[i].id,songs[i].vote,songs[i].songID));
}
}
//Run on Load to display some data
refreshData();
//TWO HELPER FUNCTIONS ///////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////
//Compares two items from the array, for the vote count
function compareVotes(a,b) {
if (a.vote > b.vote)
return -1;
if (a.vote < b.vote)
return 1;
return 0;
}
//Just returns all the bulky code in a nice form as a piece of HTML/Text
function getSongCode(id,vote,SongId){
return '<iframe data-voteCount="'+id+'" id="soundcloud'+id+'" width="100%" height="150" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/'+SongId+'&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&visual=true"></iframe>';
}

Categories