PhantomJS: Alle Hyperlinks einer Website auflisten, mit Unterstützung für JavaScript und Frames

Friday, June 1, 2018

Das folgende PhantomJS-Skript listlinks.js, listet alle Hyperlinks einer Website auf, wobei JavaScript und Frames unterstützt werden.

Damit ist es z.B. möglich…

  • sich über Gratisaktionen von Steam Informieren lassen (Free Weekend/Free For a Limited Time)
  • Werbebanner von Google Ads/Ad Scence automatisiert anklicken

Hinweis: Mittlerweile auch als Projekt auf Github zu finden: https://github.com/1nn3/listlinks

#!/usr/bin/phantomjs
// listlinks.js
// Lists all hyperlinks of an URL, supports JavaScript and Frames
// See also: http://phantomjs.org/api/
"use strict";

// Um Endlosrekursion in der Frametiefe zu erkennen und abzufangen
// FIXME: Make it a constant
// const FRAME_RECURSION_MAX_DEPTH = 10;
// SyntaxError: Unexpected token 'const'
// See: https://github.com/ariya/phantomjs/issues/14521
var FRAME_RECURSION_MAX_DEPTH = 10;

function getAttributesFromAllElementsByTagName(tagName, attributeList) {
	var elements = window.document.getElementsByTagName(tagName);

	var values = [];
	for (var i = 0; i < elements.length; i++) {
		values.push({});

		for (var j = 0; j < attributeList.length; j++) {
			values[i][attributeList[j]] = elements[i].getAttribute(attributeList[j]) || "";
		}

		values[i]["textContent"] = elements[i].textContent || "";
	}
	return values;
}

function listLinks () {
	var a = page.evaluate(getAttributesFromAllElementsByTagName, "a", ["href"]);

	for (var i = 0; i < a.length; i++) {
		// FIXME: Use the class URL to get an absolut URL
		// See: https://github.com/ariya/phantomjs/issues/14349
		var href = new URL(a[i]["href"], page.url).href || a[i]["href"];
		var textContent = a[i]["textContent"].replace(/\s+/g, " ");
		system.stdout.writeLine(href + "\t" + textContent);
	}
}

function getFrames (parrents) {
	var a = page.evaluate(getAttributesFromAllElementsByTagName, "iframe", []);

	var frames = [];
	for (var i = 0; i < a.length; i++) {
		frames.push(parrents.concat([i])); // frame position
	}
	return frames;
}

function walkThroughFrames (frames) {
	for (var i = 0; i < frames.length; i++) {
		system.stderr.writeLine("I: Switching to frame: " + frames[i].toString() + " frameName=" + page.frameName + " frameURL=" + page.frameUrl);

		if (frames[i].length > FRAME_RECURSION_MAX_DEPTH) {
			system.stderr.writeLine("E: FRAME_RECURSION_MAX_DEPTH reached!");
			continue;
		}

		page.switchToMainFrame();
		for (var j = 0; j < frames[i].length; j++) {
			page.switchToFrame(frames[i][j]);
		}

		listLinks();
		walkThroughFrames(getFrames(frames[i])); // subframes
	}
}

var system = require('system');

if (system.args.length != 2) {
	system.stderr.writeLine("Usage: " + system.args[0] + " <url>");
	phantom.exit(1);
}

var page = require("webpage").create();
page.settings.javascriptEnabled = true;
page.settings.resourceTimeout = 10*1000; // milliseconds
page.settings.userAgent = "";

page.open(system.args[1], function (status) {
	if (status !== "success") {
		system.stderr.writeLine("E: Fail to load URL: " + system.args[1]);
		phantom.exit(1);
	}

	listLinks();
	walkThroughFrames(getFrames([]));
	phantom.exit(0);
});

Download: fp-content/attachs/listlinks.js

Zur Installation laden das Skript nach /usr/local/bin/ herunter und machen es ausführbar. Dies ist für die Beispiele unten notwendig!

# wget -O /usr/local/bin/listlinks https://0010100.net/blog/fp-content/attachs/listlinks.js
# chmod +x /usr/local/bin/listlinks

Benutzt wird es dann so:

$ listlinks <URL>

