2020-10-31 13:16:37 -07:00
|
|
|
const child_process = require("child_process");
|
2020-11-01 19:22:53 -08:00
|
|
|
const path = require("path");
|
2020-12-02 16:26:20 +00:00
|
|
|
const fs = require("fs");
|
2021-04-29 14:34:56 -07:00
|
|
|
const fsp = require("fs/promises");
|
2021-04-10 13:08:22 -07:00
|
|
|
const os = require("os");
|
2021-02-08 22:21:34 -08:00
|
|
|
|
2020-10-31 13:16:37 -07:00
|
|
|
// to ignore HTTPS error for HEAD check
|
|
|
|
const HTTPS_AGENT = require("https").Agent({
|
|
|
|
rejectUnauthorized: false,
|
|
|
|
});
|
|
|
|
|
2020-11-01 19:22:53 -08:00
|
|
|
const HTTP_AGENT = require("http").Agent();
|
|
|
|
|
Per-Seed Scoping Rules + Crawl Depth (#63)
* scoped seeds:
- support per-seed scoping (include + exclude), allowHash, depth, and sitemap options
- support maxDepth per seed #16
- combine --url, --seed and --urlFile/--seedFile urls into a unified seed list
arg parsing:
- simplify seed file options into --seedFile/--urlFile, move option in help display
- rename --maxDepth -> --depth, supported globally and per seed
- ensure custom parsed params from argParser passed back correctly (behaviors, logging, device emulation)
- update to latest js-yaml
- rename --yamlConfig -> --config
- config: support reading config from stdin if --config set to 'stdin'
* scope: fix typo in 'prefix' scope
* update browsertrix-behaviors to 0.2.2
* tests: add test for passing config via stdin, also adding --excludes via cmdline
* update README:
- latest cli, add docs on config via stdin
- rename --yamlConfig -> --config, consolidate --seedFile/--urlFile, move arg position
- info on scoped seeds
- list current scope types
2021-06-26 13:11:29 -07:00
|
|
|
const fetch = require("node-fetch");
|
|
|
|
const puppeteer = require("puppeteer-core");
|
|
|
|
const { Cluster } = require("puppeteer-cluster");
|
|
|
|
const AbortController = require("abort-controller");
|
|
|
|
const Sitemapper = require("sitemapper");
|
|
|
|
const { v4: uuidv4 } = require("uuid");
|
|
|
|
const Redis = require("ioredis");
|
|
|
|
|
|
|
|
const warcio = require("warcio");
|
|
|
|
|
|
|
|
const behaviors = fs.readFileSync("/app/node_modules/browsertrix-behaviors/dist/behaviors.js", "utf-8");
|
|
|
|
|
2021-06-23 19:36:32 -07:00
|
|
|
const TextExtract = require("./util/textextract");
|
|
|
|
const { ScreenCaster } = require("./util/screencaster");
|
|
|
|
const { parseArgs } = require("./util/argParser");
|
2021-06-07 17:43:36 -07:00
|
|
|
|
2021-06-24 15:39:17 -07:00
|
|
|
const { BROWSER_BIN, BEHAVIOR_LOG_FUNC, HTML_TYPES } = require("./util/constants");
|
2020-10-31 13:16:37 -07:00
|
|
|
|
2021-07-19 15:49:43 -07:00
|
|
|
const { BlockRules } = require("./util/blockrules");
|
|
|
|
|
|
|
|
|
2020-11-01 19:22:53 -08:00
|
|
|
// ============================================================================
|
|
|
|
class Crawler {
|
|
|
|
constructor() {
|
2020-11-03 17:16:29 +00:00
|
|
|
this.headers = {};
|
2020-11-01 19:22:53 -08:00
|
|
|
this.seenList = new Set();
|
|
|
|
|
2020-11-14 19:32:31 +00:00
|
|
|
this.emulateDevice = null;
|
|
|
|
|
2020-11-01 19:22:53 -08:00
|
|
|
// links crawled counter
|
|
|
|
this.numLinks = 0;
|
|
|
|
|
2021-04-29 14:34:56 -07:00
|
|
|
// pages file
|
|
|
|
this.pagesFH = null;
|
|
|
|
|
2021-01-29 18:26:55 +00:00
|
|
|
// was the limit hit?
|
|
|
|
this.limitHit = false;
|
|
|
|
|
2020-11-14 19:32:31 +00:00
|
|
|
this.userAgent = "";
|
2021-04-10 13:08:22 -07:00
|
|
|
this.profileDir = fs.mkdtempSync(path.join(os.tmpdir(), "profile-"));
|
2020-11-14 19:32:31 +00:00
|
|
|
|
Per-Seed Scoping Rules + Crawl Depth (#63)
* scoped seeds:
- support per-seed scoping (include + exclude), allowHash, depth, and sitemap options
- support maxDepth per seed #16
- combine --url, --seed and --urlFile/--seedFile urls into a unified seed list
arg parsing:
- simplify seed file options into --seedFile/--urlFile, move option in help display
- rename --maxDepth -> --depth, supported globally and per seed
- ensure custom parsed params from argParser passed back correctly (behaviors, logging, device emulation)
- update to latest js-yaml
- rename --yamlConfig -> --config
- config: support reading config from stdin if --config set to 'stdin'
* scope: fix typo in 'prefix' scope
* update browsertrix-behaviors to 0.2.2
* tests: add test for passing config via stdin, also adding --excludes via cmdline
* update README:
- latest cli, add docs on config via stdin
- rename --yamlConfig -> --config, consolidate --seedFile/--urlFile, move arg position
- info on scoped seeds
- list current scope types
2021-06-26 13:11:29 -07:00
|
|
|
this.params = parseArgs(this.profileDir);
|
2021-06-23 19:36:32 -07:00
|
|
|
|
Per-Seed Scoping Rules + Crawl Depth (#63)
* scoped seeds:
- support per-seed scoping (include + exclude), allowHash, depth, and sitemap options
- support maxDepth per seed #16
- combine --url, --seed and --urlFile/--seedFile urls into a unified seed list
arg parsing:
- simplify seed file options into --seedFile/--urlFile, move option in help display
- rename --maxDepth -> --depth, supported globally and per seed
- ensure custom parsed params from argParser passed back correctly (behaviors, logging, device emulation)
- update to latest js-yaml
- rename --yamlConfig -> --config
- config: support reading config from stdin if --config set to 'stdin'
* scope: fix typo in 'prefix' scope
* update browsertrix-behaviors to 0.2.2
* tests: add test for passing config via stdin, also adding --excludes via cmdline
* update README:
- latest cli, add docs on config via stdin
- rename --yamlConfig -> --config, consolidate --seedFile/--urlFile, move arg position
- info on scoped seeds
- list current scope types
2021-06-26 13:11:29 -07:00
|
|
|
this.emulateDevice = this.params.emulateDevice;
|
2020-11-01 19:22:53 -08:00
|
|
|
|
Per-Seed Scoping Rules + Crawl Depth (#63)
* scoped seeds:
- support per-seed scoping (include + exclude), allowHash, depth, and sitemap options
- support maxDepth per seed #16
- combine --url, --seed and --urlFile/--seedFile urls into a unified seed list
arg parsing:
- simplify seed file options into --seedFile/--urlFile, move option in help display
- rename --maxDepth -> --depth, supported globally and per seed
- ensure custom parsed params from argParser passed back correctly (behaviors, logging, device emulation)
- update to latest js-yaml
- rename --yamlConfig -> --config
- config: support reading config from stdin if --config set to 'stdin'
* scope: fix typo in 'prefix' scope
* update browsertrix-behaviors to 0.2.2
* tests: add test for passing config via stdin, also adding --excludes via cmdline
* update README:
- latest cli, add docs on config via stdin
- rename --yamlConfig -> --config, consolidate --seedFile/--urlFile, move arg position
- info on scoped seeds
- list current scope types
2021-06-26 13:11:29 -07:00
|
|
|
console.log("Seeds", this.params.scopedSeeds);
|
2020-11-01 19:22:53 -08:00
|
|
|
|
2021-07-19 15:49:43 -07:00
|
|
|
this.captureBasePrefix = `http://${process.env.PROXY_HOST}:${process.env.PROXY_PORT}/${this.params.collection}/record`;
|
|
|
|
this.capturePrefix = this.captureBasePrerix + "/id_/";
|
2021-02-04 00:28:32 -05:00
|
|
|
|
2021-05-21 15:37:02 -07:00
|
|
|
this.gotoOpts = {
|
|
|
|
waitUntil: this.params.waitUntil,
|
|
|
|
timeout: this.params.timeout
|
|
|
|
};
|
2021-02-04 00:28:32 -05:00
|
|
|
|
|
|
|
// root collections dir
|
|
|
|
this.collDir = path.join(this.params.cwd, "collections", this.params.collection);
|
|
|
|
|
|
|
|
// pages directory
|
|
|
|
this.pagesDir = path.join(this.collDir, "pages");
|
2021-06-23 19:36:32 -07:00
|
|
|
|
2021-02-04 00:28:32 -05:00
|
|
|
// pages file
|
|
|
|
this.pagesFile = path.join(this.pagesDir, "pages.jsonl");
|
2021-07-19 15:49:43 -07:00
|
|
|
|
|
|
|
|
|
|
|
this.blockRules = null;
|
2020-11-01 19:22:53 -08:00
|
|
|
}
|
|
|
|
|
2020-11-14 19:32:31 +00:00
|
|
|
configureUA() {
|
|
|
|
// override userAgent
|
|
|
|
if (this.params.userAgent) {
|
|
|
|
|
|
|
|
if (this.emulateDevice) {
|
|
|
|
this.emulateDevice.userAgent = this.params.userAgent;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.userAgent = this.params.userAgent;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if device set, it overrides the default Chrome UA
|
|
|
|
if (this.emulateDevice) {
|
|
|
|
this.userAgent = this.emulateDevice.userAgent;
|
|
|
|
} else {
|
2021-02-03 22:24:38 -08:00
|
|
|
let version = process.env.BROWSER_VERSION;
|
2020-11-14 19:32:31 +00:00
|
|
|
|
|
|
|
try {
|
2021-06-24 15:39:17 -07:00
|
|
|
version = child_process.execFileSync(BROWSER_BIN, ["--product-version"], {encoding: "utf8"}).trim();
|
2021-02-04 00:28:32 -05:00
|
|
|
} catch(e) {
|
|
|
|
console.log(e);
|
|
|
|
}
|
2020-11-14 19:32:31 +00:00
|
|
|
|
|
|
|
this.userAgent = `Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${version} Safari/537.36`;
|
|
|
|
}
|
|
|
|
|
|
|
|
// suffix to append to default userAgent
|
|
|
|
if (this.params.userAgentSuffix) {
|
|
|
|
this.userAgent += " " + this.params.userAgentSuffix;
|
|
|
|
|
|
|
|
if (this.emulateDevice) {
|
|
|
|
this.emulateDevice.userAgent += " " + this.params.userAgentSuffix;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-06-23 19:36:32 -07:00
|
|
|
|
2020-11-01 19:22:53 -08:00
|
|
|
bootstrap() {
|
2021-03-31 13:41:27 -04:00
|
|
|
let opts = {};
|
2021-03-13 16:48:31 -08:00
|
|
|
if (this.params.logging.includes("pywb")) {
|
2021-03-04 15:36:58 -05:00
|
|
|
opts = {stdio: "inherit", cwd: this.params.cwd};
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
opts = {stdio: "ignore", cwd: this.params.cwd};
|
|
|
|
}
|
2020-11-01 19:22:53 -08:00
|
|
|
|
2020-11-14 20:51:07 +00:00
|
|
|
this.configureUA();
|
2021-06-23 19:36:32 -07:00
|
|
|
|
2020-11-03 17:16:29 +00:00
|
|
|
this.headers = {"User-Agent": this.userAgent};
|
|
|
|
|
2021-05-21 15:37:02 -07:00
|
|
|
const subprocesses = [];
|
|
|
|
|
|
|
|
subprocesses.push(child_process.spawn("redis-server", {...opts, cwd: "/tmp/"}));
|
|
|
|
|
2020-11-01 19:22:53 -08:00
|
|
|
child_process.spawnSync("wb-manager", ["init", this.params.collection], opts);
|
|
|
|
|
2021-03-31 13:41:27 -04:00
|
|
|
opts.env = {...process.env, COLL: this.params.collection, ROLLOVER_SIZE: this.params.rolloverSize};
|
2021-06-23 19:36:32 -07:00
|
|
|
|
2021-05-21 15:37:02 -07:00
|
|
|
subprocesses.push(child_process.spawn("uwsgi", [path.join(__dirname, "uwsgi.ini")], opts));
|
|
|
|
|
|
|
|
process.on("exit", () => {
|
|
|
|
for (const proc of subprocesses) {
|
|
|
|
proc.kill();
|
|
|
|
}
|
|
|
|
});
|
2020-11-01 19:22:53 -08:00
|
|
|
|
|
|
|
if (!this.params.headless) {
|
|
|
|
child_process.spawn("Xvfb", [
|
|
|
|
process.env.DISPLAY,
|
|
|
|
"-listen",
|
|
|
|
"tcp",
|
|
|
|
"-screen",
|
|
|
|
"0",
|
|
|
|
process.env.GEOMETRY,
|
|
|
|
"-ac",
|
|
|
|
"+extension",
|
|
|
|
"RANDR"
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
get chromeArgs() {
|
|
|
|
// Chrome Flags, including proxy server
|
|
|
|
return [
|
|
|
|
"--no-xshm", // needed for Chrome >80 (check if puppeteer adds automatically)
|
|
|
|
`--proxy-server=http://${process.env.PROXY_HOST}:${process.env.PROXY_PORT}`,
|
|
|
|
"--no-sandbox",
|
|
|
|
"--disable-background-media-suspend",
|
|
|
|
"--autoplay-policy=no-user-gesture-required",
|
2021-01-29 00:33:01 -08:00
|
|
|
"--disable-features=IsolateOrigins,site-per-process",
|
2021-05-21 15:37:02 -07:00
|
|
|
"--disable-popup-blocking",
|
|
|
|
"--disable-backgrounding-occluded-windows",
|
2020-11-01 19:22:53 -08:00
|
|
|
];
|
|
|
|
}
|
2020-10-31 13:16:37 -07:00
|
|
|
|
2020-11-01 19:22:53 -08:00
|
|
|
get puppeteerArgs() {
|
|
|
|
// Puppeter Options
|
|
|
|
return {
|
|
|
|
headless: this.params.headless,
|
2021-06-24 15:39:17 -07:00
|
|
|
executablePath: BROWSER_BIN,
|
2020-11-01 19:22:53 -08:00
|
|
|
ignoreHTTPSErrors: true,
|
2021-04-10 13:08:22 -07:00
|
|
|
args: this.chromeArgs,
|
|
|
|
userDataDir: this.profileDir,
|
|
|
|
defaultViewport: null,
|
2020-11-01 19:22:53 -08:00
|
|
|
};
|
|
|
|
}
|
2020-10-31 13:16:37 -07:00
|
|
|
|
2020-11-01 19:22:53 -08:00
|
|
|
async run() {
|
|
|
|
this.bootstrap();
|
2020-10-31 13:16:37 -07:00
|
|
|
|
|
|
|
try {
|
2020-11-01 19:22:53 -08:00
|
|
|
await this.crawl();
|
|
|
|
process.exit(0);
|
2020-10-31 13:16:37 -07:00
|
|
|
} catch(e) {
|
2020-11-01 19:22:53 -08:00
|
|
|
console.error("Crawl failed");
|
|
|
|
console.error(e);
|
|
|
|
process.exit(1);
|
2020-10-31 13:16:37 -07:00
|
|
|
}
|
2020-11-01 19:22:53 -08:00
|
|
|
}
|
2021-04-10 13:08:22 -07:00
|
|
|
|
|
|
|
_behaviorLog({data, type}) {
|
|
|
|
switch (type) {
|
|
|
|
case "info":
|
|
|
|
console.log(JSON.stringify(data));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "debug":
|
|
|
|
default:
|
Per-Seed Scoping Rules + Crawl Depth (#63)
* scoped seeds:
- support per-seed scoping (include + exclude), allowHash, depth, and sitemap options
- support maxDepth per seed #16
- combine --url, --seed and --urlFile/--seedFile urls into a unified seed list
arg parsing:
- simplify seed file options into --seedFile/--urlFile, move option in help display
- rename --maxDepth -> --depth, supported globally and per seed
- ensure custom parsed params from argParser passed back correctly (behaviors, logging, device emulation)
- update to latest js-yaml
- rename --yamlConfig -> --config
- config: support reading config from stdin if --config set to 'stdin'
* scope: fix typo in 'prefix' scope
* update browsertrix-behaviors to 0.2.2
* tests: add test for passing config via stdin, also adding --excludes via cmdline
* update README:
- latest cli, add docs on config via stdin
- rename --yamlConfig -> --config, consolidate --seedFile/--urlFile, move arg position
- info on scoped seeds
- list current scope types
2021-06-26 13:11:29 -07:00
|
|
|
if (this.params.behaviorsLogDebug) {
|
2021-04-10 13:08:22 -07:00
|
|
|
console.log("behavior debug: " + JSON.stringify(data));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-08 22:21:34 -08:00
|
|
|
async crawlPage({page, data}) {
|
|
|
|
try {
|
2021-06-07 17:43:36 -07:00
|
|
|
if (this.screencaster) {
|
|
|
|
await this.screencaster.newTarget(page.target());
|
|
|
|
}
|
|
|
|
|
2021-02-08 22:21:34 -08:00
|
|
|
if (this.emulateDevice) {
|
|
|
|
await page.emulate(this.emulateDevice);
|
|
|
|
}
|
|
|
|
|
Per-Seed Scoping Rules + Crawl Depth (#63)
* scoped seeds:
- support per-seed scoping (include + exclude), allowHash, depth, and sitemap options
- support maxDepth per seed #16
- combine --url, --seed and --urlFile/--seedFile urls into a unified seed list
arg parsing:
- simplify seed file options into --seedFile/--urlFile, move option in help display
- rename --maxDepth -> --depth, supported globally and per seed
- ensure custom parsed params from argParser passed back correctly (behaviors, logging, device emulation)
- update to latest js-yaml
- rename --yamlConfig -> --config
- config: support reading config from stdin if --config set to 'stdin'
* scope: fix typo in 'prefix' scope
* update browsertrix-behaviors to 0.2.2
* tests: add test for passing config via stdin, also adding --excludes via cmdline
* update README:
- latest cli, add docs on config via stdin
- rename --yamlConfig -> --config, consolidate --seedFile/--urlFile, move arg position
- info on scoped seeds
- list current scope types
2021-06-26 13:11:29 -07:00
|
|
|
if (this.params.behaviorOpts && !page.__bx_inited) {
|
2021-04-10 13:08:22 -07:00
|
|
|
await page.exposeFunction(BEHAVIOR_LOG_FUNC, (logdata) => this._behaviorLog(logdata));
|
Per-Seed Scoping Rules + Crawl Depth (#63)
* scoped seeds:
- support per-seed scoping (include + exclude), allowHash, depth, and sitemap options
- support maxDepth per seed #16
- combine --url, --seed and --urlFile/--seedFile urls into a unified seed list
arg parsing:
- simplify seed file options into --seedFile/--urlFile, move option in help display
- rename --maxDepth -> --depth, supported globally and per seed
- ensure custom parsed params from argParser passed back correctly (behaviors, logging, device emulation)
- update to latest js-yaml
- rename --yamlConfig -> --config
- config: support reading config from stdin if --config set to 'stdin'
* scope: fix typo in 'prefix' scope
* update browsertrix-behaviors to 0.2.2
* tests: add test for passing config via stdin, also adding --excludes via cmdline
* update README:
- latest cli, add docs on config via stdin
- rename --yamlConfig -> --config, consolidate --seedFile/--urlFile, move arg position
- info on scoped seeds
- list current scope types
2021-06-26 13:11:29 -07:00
|
|
|
await page.evaluateOnNewDocument(behaviors + `;\nself.__bx_behaviors.init(${this.params.behaviorOpts});`);
|
2021-06-07 17:43:36 -07:00
|
|
|
page.__bx_inited = true;
|
2021-03-13 16:48:31 -08:00
|
|
|
}
|
2021-02-08 22:21:34 -08:00
|
|
|
|
|
|
|
// run custom driver here
|
|
|
|
await this.driver({page, data, crawler: this});
|
2021-06-23 19:36:32 -07:00
|
|
|
|
|
|
|
|
2021-02-08 22:21:34 -08:00
|
|
|
const title = await page.title();
|
2021-03-31 13:41:27 -04:00
|
|
|
let text = "";
|
2021-03-13 16:48:31 -08:00
|
|
|
if (this.params.text) {
|
2021-02-23 16:52:54 -05:00
|
|
|
const client = await page.target().createCDPSession();
|
|
|
|
const result = await client.send("DOM.getDocument", {"depth": -1, "pierce": true});
|
2021-03-04 15:36:58 -05:00
|
|
|
text = await new TextExtract(result).parseTextFromDom();
|
2021-02-23 16:52:54 -05:00
|
|
|
}
|
2021-06-23 19:36:32 -07:00
|
|
|
|
2021-04-29 14:34:56 -07:00
|
|
|
await this.writePage(data.url, title, this.params.text, text);
|
2021-02-08 22:21:34 -08:00
|
|
|
|
Per-Seed Scoping Rules + Crawl Depth (#63)
* scoped seeds:
- support per-seed scoping (include + exclude), allowHash, depth, and sitemap options
- support maxDepth per seed #16
- combine --url, --seed and --urlFile/--seedFile urls into a unified seed list
arg parsing:
- simplify seed file options into --seedFile/--urlFile, move option in help display
- rename --maxDepth -> --depth, supported globally and per seed
- ensure custom parsed params from argParser passed back correctly (behaviors, logging, device emulation)
- update to latest js-yaml
- rename --yamlConfig -> --config
- config: support reading config from stdin if --config set to 'stdin'
* scope: fix typo in 'prefix' scope
* update browsertrix-behaviors to 0.2.2
* tests: add test for passing config via stdin, also adding --excludes via cmdline
* update README:
- latest cli, add docs on config via stdin
- rename --yamlConfig -> --config, consolidate --seedFile/--urlFile, move arg position
- info on scoped seeds
- list current scope types
2021-06-26 13:11:29 -07:00
|
|
|
if (this.params.behaviorOpts) {
|
2021-03-13 16:48:31 -08:00
|
|
|
await Promise.allSettled(page.frames().map(frame => frame.evaluate("self.__bx_behaviors.run();")));
|
2021-02-08 22:21:34 -08:00
|
|
|
}
|
|
|
|
|
2021-04-29 14:34:56 -07:00
|
|
|
await this.writeStats();
|
2021-02-08 22:21:34 -08:00
|
|
|
|
2021-06-07 17:43:36 -07:00
|
|
|
if (this.screencaster) {
|
|
|
|
await this.screencaster.endTarget(page.target());
|
|
|
|
}
|
|
|
|
|
2021-02-08 22:21:34 -08:00
|
|
|
} catch (e) {
|
|
|
|
console.warn(e);
|
|
|
|
}
|
|
|
|
}
|
2021-06-23 19:36:32 -07:00
|
|
|
|
2021-03-31 13:41:27 -04:00
|
|
|
async createWARCInfo(filename) {
|
2021-07-07 18:56:52 -04:00
|
|
|
const warcVersion = "WARC/1.0";
|
2021-03-31 13:41:27 -04:00
|
|
|
const type = "warcinfo";
|
2021-04-29 14:34:56 -07:00
|
|
|
const packageFileJSON = JSON.parse(await fsp.readFile("../app/package.json"));
|
2021-07-07 18:56:52 -04:00
|
|
|
const warcioPackageJSON = JSON.parse(await fsp.readFile("/app/node_modules/warcio/package.json"));
|
2021-06-24 15:39:17 -07:00
|
|
|
const pywbVersion = child_process.execSync("pywb -V", {encoding: "utf8"}).trim().split(" ")[1];
|
2021-03-31 13:41:27 -04:00
|
|
|
|
|
|
|
const info = {
|
2021-07-07 18:56:52 -04:00
|
|
|
"software": `Browsertrix-Crawler ${packageFileJSON.version} (with warcio.js ${warcioPackageJSON.version} pywb ${pywbVersion})`,
|
|
|
|
"format": "WARC File Format 1.0"
|
2021-03-31 13:41:27 -04:00
|
|
|
};
|
2021-07-07 18:56:52 -04:00
|
|
|
|
|
|
|
const warcInfo = {...info, ...this.params.warcInfo, };
|
|
|
|
const record = await warcio.WARCRecord.createWARCInfo({filename, type, warcVersion}, warcInfo);
|
2021-03-31 13:41:27 -04:00
|
|
|
const buffer = await warcio.WARCSerializer.serialize(record, {gzip: true});
|
|
|
|
return buffer;
|
|
|
|
}
|
2021-06-23 19:36:32 -07:00
|
|
|
|
2021-04-29 14:34:56 -07:00
|
|
|
async getFileSize(filename) {
|
2021-07-07 18:56:52 -04:00
|
|
|
const stats = await fsp.stat(filename);
|
2021-03-31 13:41:27 -04:00
|
|
|
return stats.size;
|
|
|
|
}
|
2021-02-08 22:21:34 -08:00
|
|
|
|
2020-11-01 19:22:53 -08:00
|
|
|
async crawl() {
|
2021-06-23 19:36:32 -07:00
|
|
|
|
2020-10-31 13:16:37 -07:00
|
|
|
try {
|
2020-11-01 19:22:53 -08:00
|
|
|
this.driver = require(this.params.driver);
|
|
|
|
} catch(e) {
|
|
|
|
console.log(e);
|
|
|
|
return;
|
2020-10-31 13:16:37 -07:00
|
|
|
}
|
|
|
|
|
2020-11-01 19:22:53 -08:00
|
|
|
// Puppeteer Cluster init and options
|
|
|
|
this.cluster = await Cluster.launch({
|
|
|
|
concurrency: this.params.newContext,
|
|
|
|
maxConcurrency: this.params.workers,
|
|
|
|
skipDuplicateUrls: true,
|
|
|
|
timeout: this.params.timeout * 2,
|
|
|
|
puppeteerOptions: this.puppeteerArgs,
|
|
|
|
puppeteer,
|
2021-03-13 16:48:31 -08:00
|
|
|
monitor: this.params.logging.includes("stats")
|
2020-11-01 19:22:53 -08:00
|
|
|
});
|
2020-10-31 13:16:37 -07:00
|
|
|
|
2021-02-08 22:21:34 -08:00
|
|
|
this.cluster.task((opts) => this.crawlPage(opts));
|
2021-06-23 19:36:32 -07:00
|
|
|
|
2021-04-29 14:34:56 -07:00
|
|
|
await this.initPages();
|
2021-06-07 17:43:36 -07:00
|
|
|
|
2021-07-19 15:49:43 -07:00
|
|
|
if (this.params.blockRules) {
|
|
|
|
this.blockRules = new BlockRules(this.params.blockRules, this.captureBasePrefix, this.params.blockMessage);
|
|
|
|
}
|
|
|
|
|
2021-06-07 17:43:36 -07:00
|
|
|
if (this.params.screencastPort) {
|
|
|
|
this.screencaster = new ScreenCaster(this.cluster, this.params.screencastPort);
|
|
|
|
}
|
|
|
|
|
Per-Seed Scoping Rules + Crawl Depth (#63)
* scoped seeds:
- support per-seed scoping (include + exclude), allowHash, depth, and sitemap options
- support maxDepth per seed #16
- combine --url, --seed and --urlFile/--seedFile urls into a unified seed list
arg parsing:
- simplify seed file options into --seedFile/--urlFile, move option in help display
- rename --maxDepth -> --depth, supported globally and per seed
- ensure custom parsed params from argParser passed back correctly (behaviors, logging, device emulation)
- update to latest js-yaml
- rename --yamlConfig -> --config
- config: support reading config from stdin if --config set to 'stdin'
* scope: fix typo in 'prefix' scope
* update browsertrix-behaviors to 0.2.2
* tests: add test for passing config via stdin, also adding --excludes via cmdline
* update README:
- latest cli, add docs on config via stdin
- rename --yamlConfig -> --config, consolidate --seedFile/--urlFile, move arg position
- info on scoped seeds
- list current scope types
2021-06-26 13:11:29 -07:00
|
|
|
for (let i = 0; i < this.params.scopedSeeds.length; i++) {
|
|
|
|
const seed = this.params.scopedSeeds[i];
|
|
|
|
this.queueUrl(i, seed.url, 0);
|
2021-06-23 19:36:32 -07:00
|
|
|
|
Per-Seed Scoping Rules + Crawl Depth (#63)
* scoped seeds:
- support per-seed scoping (include + exclude), allowHash, depth, and sitemap options
- support maxDepth per seed #16
- combine --url, --seed and --urlFile/--seedFile urls into a unified seed list
arg parsing:
- simplify seed file options into --seedFile/--urlFile, move option in help display
- rename --maxDepth -> --depth, supported globally and per seed
- ensure custom parsed params from argParser passed back correctly (behaviors, logging, device emulation)
- update to latest js-yaml
- rename --yamlConfig -> --config
- config: support reading config from stdin if --config set to 'stdin'
* scope: fix typo in 'prefix' scope
* update browsertrix-behaviors to 0.2.2
* tests: add test for passing config via stdin, also adding --excludes via cmdline
* update README:
- latest cli, add docs on config via stdin
- rename --yamlConfig -> --config, consolidate --seedFile/--urlFile, move arg position
- info on scoped seeds
- list current scope types
2021-06-26 13:11:29 -07:00
|
|
|
if (seed.sitemap) {
|
|
|
|
await this.parseSitemap(seed.sitemap, i);
|
|
|
|
}
|
2020-11-14 21:55:02 +00:00
|
|
|
}
|
|
|
|
|
2020-11-01 19:22:53 -08:00
|
|
|
await this.cluster.idle();
|
|
|
|
await this.cluster.close();
|
|
|
|
|
2020-12-02 16:26:20 +00:00
|
|
|
this.writeStats();
|
|
|
|
|
2021-04-29 14:34:56 -07:00
|
|
|
if (this.pagesFH) {
|
2021-05-21 15:37:02 -07:00
|
|
|
await this.pagesFH.sync();
|
2021-04-29 14:34:56 -07:00
|
|
|
await this.pagesFH.close();
|
|
|
|
}
|
|
|
|
|
2020-11-01 19:22:53 -08:00
|
|
|
// extra wait for all resources to land into WARCs
|
2021-04-30 12:31:14 -07:00
|
|
|
await this.awaitPendingClear();
|
|
|
|
|
2021-03-31 13:41:27 -04:00
|
|
|
if (this.params.combineWARC) {
|
|
|
|
await this.combineWARC();
|
|
|
|
}
|
2020-11-01 19:22:53 -08:00
|
|
|
|
2020-11-03 21:33:19 +00:00
|
|
|
if (this.params.generateCDX) {
|
2020-11-01 19:22:53 -08:00
|
|
|
console.log("Generate CDX");
|
|
|
|
|
2020-11-02 15:28:19 +00:00
|
|
|
child_process.spawnSync("wb-manager", ["reindex", this.params.collection], {stdio: "inherit", cwd: this.params.cwd});
|
2020-10-31 13:16:37 -07:00
|
|
|
}
|
2021-06-23 19:36:32 -07:00
|
|
|
|
|
|
|
if (this.params.generateWACZ) {
|
2021-02-04 00:28:32 -05:00
|
|
|
console.log("Generating WACZ");
|
|
|
|
|
|
|
|
const archiveDir = path.join(this.collDir, "archive");
|
|
|
|
|
|
|
|
// Get a list of the warcs inside
|
2021-04-29 14:34:56 -07:00
|
|
|
const warcFileList = await fsp.readdir(archiveDir);
|
2021-06-23 19:36:32 -07:00
|
|
|
|
2021-02-04 00:28:32 -05:00
|
|
|
// Build the argument list to pass to the wacz create command
|
|
|
|
const waczFilename = this.params.collection.concat(".wacz");
|
|
|
|
const waczPath = path.join(this.collDir, waczFilename);
|
|
|
|
const argument_list = ["create", "-o", waczPath, "--pages", this.pagesFile, "-f"];
|
2021-02-17 12:37:07 -05:00
|
|
|
warcFileList.forEach((val, index) => argument_list.push(path.join(archiveDir, val))); // eslint-disable-line no-unused-vars
|
2021-06-23 19:36:32 -07:00
|
|
|
|
2021-02-04 00:28:32 -05:00
|
|
|
// Run the wacz create command
|
|
|
|
child_process.spawnSync("wacz" , argument_list);
|
2021-06-23 19:36:32 -07:00
|
|
|
console.log(`WACZ successfully generated and saved to: ${waczPath}`);
|
2021-02-04 00:28:32 -05:00
|
|
|
}
|
2020-11-01 19:22:53 -08:00
|
|
|
}
|
2020-10-31 13:16:37 -07:00
|
|
|
|
2021-04-29 14:34:56 -07:00
|
|
|
async writeStats() {
|
2020-12-02 16:26:20 +00:00
|
|
|
if (this.params.statsFilename) {
|
|
|
|
const total = this.cluster.allTargetCount;
|
|
|
|
const workersRunning = this.cluster.workersBusy.length;
|
|
|
|
const numCrawled = total - this.cluster.jobQueue.size() - workersRunning;
|
2021-01-29 18:26:55 +00:00
|
|
|
const limit = {max: this.params.limit || 0, hit: this.limitHit};
|
|
|
|
const stats = {numCrawled, workersRunning, total, limit};
|
2020-12-02 16:26:20 +00:00
|
|
|
|
|
|
|
try {
|
2021-04-29 14:34:56 -07:00
|
|
|
await fsp.writeFile(this.params.statsFilename, JSON.stringify(stats, null, 2));
|
2020-12-02 16:26:20 +00:00
|
|
|
} catch (err) {
|
|
|
|
console.warn("Stats output failed", err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Per-Seed Scoping Rules + Crawl Depth (#63)
* scoped seeds:
- support per-seed scoping (include + exclude), allowHash, depth, and sitemap options
- support maxDepth per seed #16
- combine --url, --seed and --urlFile/--seedFile urls into a unified seed list
arg parsing:
- simplify seed file options into --seedFile/--urlFile, move option in help display
- rename --maxDepth -> --depth, supported globally and per seed
- ensure custom parsed params from argParser passed back correctly (behaviors, logging, device emulation)
- update to latest js-yaml
- rename --yamlConfig -> --config
- config: support reading config from stdin if --config set to 'stdin'
* scope: fix typo in 'prefix' scope
* update browsertrix-behaviors to 0.2.2
* tests: add test for passing config via stdin, also adding --excludes via cmdline
* update README:
- latest cli, add docs on config via stdin
- rename --yamlConfig -> --config, consolidate --seedFile/--urlFile, move arg position
- info on scoped seeds
- list current scope types
2021-06-26 13:11:29 -07:00
|
|
|
async loadPage(page, urlData, selector = "a[href]") {
|
|
|
|
const {url, seedId, depth} = urlData;
|
|
|
|
|
2021-05-21 15:37:02 -07:00
|
|
|
if (!await this.isHTML(url)) {
|
|
|
|
await this.directFetchCapture(url);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-07-19 15:49:43 -07:00
|
|
|
if (this.blockRules) {
|
|
|
|
await this.blockRules.initPage(page);
|
|
|
|
}
|
|
|
|
|
2021-05-21 15:37:02 -07:00
|
|
|
try {
|
|
|
|
await page.goto(url, this.gotoOpts);
|
|
|
|
} catch (e) {
|
|
|
|
console.log(`Load timeout for ${url}`, e);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (selector) {
|
Per-Seed Scoping Rules + Crawl Depth (#63)
* scoped seeds:
- support per-seed scoping (include + exclude), allowHash, depth, and sitemap options
- support maxDepth per seed #16
- combine --url, --seed and --urlFile/--seedFile urls into a unified seed list
arg parsing:
- simplify seed file options into --seedFile/--urlFile, move option in help display
- rename --maxDepth -> --depth, supported globally and per seed
- ensure custom parsed params from argParser passed back correctly (behaviors, logging, device emulation)
- update to latest js-yaml
- rename --yamlConfig -> --config
- config: support reading config from stdin if --config set to 'stdin'
* scope: fix typo in 'prefix' scope
* update browsertrix-behaviors to 0.2.2
* tests: add test for passing config via stdin, also adding --excludes via cmdline
* update README:
- latest cli, add docs on config via stdin
- rename --yamlConfig -> --config, consolidate --seedFile/--urlFile, move arg position
- info on scoped seeds
- list current scope types
2021-06-26 13:11:29 -07:00
|
|
|
await this.extractLinks(page, seedId, depth, selector);
|
2021-05-21 15:37:02 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Per-Seed Scoping Rules + Crawl Depth (#63)
* scoped seeds:
- support per-seed scoping (include + exclude), allowHash, depth, and sitemap options
- support maxDepth per seed #16
- combine --url, --seed and --urlFile/--seedFile urls into a unified seed list
arg parsing:
- simplify seed file options into --seedFile/--urlFile, move option in help display
- rename --maxDepth -> --depth, supported globally and per seed
- ensure custom parsed params from argParser passed back correctly (behaviors, logging, device emulation)
- update to latest js-yaml
- rename --yamlConfig -> --config
- config: support reading config from stdin if --config set to 'stdin'
* scope: fix typo in 'prefix' scope
* update browsertrix-behaviors to 0.2.2
* tests: add test for passing config via stdin, also adding --excludes via cmdline
* update README:
- latest cli, add docs on config via stdin
- rename --yamlConfig -> --config, consolidate --seedFile/--urlFile, move arg position
- info on scoped seeds
- list current scope types
2021-06-26 13:11:29 -07:00
|
|
|
async extractLinks(page, seedId, depth, selector = "a[href]") {
|
2021-04-30 17:41:00 +02:00
|
|
|
let results = [];
|
2020-10-31 13:16:37 -07:00
|
|
|
|
|
|
|
try {
|
2021-04-30 17:41:00 +02:00
|
|
|
await Promise.allSettled(page.frames().map(frame => frame.evaluate((selector) => {
|
2020-11-01 19:22:53 -08:00
|
|
|
/* eslint-disable-next-line no-undef */
|
|
|
|
return [...document.querySelectorAll(selector)].map(elem => elem.href);
|
2021-04-30 17:41:00 +02:00
|
|
|
}, selector))).then((linkResults) => {
|
|
|
|
linkResults.forEach((linkResult) => {linkResult.value.forEach(link => results.push(link));});});
|
2020-10-31 13:16:37 -07:00
|
|
|
} catch (e) {
|
|
|
|
console.warn("Link Extraction failed", e);
|
|
|
|
return;
|
|
|
|
}
|
Per-Seed Scoping Rules + Crawl Depth (#63)
* scoped seeds:
- support per-seed scoping (include + exclude), allowHash, depth, and sitemap options
- support maxDepth per seed #16
- combine --url, --seed and --urlFile/--seedFile urls into a unified seed list
arg parsing:
- simplify seed file options into --seedFile/--urlFile, move option in help display
- rename --maxDepth -> --depth, supported globally and per seed
- ensure custom parsed params from argParser passed back correctly (behaviors, logging, device emulation)
- update to latest js-yaml
- rename --yamlConfig -> --config
- config: support reading config from stdin if --config set to 'stdin'
* scope: fix typo in 'prefix' scope
* update browsertrix-behaviors to 0.2.2
* tests: add test for passing config via stdin, also adding --excludes via cmdline
* update README:
- latest cli, add docs on config via stdin
- rename --yamlConfig -> --config, consolidate --seedFile/--urlFile, move arg position
- info on scoped seeds
- list current scope types
2021-06-26 13:11:29 -07:00
|
|
|
this.queueUrls(seedId, results, depth);
|
2020-11-14 21:55:02 +00:00
|
|
|
}
|
|
|
|
|
Per-Seed Scoping Rules + Crawl Depth (#63)
* scoped seeds:
- support per-seed scoping (include + exclude), allowHash, depth, and sitemap options
- support maxDepth per seed #16
- combine --url, --seed and --urlFile/--seedFile urls into a unified seed list
arg parsing:
- simplify seed file options into --seedFile/--urlFile, move option in help display
- rename --maxDepth -> --depth, supported globally and per seed
- ensure custom parsed params from argParser passed back correctly (behaviors, logging, device emulation)
- update to latest js-yaml
- rename --yamlConfig -> --config
- config: support reading config from stdin if --config set to 'stdin'
* scope: fix typo in 'prefix' scope
* update browsertrix-behaviors to 0.2.2
* tests: add test for passing config via stdin, also adding --excludes via cmdline
* update README:
- latest cli, add docs on config via stdin
- rename --yamlConfig -> --config, consolidate --seedFile/--urlFile, move arg position
- info on scoped seeds
- list current scope types
2021-06-26 13:11:29 -07:00
|
|
|
queueUrls(seedId, urls, depth) {
|
2020-10-31 13:16:37 -07:00
|
|
|
try {
|
Per-Seed Scoping Rules + Crawl Depth (#63)
* scoped seeds:
- support per-seed scoping (include + exclude), allowHash, depth, and sitemap options
- support maxDepth per seed #16
- combine --url, --seed and --urlFile/--seedFile urls into a unified seed list
arg parsing:
- simplify seed file options into --seedFile/--urlFile, move option in help display
- rename --maxDepth -> --depth, supported globally and per seed
- ensure custom parsed params from argParser passed back correctly (behaviors, logging, device emulation)
- update to latest js-yaml
- rename --yamlConfig -> --config
- config: support reading config from stdin if --config set to 'stdin'
* scope: fix typo in 'prefix' scope
* update browsertrix-behaviors to 0.2.2
* tests: add test for passing config via stdin, also adding --excludes via cmdline
* update README:
- latest cli, add docs on config via stdin
- rename --yamlConfig -> --config, consolidate --seedFile/--urlFile, move arg position
- info on scoped seeds
- list current scope types
2021-06-26 13:11:29 -07:00
|
|
|
depth += 1;
|
|
|
|
const seed = this.params.scopedSeeds[seedId];
|
|
|
|
|
2020-11-14 21:55:02 +00:00
|
|
|
for (const url of urls) {
|
Per-Seed Scoping Rules + Crawl Depth (#63)
* scoped seeds:
- support per-seed scoping (include + exclude), allowHash, depth, and sitemap options
- support maxDepth per seed #16
- combine --url, --seed and --urlFile/--seedFile urls into a unified seed list
arg parsing:
- simplify seed file options into --seedFile/--urlFile, move option in help display
- rename --maxDepth -> --depth, supported globally and per seed
- ensure custom parsed params from argParser passed back correctly (behaviors, logging, device emulation)
- update to latest js-yaml
- rename --yamlConfig -> --config
- config: support reading config from stdin if --config set to 'stdin'
* scope: fix typo in 'prefix' scope
* update browsertrix-behaviors to 0.2.2
* tests: add test for passing config via stdin, also adding --excludes via cmdline
* update README:
- latest cli, add docs on config via stdin
- rename --yamlConfig -> --config, consolidate --seedFile/--urlFile, move arg position
- info on scoped seeds
- list current scope types
2021-06-26 13:11:29 -07:00
|
|
|
const captureUrl = seed.isIncluded(url, depth);
|
|
|
|
|
2020-11-01 19:22:53 -08:00
|
|
|
if (captureUrl) {
|
Per-Seed Scoping Rules + Crawl Depth (#63)
* scoped seeds:
- support per-seed scoping (include + exclude), allowHash, depth, and sitemap options
- support maxDepth per seed #16
- combine --url, --seed and --urlFile/--seedFile urls into a unified seed list
arg parsing:
- simplify seed file options into --seedFile/--urlFile, move option in help display
- rename --maxDepth -> --depth, supported globally and per seed
- ensure custom parsed params from argParser passed back correctly (behaviors, logging, device emulation)
- update to latest js-yaml
- rename --yamlConfig -> --config
- config: support reading config from stdin if --config set to 'stdin'
* scope: fix typo in 'prefix' scope
* update browsertrix-behaviors to 0.2.2
* tests: add test for passing config via stdin, also adding --excludes via cmdline
* update README:
- latest cli, add docs on config via stdin
- rename --yamlConfig -> --config, consolidate --seedFile/--urlFile, move arg position
- info on scoped seeds
- list current scope types
2021-06-26 13:11:29 -07:00
|
|
|
this.queueUrl(seedId, captureUrl, depth);
|
2020-10-31 13:16:37 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (e) {
|
2020-11-01 19:22:53 -08:00
|
|
|
console.log("Queuing Error: ", e);
|
2020-10-31 13:16:37 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Per-Seed Scoping Rules + Crawl Depth (#63)
* scoped seeds:
- support per-seed scoping (include + exclude), allowHash, depth, and sitemap options
- support maxDepth per seed #16
- combine --url, --seed and --urlFile/--seedFile urls into a unified seed list
arg parsing:
- simplify seed file options into --seedFile/--urlFile, move option in help display
- rename --maxDepth -> --depth, supported globally and per seed
- ensure custom parsed params from argParser passed back correctly (behaviors, logging, device emulation)
- update to latest js-yaml
- rename --yamlConfig -> --config
- config: support reading config from stdin if --config set to 'stdin'
* scope: fix typo in 'prefix' scope
* update browsertrix-behaviors to 0.2.2
* tests: add test for passing config via stdin, also adding --excludes via cmdline
* update README:
- latest cli, add docs on config via stdin
- rename --yamlConfig -> --config, consolidate --seedFile/--urlFile, move arg position
- info on scoped seeds
- list current scope types
2021-06-26 13:11:29 -07:00
|
|
|
queueUrl(seedId, url, depth) {
|
|
|
|
if (this.seenList.has(url)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-11-01 19:22:53 -08:00
|
|
|
this.seenList.add(url);
|
|
|
|
if (this.numLinks >= this.params.limit && this.params.limit > 0) {
|
2021-01-29 18:26:55 +00:00
|
|
|
this.limitHit = true;
|
2020-11-01 19:22:53 -08:00
|
|
|
return false;
|
2020-10-31 13:16:37 -07:00
|
|
|
}
|
2020-11-01 19:22:53 -08:00
|
|
|
this.numLinks++;
|
Per-Seed Scoping Rules + Crawl Depth (#63)
* scoped seeds:
- support per-seed scoping (include + exclude), allowHash, depth, and sitemap options
- support maxDepth per seed #16
- combine --url, --seed and --urlFile/--seedFile urls into a unified seed list
arg parsing:
- simplify seed file options into --seedFile/--urlFile, move option in help display
- rename --maxDepth -> --depth, supported globally and per seed
- ensure custom parsed params from argParser passed back correctly (behaviors, logging, device emulation)
- update to latest js-yaml
- rename --yamlConfig -> --config
- config: support reading config from stdin if --config set to 'stdin'
* scope: fix typo in 'prefix' scope
* update browsertrix-behaviors to 0.2.2
* tests: add test for passing config via stdin, also adding --excludes via cmdline
* update README:
- latest cli, add docs on config via stdin
- rename --yamlConfig -> --config, consolidate --seedFile/--urlFile, move arg position
- info on scoped seeds
- list current scope types
2021-06-26 13:11:29 -07:00
|
|
|
this.cluster.queue({url, seedId, depth});
|
2020-11-01 19:22:53 -08:00
|
|
|
return true;
|
2020-10-31 13:16:37 -07:00
|
|
|
}
|
|
|
|
|
2021-04-29 14:34:56 -07:00
|
|
|
async initPages() {
|
2021-02-04 00:28:32 -05:00
|
|
|
try {
|
2021-04-29 14:34:56 -07:00
|
|
|
let createNew = false;
|
|
|
|
|
2021-04-30 22:05:04 -04:00
|
|
|
// create pages dir if doesn't exist and write pages.jsonl header
|
|
|
|
if (fs.existsSync(this.pagesDir) != true){
|
|
|
|
await fsp.mkdir(this.pagesDir);
|
2021-04-29 14:34:56 -07:00
|
|
|
createNew = true;
|
|
|
|
}
|
2021-06-23 19:36:32 -07:00
|
|
|
|
2021-04-29 14:34:56 -07:00
|
|
|
this.pagesFH = await fsp.open(this.pagesFile, "a");
|
|
|
|
|
|
|
|
if (createNew) {
|
2021-03-31 13:41:27 -04:00
|
|
|
const header = {"format": "json-pages-1.0", "id": "pages", "title": "All Pages"};
|
2021-02-23 16:52:54 -05:00
|
|
|
if (this.params.text) {
|
|
|
|
console.log("creating pages with full text");
|
2021-03-31 13:41:27 -04:00
|
|
|
header["hasText"] = true;
|
2021-02-23 16:52:54 -05:00
|
|
|
}
|
|
|
|
else{
|
|
|
|
console.log("creating pages without full text");
|
2021-03-31 13:41:27 -04:00
|
|
|
header["hasText"] = false;
|
2021-02-23 16:52:54 -05:00
|
|
|
}
|
2021-03-31 13:41:27 -04:00
|
|
|
const header_formatted = JSON.stringify(header).concat("\n");
|
2021-04-29 14:34:56 -07:00
|
|
|
await this.pagesFH.writeFile(header_formatted);
|
2021-02-04 00:28:32 -05:00
|
|
|
}
|
2021-04-29 14:34:56 -07:00
|
|
|
|
2021-02-04 00:28:32 -05:00
|
|
|
} catch(err) {
|
|
|
|
console.log("pages/pages.jsonl creation failed", err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-29 14:34:56 -07:00
|
|
|
async writePage(url, title, text, text_content){
|
2021-02-04 00:28:32 -05:00
|
|
|
const id = uuidv4();
|
|
|
|
const row = {"id": id, "url": url, "title": title};
|
2021-02-23 16:52:54 -05:00
|
|
|
|
|
|
|
if (text == true){
|
2021-03-31 13:41:27 -04:00
|
|
|
row["text"] = text_content;
|
2021-02-23 16:52:54 -05:00
|
|
|
}
|
2021-06-23 19:36:32 -07:00
|
|
|
|
2021-02-04 00:28:32 -05:00
|
|
|
const processedRow = JSON.stringify(row).concat("\n");
|
|
|
|
try {
|
2021-04-29 14:34:56 -07:00
|
|
|
this.pagesFH.writeFile(processedRow);
|
2021-02-04 00:28:32 -05:00
|
|
|
}
|
|
|
|
catch (err) {
|
|
|
|
console.warn("pages/pages.jsonl append failed", err);
|
|
|
|
}
|
|
|
|
}
|
2021-06-23 19:36:32 -07:00
|
|
|
|
2020-11-01 19:22:53 -08:00
|
|
|
resolveAgent(urlParsed) {
|
|
|
|
return urlParsed.protocol === "https:" ? HTTPS_AGENT : HTTP_AGENT;
|
2020-10-31 13:16:37 -07:00
|
|
|
}
|
|
|
|
|
2020-11-01 19:22:53 -08:00
|
|
|
async isHTML(url) {
|
|
|
|
try {
|
|
|
|
const resp = await fetch(url, {
|
|
|
|
method: "HEAD",
|
|
|
|
headers: this.headers,
|
|
|
|
agent: this.resolveAgent
|
|
|
|
});
|
|
|
|
if (resp.status >= 400) {
|
2021-03-13 16:48:31 -08:00
|
|
|
console.log(`Skipping HEAD check ${url}, invalid status ${resp.status}`);
|
|
|
|
return true;
|
2020-10-31 13:16:37 -07:00
|
|
|
}
|
|
|
|
|
2020-11-01 19:22:53 -08:00
|
|
|
const contentType = resp.headers.get("Content-Type");
|
2020-10-31 13:16:37 -07:00
|
|
|
|
2020-11-01 19:22:53 -08:00
|
|
|
// just load if no content-type
|
|
|
|
if (!contentType) {
|
|
|
|
return true;
|
2020-10-31 13:16:37 -07:00
|
|
|
}
|
|
|
|
|
2020-11-01 19:22:53 -08:00
|
|
|
const mime = contentType.split(";")[0];
|
2020-10-31 13:16:37 -07:00
|
|
|
|
2020-11-01 19:22:53 -08:00
|
|
|
if (HTML_TYPES.includes(mime)) {
|
|
|
|
return true;
|
2020-10-31 13:16:37 -07:00
|
|
|
}
|
|
|
|
|
2020-11-01 19:22:53 -08:00
|
|
|
return false;
|
|
|
|
} catch(e) {
|
|
|
|
// can't confirm not html, so try in browser
|
2020-10-31 13:16:37 -07:00
|
|
|
return true;
|
2020-11-01 19:22:53 -08:00
|
|
|
}
|
2020-10-31 13:16:37 -07:00
|
|
|
}
|
|
|
|
|
2020-11-01 19:22:53 -08:00
|
|
|
async directFetchCapture(url) {
|
|
|
|
//console.log(`Direct capture: ${this.capturePrefix}${url}`);
|
|
|
|
const abort = new AbortController();
|
|
|
|
const signal = abort.signal;
|
2021-02-08 12:45:46 -05:00
|
|
|
await fetch(this.capturePrefix + url, {signal, headers: this.headers});
|
2020-11-01 19:22:53 -08:00
|
|
|
abort.abort();
|
|
|
|
}
|
2020-10-31 13:16:37 -07:00
|
|
|
|
2021-04-30 12:31:14 -07:00
|
|
|
async awaitPendingClear() {
|
|
|
|
console.log("Waiting to ensure pending data is written to WARC...");
|
|
|
|
|
|
|
|
const redis = new Redis("redis://localhost/0");
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
const res = await redis.get(`pywb:${this.params.collection}:pending`);
|
|
|
|
if (res === "0" || !res) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log(`Still waiting for ${res} pending requests to finish...`);
|
|
|
|
|
|
|
|
await this.sleep(1000);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-01 19:22:53 -08:00
|
|
|
sleep(time) {
|
|
|
|
return new Promise(resolve => setTimeout(resolve, time));
|
|
|
|
}
|
2020-10-31 13:16:37 -07:00
|
|
|
|
Per-Seed Scoping Rules + Crawl Depth (#63)
* scoped seeds:
- support per-seed scoping (include + exclude), allowHash, depth, and sitemap options
- support maxDepth per seed #16
- combine --url, --seed and --urlFile/--seedFile urls into a unified seed list
arg parsing:
- simplify seed file options into --seedFile/--urlFile, move option in help display
- rename --maxDepth -> --depth, supported globally and per seed
- ensure custom parsed params from argParser passed back correctly (behaviors, logging, device emulation)
- update to latest js-yaml
- rename --yamlConfig -> --config
- config: support reading config from stdin if --config set to 'stdin'
* scope: fix typo in 'prefix' scope
* update browsertrix-behaviors to 0.2.2
* tests: add test for passing config via stdin, also adding --excludes via cmdline
* update README:
- latest cli, add docs on config via stdin
- rename --yamlConfig -> --config, consolidate --seedFile/--urlFile, move arg position
- info on scoped seeds
- list current scope types
2021-06-26 13:11:29 -07:00
|
|
|
async parseSitemap(url, seedId) {
|
2020-11-14 21:55:02 +00:00
|
|
|
const sitemapper = new Sitemapper({
|
|
|
|
url,
|
|
|
|
timeout: 15000,
|
|
|
|
requestHeaders: this.headers
|
|
|
|
});
|
|
|
|
|
|
|
|
try {
|
|
|
|
const { sites } = await sitemapper.fetch();
|
Per-Seed Scoping Rules + Crawl Depth (#63)
* scoped seeds:
- support per-seed scoping (include + exclude), allowHash, depth, and sitemap options
- support maxDepth per seed #16
- combine --url, --seed and --urlFile/--seedFile urls into a unified seed list
arg parsing:
- simplify seed file options into --seedFile/--urlFile, move option in help display
- rename --maxDepth -> --depth, supported globally and per seed
- ensure custom parsed params from argParser passed back correctly (behaviors, logging, device emulation)
- update to latest js-yaml
- rename --yamlConfig -> --config
- config: support reading config from stdin if --config set to 'stdin'
* scope: fix typo in 'prefix' scope
* update browsertrix-behaviors to 0.2.2
* tests: add test for passing config via stdin, also adding --excludes via cmdline
* update README:
- latest cli, add docs on config via stdin
- rename --yamlConfig -> --config, consolidate --seedFile/--urlFile, move arg position
- info on scoped seeds
- list current scope types
2021-06-26 13:11:29 -07:00
|
|
|
this.queueUrls(seedId, sites, 0);
|
2020-11-14 21:55:02 +00:00
|
|
|
} catch(e) {
|
|
|
|
console.log(e);
|
|
|
|
}
|
|
|
|
}
|
2021-03-31 13:41:27 -04:00
|
|
|
|
|
|
|
async combineWARC() {
|
2021-04-29 14:34:56 -07:00
|
|
|
console.log("Combining the WARCs");
|
2021-03-31 13:41:27 -04:00
|
|
|
|
|
|
|
// Get the list of created Warcs
|
2021-04-29 14:34:56 -07:00
|
|
|
const warcLists = await fsp.readdir(path.join(this.collDir, "archive"));
|
|
|
|
|
|
|
|
console.log(`Combining ${warcLists.length} WARCs...`);
|
2021-03-31 13:41:27 -04:00
|
|
|
|
|
|
|
const fileSizeObjects = []; // Used to sort the created warc by fileSize
|
|
|
|
|
|
|
|
// Go through a list of the created works and create an array sorted by their filesize with the largest file first.
|
|
|
|
for (let i = 0; i < warcLists.length; i++) {
|
|
|
|
let fileName = path.join(this.collDir, "archive", warcLists[i]);
|
2021-04-29 14:34:56 -07:00
|
|
|
let fileSize = await this.getFileSize(fileName);
|
2021-03-31 13:41:27 -04:00
|
|
|
fileSizeObjects.push({"fileSize": fileSize, "fileName": fileName});
|
|
|
|
fileSizeObjects.sort(function(a, b){
|
|
|
|
return b.fileSize - a.fileSize;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
const generatedCombinedWarcs = [];
|
|
|
|
|
|
|
|
// Used to name combined warcs, default to -1 for first increment
|
|
|
|
let combinedWarcNumber = -1;
|
|
|
|
|
|
|
|
// write combine WARC to collection root
|
|
|
|
let combinedWarcFullPath = "";
|
|
|
|
|
2021-04-29 14:34:56 -07:00
|
|
|
// fileHandler
|
|
|
|
let fh = null;
|
|
|
|
|
2021-03-31 13:41:27 -04:00
|
|
|
// Iterate through the sorted file size array.
|
|
|
|
for (let j = 0; j < fileSizeObjects.length; j++) {
|
|
|
|
|
|
|
|
// if need to rollover to new warc
|
|
|
|
let doRollover = false;
|
|
|
|
|
|
|
|
// set to true for first warc
|
|
|
|
if (combinedWarcNumber < 0) {
|
|
|
|
doRollover = true;
|
|
|
|
} else {
|
|
|
|
// Check the size of the existing combined warc.
|
2021-04-29 14:34:56 -07:00
|
|
|
const currentCombinedWarcSize = await this.getFileSize(combinedWarcFullPath);
|
2021-03-31 13:41:27 -04:00
|
|
|
|
|
|
|
// If adding the current warc to the existing combined file creates a file smaller than the rollover size add the data to the combinedWarc
|
|
|
|
const proposedWarcSize = fileSizeObjects[j].fileSize + currentCombinedWarcSize;
|
|
|
|
|
|
|
|
doRollover = (proposedWarcSize >= this.params.rolloverSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (doRollover) {
|
2021-06-23 19:36:32 -07:00
|
|
|
// If adding the current warc to the existing combined file creates a file larger than the rollover size do the following:
|
2021-03-31 13:41:27 -04:00
|
|
|
// 1. increment the combinedWarcNumber
|
|
|
|
// 2. create the name of the new combinedWarcFile
|
|
|
|
// 3. Write the header out to the new file
|
|
|
|
// 4. Write out the current warc data to the combinedFile
|
|
|
|
combinedWarcNumber = combinedWarcNumber + 1;
|
|
|
|
|
2021-04-29 14:34:56 -07:00
|
|
|
const combinedWarcName = `${this.params.collection}_${combinedWarcNumber}.warc.gz`;
|
2021-03-31 13:41:27 -04:00
|
|
|
|
|
|
|
// write combined warcs to root collection dir as they're output of a collection (like wacz)
|
|
|
|
combinedWarcFullPath = path.join(this.collDir, combinedWarcName);
|
|
|
|
|
2021-04-29 14:34:56 -07:00
|
|
|
if (fh) {
|
|
|
|
fh.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
fh = fs.createWriteStream(combinedWarcFullPath, {flags: "a"});
|
|
|
|
|
2021-03-31 13:41:27 -04:00
|
|
|
generatedCombinedWarcs.push(combinedWarcName);
|
|
|
|
|
|
|
|
const warcBuffer = await this.createWARCInfo(combinedWarcName);
|
2021-04-29 14:34:56 -07:00
|
|
|
fh.write(warcBuffer);
|
2021-03-31 13:41:27 -04:00
|
|
|
}
|
|
|
|
|
2021-04-29 14:34:56 -07:00
|
|
|
console.log(`Appending WARC ${fileSizeObjects[j].fileName}`);
|
|
|
|
|
|
|
|
const reader = fs.createReadStream(fileSizeObjects[j].fileName);
|
|
|
|
|
|
|
|
const p = new Promise((resolve) => {
|
|
|
|
reader.on("end", () => resolve());
|
|
|
|
});
|
|
|
|
|
|
|
|
reader.pipe(fh, {end: false});
|
|
|
|
|
|
|
|
await p;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fh) {
|
|
|
|
await fh.end();
|
2021-03-31 13:41:27 -04:00
|
|
|
}
|
|
|
|
|
2021-04-29 14:34:56 -07:00
|
|
|
console.log(`Combined WARCs saved as: ${generatedCombinedWarcs}`);
|
2021-03-31 13:41:27 -04:00
|
|
|
}
|
2020-11-01 19:22:53 -08:00
|
|
|
}
|
2020-10-31 13:16:37 -07:00
|
|
|
|
2020-11-01 19:22:53 -08:00
|
|
|
module.exports.Crawler = Crawler;
|