【问题标题】:Using Puppeteer, how to get the final redirect URI without having to actually click the link and load the page?使用 Puppeteer,如何在无需实际单击链接并加载页面的情况下获取最终的重定向 URI?
【发布时间】:2020-03-18 02:24:51
【问题描述】:

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

[编辑] 在发布此问题时发现此Puppeteer get request redirects,但它要求我首先“单击”链接并检查响应标头,并且我试图在没有单击的情况下获取值,我认为这应该是可能的......

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

我当然使用递归和 async/await 并且对这种编程范式相当陌生,但我似乎取得了不错的进展。这是我在“点击”期间返回的每个页面上调用的主要函数...

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;
};

我通过构建其他基于非 Puppeteer 的爬虫的经验了解到重定向链接是一个问题,因为您必须使用初始 href URI 值和最终 URI 来跟踪每个可点击链接,因为您不希望要重复任何抓取,您应该在将这些值提交到 qued_clickable_links Map() 之前知道这些值。

因此,使用此代码块将为我提供重定向的 URI但仅限于基于非 JavaScript 的 href 值,而不是页面上的每个可点击链接...

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 上的链接之一,与text=TERMS &amp; CONDITIONS 的链接的给定值是href=javascript:void(0) 将这些值作为href_value 放入上述函数中只会给我一个错误!这使得该功能对于我想要实现的功能相对毫无价值。

这样做的重点是通过点击链接来抓取网站,而不仅仅是扫描页面,所以我需要一个重定向功能来理解这一点并为我提供价值就像我点击了链接

【问题讨论】:

  • 我的两个想法是 1. 捕获 URL 并在 Puppeteer 的上下文之外对其进行 ping 操作,并检查那里的标题。您可以使用 axios 在 Node 环境中执行此操作。 2.复制初始href值,传回Node运行时,然后在新的pptr浏览器实例中打开url,记录去哪里。
  • hmmm 那么 axios 方法的资源密集程度如何?它真的会启动浏览器并呈现页面并抓取标题吗?至于第二个,这当然是蛮力的方式来做到这一点,但我真的在寻找(如果它存在的话)一种无需在初始 uri 上进行全面渲染即可获得该值的方法。也,就像你还在想我实际上有一个href!这就是重点……这些“链接”具有 void() href 值,并且仅由单击事件呈现:)
  • “这些“链接”具有 void() href 值,并且仅由单击事件呈现”-因此您需要单击它们,或者抓取该页面上的 JS……听起来很困难。

标签: javascript node.js redirect web-crawler puppeteer


【解决方案1】:

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

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

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

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

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

  • 是否是重定向网址
  • 如果它包含完整路径或只是相对路径
  • 是否在新窗口/标签页中打开

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

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

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

希望我能帮上忙。

【讨论】:

  • ,是的!非常感谢您的见解,我同意,链接不仅仅是一个 url...目前我正在存储完整的 elementHandle 对象,尽管我一直在与它作斗争,而且我还没有找到一个干净、易于理解的方法识别这些对象,这样我就可以在调试器中查看它们并弄清楚它们是什么......所以$(selector)[0].onclick.toString(); 会做到这一点吧?看起来太容易了哈哈,但无论如何我都会将你的答案标记为正确:)
猜你喜欢
  • 2012-01-19
  • 2018-04-28
  • 2019-01-20
  • 2011-01-29
  • 2014-01-17
  • 1970-01-01
  • 2019-06-02
  • 2019-04-10
  • 1970-01-01
相关资源
最近更新 更多