Sich über Gratisaktionen von Steam Informieren lassen (Free Weekend/Free For a Limited Time)

Mit dem Shell-Skript free-games-on-steam.sh, kann man sich über Gratisaktionen von Steam informieren lassen (Free Weekend/Free For a Limited Time). Dazu erstellt man einen Cronjob: @hourly free-games-on-steam 2>/dev/null

So bekommt man alle neuen Gratisaktionen via Mail mitgeteilt.

#!/bin/sh

set -e

seen="${XDG_CACHE_HOME:-$HOME/.cache}/free-games-on-steam.seen"
listlinks="$(which listlinks)"
url="https://store.steampowered.com/news/?headlines=1"

mkdir -p "$(dirname "$seen")"
touch "$seen"

xvfb-run --auto-servernum $listlinks "$url" | cut -f 2 | grep -i -w "free" | while read REPLY; do
	if grep -q --line-regexp "$REPLY" "$seen"; then
		continue
	fi

	echo "$REPLY" | tee --append "$seen"
done

cut-cache "$seen"

Download: fp-content/attachs/free-games-on-steam.sh

Werbebanner von Google Ads/Ad Scence automatisiert anklicken

Mit dem Shell-Skript gadclick.sh, können Werbebanner von Google Ads/Ad Scence automatisiert anklicken werden. Das funktioniert allerdings nicht immer/mit allen Websites (Ich tippe mal auf ein Timing-Problem o.ä.).

#!/bin/sh

set -e

temp="$(/bin/mktemp --tmpdir=/dev/shm/)"
trap "rm -f -- '$temp'" 0 1 2 3 15 # Lösche Datei beim Beenden, Abbruch ...

sites="${XDG_CONFIG_HOME:-$HOME/.config}/gadclick.sites"

log="${XDG_CACHE_HOME:-$HOME/.cache}/gadclick.log"
mkdir -p "$(dirname "$log")"
touch "$log"

shuf "$sites" | while read REPLY; do

	xvfb-run --auto-servernum listlinks "$REPLY" | cut -f 1 >"$temp" || continue

	cat <<! | grep -E -f - "$temp" | shuf -n 1 | wget -i - -qO /dev/null || continue
^https?://adclick\.g\.doubleclick\.net/
^https?://googleads\.g\.doubleclick\.net/
^https?://www\.googleadservices\.com/
!

	date +"%D %T	$REPLY" >>"$log"

done

cut-cache "$log"

Download: fp-content/attachs/gadclick.sh

[h2]cut-cache[(h2]

#!/bin/sh
# Cache kürzen
# cut-cache <path/to/cachefile> [keep [threshold]]

set -e

cachefile="$1"
keep="${2:-100}"
threshold="${3:-500}"

if test "$( cat "$cachefile" | wc -l )" -gt "$threshold"; then
	temp=$(mktemp)
	tail -n "$keep" "$cachefile" >"$temp"
	mv "$temp" "$cache"
fi

Download: fp-content/attachs/cut-cache.sh

Wie die anderen Skript auch, kann cut-cache.sh nach /usr/local/bin/ installiert und ausführbar gemacht werden:

$ wget -O /usr/local/bin/listlinks https://0010100.net/blog/fp-content/attachs/listlinks.js
$ wget -O /usr/local/bin/free-games-on-steam https://0010100.net/blog/fp-content/attachs/free-games-on-steam.sh
$ wget -O /usr/local/bin/gadclick https://0010100.net/blog/fp-content/attachs/gadclick.sh
$ wget -O /usr/local/bin/cut-cache https://0010100.net/blog/fp-content/attachs/cut-cache.sh
$ chmod +x /usr/local/bin/{listlinks,free-games-on-steam,gadclick,cut-cache}

Anhang: Weitere Lösungen mit Selenium

Die Skripts nutzen z.Z. Chromium, aber Firefox sollte auch funktionieren (entsprechenden Code aktivieren).

PhantomJS funktioniert z.Z. nicht (könnte aber einzurichten sein):

Message: Error - Unable to load Atom ‘find_elements’ from file ‘:/ghostdriver/./third_party/webdriver-atoms/find_elements.js’