使用 Puppeteer,如何在不必实际单击 link 并加载页面的情况下获取最终的重定向 URI?

Using Puppeteer, how to get the final redirect URI without having to actually click the link and load the page?

TLDR;是否可以在使用 JS 函数和 location.href() 或 window.open() 的可点击 link 上获取重定向 URI,以在 JS and/or Puppeteer 中打开新页面无需实际单击 link?

[编辑] 在发布此问题时发现此 ,但它要求我首先 "click" link 并检查响应 header,我试图在不单击的情况下获取值,我认为应该是可能的...

我正在使用 Puppeteer 构建一个站点爬虫,我通过在页面上实际 "clicking"(模拟)每个 link 而不是仅仅扫描页面以查找 a.href标记并获取指示的 URI。

我当然在使用递归 async/await 并且对这种编程范例还很陌生,但我似乎取得了不错的进步。这是我在 "click"...

期间返回的每个页面上调用的主要函数
async function crawl(page) {

    const url = await page.url();

    // kick out already processed pages
    if (crawled_pages.has(url)) {
        CURRENT_DEPTH--;
        return false;
    }

    // scrape all rendered <a> links off page
    let page_alinks = await page.$$("a");

    // kick out of recursion if we dont find any links on the page...
    if (page_alinks.length === 0) return false;

    console.log("[ " + url + " ] links found: " + page_alinks.length);

    // var data = await page.$eval('a[href|="data:text"]', el => el.href);
    crawled_pages.set(url, page);

    // now add each of the links to a mapped collection using the concatted text and link 
    // values as the key and the link node as the value
    for (let click_node of page_alinks) {

        let href_value = await (await click_node.getProperty('href')).jsonValue();
        let text_value = await (await click_node.getProperty('text')).jsonValue();

        let redir_value = "";
        request({url: href_value, followRedirect: false}, function (err, res, body) {
            redir_value = res.headers.location;
        });

        if (debug) {
            console.log("text = " + text_value);
            console.log("href = " + href_value);
            console.log("redir= " + redir_value);
        }

        // track how deep into the recurse are we
        click_node.depth = CURRENT_DEPTH + 1;
        click_node.redir = redir_value;

        if (qued_clickable_links.store_link(text_value, href_value, click_node)) {
            if (debug) console.log("storg link [ " + text_value + "`" + href_value + " ]");
        } else {
            if (debug) console.log("rejecting link [ " + text_value + "`" + href_value + " ]");
        }
    }

    // loop the clickable links
    let clicknode = null, next_page = null;
    while ((clicknode = qued_clickable_links.pop()) === true) {
        (async () => {
            const newPagePromise = getNewPageWhenLoaded();
            await clicknode.click({delay: 1000});
            next_page = await newPagePromise;
        })();
        next_page.depth = CURRENT_DEPTH + 1;
        CURRENT_DEPTH++;
        let rv = await crawl(next_page);
    }

    CURRENT_DEPTH--;
    return true;
};

我通过构建其他基于 non-Puppeteer 的重定向 link 爬虫的经验了解到这是一个问题,因为您必须使用初始 href URI 跟踪每个可点击的 link值和最终 URI,并且由于您不想重复任何爬网,因此在将它们提交给 qued_clickable_links Map().

之前,您应该知道这些值

因此,使用此代码块会给我重定向的 URI ,但仅限于基于 non-Javascript 的 href 值,而不是页面上每个可点击的 link ...

const request = require('request');
 let redir_value = "";
        request({url: href_value, followRedirect: false}, function (err, res, body) {
            redir_value = res.headers.location;
        });

例如,对于底部 https://krksol-miraclebust.com 上的 link 之一,link 和 text=TERMS & CONDITIONS 的给定值是 href=javascript:void(0)将这些值作为 href_value 放入上述函数只会给我一个错误!这使得该功能对于我想要实现的目标来说相对毫无价值。

这一切的重点是通过单击 link 来抓取网站,而不仅仅是扫描页面,所以我需要一个重定向功能来理解这一点并给我值 as如果我点击 link

恐怕这是您需要以特殊方式处理以构建真正的站点爬虫的情况之一。我认为会有更多这样的案例,所以我认为您的实施应该更加复杂。一般来说,这是一个很大的话题,但我希望你能提供一些见解。

网站上可能有几种不同类型的 link 和重定向。其中一些是:

  • links 到另一个网站
  • links 到您网站的另一个页面
  • link秒下载一个文件
  • links 发送电子邮件
  • links 到位于同一页面上的锚点
  • links 到另一个页面上的锚点

可能我错过了一些,当然我没有提到通过执行 JS 函数进行的重定向,因为这是您遇到的困难 (href="javascript:void(0);")

因此,我建议将所有 link 保留为结构化 class 的对象,而不仅仅是 link 的集合。实际上我认为你这样做了,至少这个方法 qued_clickable_links.store_link() 是这样建议的。但是,查看该方法接受的参数,我认为这可能还不够。 class 应包含如下信息:

  • 是否重定向URL是否
  • 如果它包含完整路径或仅包含相对路径
  • 是否在新版本中打开window/tab

只有这样,您才能管理您在旅途中遇到的所有类型和问题。

但是回到在这种特殊情况下不点击它就获得 link 的问题——我很确定唯一合理的解决方案是解析 onclick 函数以提取第一个参数它的意思是要加载的资源的URL。我在解析字符串方面已经足够好了,所以我不会为您提供任何好的实现。你可以这样得到onclick函数的字符串:

var selector = 'body > div:nth-child(3) > div.footer__block.footer-menu > div:nth-child(3) > a';
$(selector)[0].onclick.toString();

希望对您有所帮助。