Im trying to scrape a div class but everything I have tried has failed so far :(
Im trying to scrape the element(s):
<a href="http://www.bellator.com/events/d306b5/bellator-newcastle-pitbull-vs-
scope"><div class="s_buttons_button s_buttons_buttonAlt
s_buttons_buttonSlashBack">More info</div></a>
from the website: http://www.bellator.com/events
I tried accessing the list of elements by doing
Elements elements = document.select("div[class=s_container] > li");
but that didnt return anything.
Then i tried accessing just the parent with
Elements elements = document.select("div[class=s_container]");
and that returned two div with classname "s_container", non of which is the one I needed :<
then i tried accessing that ones parent with
Elements elements = document.select("div[class=ent_m152_bellator module
ent_m152_bellator_V1_1_0 ent_m152]");
And that didnt return anything
I also tried
Elements elements = document.select("div[class=ent_m152_bellator]");
because I wasnt sure about the white spaces but it didnt return anything either
Then I tried accessing its parent by
Elements elements = document.select("div#t3_lc");
and that worked, but it returned an element containing
<div id="t3_lc">
<div class="triforce-module" id="t3_lc_promo1"></div>
</div>
which is kinda weird because i cant see that it has that child when i inspect the website in chrome :S
Anyone knows whats going on? I feel kinda lost..
What you see in your web browser is not what Jsoup sees. Disable JavaScript and refresh page to get what Jsoup gets OR press CTRL+U ("Show source", not "Inspect"!) in your browser to see original HTML document before JavaScript modifications. When you use your browser's debugger it shows final document after modifications so it's not not suitable for your needs.
It seems like whole "UPCOMING EVENTS" section is dynamically loaded by JavaScript.
Even more, this section is asynchronously loaded with AJAX. You can use your browsers debugger (Network tab) to see every possible request and response.
I found it but unfortunately all the data you need is returned as JSON so you're going to need another library to parse JSON.
That's not the end of the bad news and this case is more complicated. You could make direct request for the data:
http://www.bellator.com/feeds/ent_m152_bellator/V1_1_0/d10a728c-547e-4a6f-b140-7eecb67cff6b
but the URL seems random and few of these URLs (one per upcoming event?) are included inside JavaScript code in HTML.
My approach would be to get the URLs of these feeds with something like:
List<String> feedUrls = new ArrayList<>();
//select all the scripts
Elements scripts = document.select("script");
for(Element script: scripts){
if(script.text().contains("http://www.bellator.com/feeds/")){
// here use regexp to get all URLs from script.text() and add them to feedUrls
}
}
for(String feedUrl : feedUrls){
// iterate over feed URLs, download each of them
String json = Jsoup.connect(feedUrl).ignoreContentType(true).get().body().toString();
// here use JSON parsing library to get the data you need
}
ALTERNATIVE approach would be to stop using Jsoup because of its limitations and use Selenium Webdriver as it supports dynamic page modifications by JavaScript so you'd get the HTML of the final result - exactly what you see in web browser and Inspector.
If anyone finds this in the future; I managed to solve it with Selenium, dont know if its a good/correct solution but it seems to be working.
System.setProperty("webdriver.chrome.driver", "C:\\Users\\PC\\Desktop\\Chromedriver\\chromedriver.exe");
WebDriver driver = new ChromeDriver();
driver.get("http://www.bellator.com/events");
String html = driver.getPageSource();
Document doc = Jsoup.parse(html);
Elements elements = doc.select("ul.s_layouts_lineListAlt > li > a");
for(Element element : elements) {
System.out.println(element.attr("href"));
}
Output:
http://www.bellator.com/events/d306b5/bellator-newcastle-pitbull-vs-scope
http://www.bellator.com/events/ylcu8d/bellator-215-mitrione-vs-kharitonov
http://www.bellator.com/events/yk2djw/bellator-216-mvp-vs-daley
http://www.bellator.com/events/e8rdqs/bellator-217-gallagher-vs-graham
http://www.bellator.com/events/281wxq/bellator-218-sanchez-vs-grimshaw
http://www.bellator.com/events/8lcbdi/bellator-219-koreshkov-vs-larkin
http://www.bellator.com/events/9rqguc/bellator-macdonald-vs-fitch
Related
I need to scrape some URLs from some retailer product pages, but the specific URLs I need to get aren't in the html part of the page. The html looks like this for each of the items on which one would click to get to the page with the URL I need to grab:
<div id="name" class="hand bold" onclick="AVON.productcontrol.Go(45714);">ADVANCE TECHNIQUES Color Protection Conditioner Bonus Size</div>
I wrote the following to get URLs from the page, but since the actual URLs I need don’t seem to be stored in the page, it doesn’t get what I need:
def getUrls(URL):
"""input: product page url
output: list of urls to products
"""
connection = urllib.urlopen(URL)
dom = lxml.html.fromstring(connection.read())
selAnchor = CSSSelector('a')
foundElements = selAnchor(dom)
urlList = [e.get('href') for e in foundElements]
return urlList
Is there a way to get the link that the function after ‘onclick’ (I guess AVON.productcontrol.Go(#);) takes you to? I don’t fully understand html, and while I’ve read a bit about onclick, I can’t figure out how the function after 'onclick' works.
In order to find the URL that you are taken to on click, you need to find the JavaScript source code of the 'Go' function and read and understand it. It's buried somewhere within a tag or some JavaScript .js file that is referenced directly or indirectly by the HTML page. Happy digging!
Or: you automate the interaction with the web page with a tool like Selenium (http://docs.seleniumhq.org/) and just check where it takes you if you click.
Im trying to scrape data from this website: http://www.bundesliga.de/de/liga/tabelle/
In the source code i can see the tables but there's no content, just things like:
<td>[no content]</td>
<td>[no content]</td>
<td>[no content]</td>
<td>[no content]</td>
....
With firebug (F12 in Firefox) i wont see any content too but i can select the table and then copy the innerHTML via firebug option. In that case i get all the informations about the teams, but i dont know how to get the table with the content in Jsoup.
To get the value of an attribute, use the Node.attr(String key) method
For the text on an element (and its combined children), use Element.text()
For HTML, use Element.html(), or Node.outerHtml() as appropriate
For example:
String html = "<p>An <a href='http://example.com/'><b>example</b></a> link.</p>";
Document doc = Jsoup.parse(html);
Element link = doc.select("a").first();
String text = doc.body().text(); // "An example link"
String linkHref = link.attr("href"); // "http://example.com/"
String linkText = link.text(); // "example""
String linkOuterH = link.outerHtml();
// "<b>example</b>"
String linkInnerH = link.html(); // "<b>example</b>"
reference:
http://jsoup.org/cookbook/extracting-data/attributes-text-html
The table is not rendered on the server directly, but build by the client side JavaScript of the page and constructed with data that is getting to the client via AJAX. So what you get with the naive Jsoup approach is expected.
I see two possible solutions:
You analyze the network traffic and identify the ajax calls that the site is making. Then you try to reconstruct the format and fire the same requests as the JavaScript would. Then you can reconstruct the table.
you don't use Jsoup but a real browser, that loads the page and runs the JavaScript including all AJAX calls. You could use Selenium webdriver for that. There is a headless browser called phantomjs which has a relatively small footprint that you can use in combination with selenium webdriver.
both options have their (dis)advantages:
This takes more time, since you need to understand the network traffic pretty good. The reward will be a very fast and memory efficient scraper.
The programming of selenium is very easy and you should not have any difficulties achieving your goal. You don't need to understand the inner workings of the site you want to scrape. However, the price is a further dependency in your project. Memory consumption is high. Another process runs. The scraping will be slow.
Maybe you find another source with the soccer table that is holding the infos you want? That might be the easiest. For example http://www.fussballdaten.de/bundesliga/
That title is probably a bit confusing so let me elaborate.
I'm using HTML_purifier to clean up user input, although in this case the only user who will be using it will be myself (its in password protected folders). A long story short I would like to be able to add in image tag code to a web form, then on the page that it sends too use the code to display said image.
However i need the image tag to have css attributes added to it, one of which is
display:block
Anyway by default HTML_purifier removes this, detailed here because of the CSS.allowTricky option. As i understand it if you set the CSS.allowTricky option to True, then it should allow
display:block
However after doing this its still removing it, just wondering if anybody has done this before as i can't find much documentation about it on the web? Its not generating any errors in syslog, so im assuming that its the correct implementation but isn't working as expected.
My code at the moment.
include('HTMLPurifier.standalone.php');
$config = HTMLPurifier_Config::createDefault();
$config->set('CSS.AllowTricky', true);
* UPDATE **
The code should pass the config object (which the code already set) to the html purifier object. Putting it together it should look something like this.
include('HTMLPurifier.standalone.php');
$config = HTMLPurifier_Config::createDefault();
$config->set('CSS.AllowTricky', true);
$purifier = new HTMLPurifier($config);
Duplicate of http://htmlpurifier.org/phorum/read.php?3,6724 (solution was passing the config object to the HTML Purifier object so that the config actually got applied.)
For some reason, it seems like IE9 (I believe IE8 too, but not sure), is injecting
<SCRIPT LANGUAGE=VBScript>on error resume next pluginFound = IsObject(CreateObject("DIFFERENT PLUGIN EVERY TIME"))
in the middle of my content without any regards to surrounding context. This means it gets added in the middle of an attribute, or in the middle of some JavaScript, causing the HTML to be malformed and causing all sorts of problems.
This happens on multiple computers with different plugins, so it's not machine specific. And it's also not consistent: the location in which the offending script gets injected varies, the offending script varies. Sometimes you'll get several page loads without a problem and then you'll get the broken HTML.
My page is using a fair amount of JS, but nothing crazy. It's currently using jQuery, Google Maps, Bootstrap, Google Tag Manager, and loading a couple of Twitter, Google+, Facebook Iframes with their own little JS snippets. So, there are some asynchronous callbacks happening, but I wouldn't think this would interfere with how the browser renders the DOM and when it decides to inject plugin code.
You can see the problem if you reload http://www.rew.ca/properties/search/839721 enough times. If you scroll to the bottom of the page, you'll see raw JSON, or sometimes just some random HTML snippet will show in the middle of the page (because of mismatched tags).
Any ideas of why these scripts get injected arbitrarily and how to work around that?
Thanks
[UPDATE]
Here's another example of the script tags getting included in the middle of HTML content:
In my opinion, the problem lies in the http://cn.clickable.net/js/cct.js script, and specifically in the IsIEPlugin method of the __cct_tracker class:
this.IsIEPlugin = function (e) {
var t = !1;
return document.write('<SCRIPT LANGUAGE=VBScript>\n on error resume next \n pluginFound = IsObject(CreateObject("' + e + '")) </SCR' + "IPT>\n"), t ? 1 : 0
}
This method is called several times, with different arguments:
this.pixelRequestParams.cctDir = this.IsIEPlugin("SWCtl.SWCtl.1"),
this.pixelRequestParams.cctFlashPlugin = this.IsIEPlugin("ShockwaveFlash.ShockwaveFlash.1");
if (this.IsIEPlugin("PDF.PdfCtrl.1") == 1 ||
this.IsIEPlugin("PDF.PdfCtrl.5") == 1 ||
this.IsIEPlugin("PDF.PdfCtrl.6") == 1)
this.pixelRequestParams.cctPdf = 1;
this.pixelRequestParams.cctQuickTime = this.IsIEPlugin("Quicktime.Quicktime"),
this.pixelRequestParams.cctRealPlayer = this.IsIEPlugin("rmocx.RealPlayer G2 Control.1"),
this.pixelRequestParams.cctWmPlayer = this.IsIEPlugin("wmplayer.ocx")
I suppose (even I'm not sure) that "cct" stands for "Clickable Conversion Tracking", so it must be some sort of tracking code.
After further investigations, I've determined that the "cct.js" script gets loaded by the Google Tag Manager (GTM) script http://www.googletagmanager.com/gtm.js.
So, if I'm not wrong, by removing the GTM code snippet from your HTML page, you should be able to solve the problem.
It seems that the VBSCRIPT code dynamically injected by the GTM JavaScript code sometimes is not executed, but don't ask me why, since I don't really know.
I want to write a Chrome extension that looks at the HTML of the page its on, and if it finds eg <div id="hello"> then it will output, as a HTML list in the popup, 'This page has a friendly div' and if it finds eg I am married to a banana then it will output 'This guy is weird.'
So in other words, searching for specific stuff in the DOM and outputting messages in the popup depending on what it finds.
I had a look at Google Chrome Extension - Accessing The DOM for accessing the dom but I'm afraid I don't really understand it. Then of course there will be traversing the dom and presumably using regex and then conditional statements.
Well that stackoverflow question asked how to let your extension talk to the DOM. There are numerous ways, one way is through chrome.tabs.executeScript, and another way is through Message Passing as I explained in that question.
Back to your question, you could use XPath to search within the DOM. It is pretty powerful. For example you said you want to search for <div id="hello">, you can do it like this:
var nodes = document.evaluate("//div[#id='hello']", document, null,
XPathResult.ANY_TYPE, null)
var resultNode = nodes.iterateNext()
if (resultNode) {
// Found the first node. Output its contents.
alert(resultNode.innerHTML);
}
Now for your second example, same thing ..
I am married to a banana
var nodes = document.evaluate("//a[#href='http://bananas.com']/text()[contains(.,'married')]",
document, null,
XPathResult.ANY_TYPE, null)
var resultNode = nodes.iterateNext()
if (resultNode) {
// Found the first node. Output its contents.
alert('This guy is weird');
}
Well you could use XPath which does work perfectly in Chrome, and you can make your query simple such as finding nodes that you want or even complex with detail. You can query any node, and then do post processing if you wish as well.
Hope that helped. Remember all this should be within a content script in the Chrome Extension. And if you want your extension to communicate to that, you can use Message Passing as I explained in the other post. So basically, within your popup.html, you send a request to the content script to find you text. Your content script will send back a response from its callback. To send the request, you should use chrome.tabs.sendRequest and within the content script.You listen for that request and handle it. As I explained in the other stackoverflow question.
Do NOT use regular expressions to parse HTML. The <center> cannot hold.
With that out of the way... although you can use XPath, I think querySelector is similar in power while being somewhat simpler as well.
You simply pass a CSS selector as a string, and it returns the elements that match the selector. Kinda like using jQuery without needing to load the jQuery library.
Here's how you would use it:
var query = document.querySelector("div#hello");
if (query) {
alert("This page has a friendly div");
}
var query = document.querySelectorAll("a[href='http://bananas.com']");
for (var i = 0; i < query.length; i += 1) {
if (query[i].textContent === "I am married to a banana") {
alert("This guy is weird.");
return;
}
}
document.querySelector finds only a single element, and returns null if that element is not found.
document.querySelectorAll returns a fake-array of elements, or an empty fake-array if none are found.
...however, it sounds like you're wanting to update the browser action popup when something is detected in a webpage, correct? If so, that is possible but immensely more difficult.
Mohamed Mansour's post will get you to the point where you can communicate between content scripts and the background page/popup, but there are other bits that need to be done as well.
Unless the problem is more complex than I think, why not just use jQuery or other convenient js api for this? This is what they were made for - to traverse the dom easily. You can inject jquery and your script that will be using it into required pages in manifest:
"content_scripts": [ {
"js": [ "jquery.js", "script.js" ],
"matches": [ "http://*/*", "https://*/*" ]
}]